b784045f1263b60d3c1b3c55764190db2e631880
[vlc.git] / modules / gui / macosx / DebugMessageVisualizer.m
1 /*****************************************************************************
2  * DebugMessageVisualizer.m: Mac OS X interface crash reporter
3  *****************************************************************************
4  * Copyright (C) 2004-2013 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Felix Paul K├╝hne <fkuehne at videolan dot org>
8  *          Pierre d'Herbemont <pdherbemont # videolan org>
9  *          Derk-Jan Hartman <hartman at videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #import "DebugMessageVisualizer.h"
27 #import "intf.h"
28 #import <vlc_common.h>
29
30 static void MsgCallback(void *data, int type, const vlc_log_t *item, const char *format, va_list ap);
31
32 /*****************************************************************************
33  * MsgCallback: Callback triggered by the core once a new debug message is
34  * ready to be displayed. We store everything in a NSArray in our Cocoa part
35  * of this file.
36  *****************************************************************************/
37
38 @interface VLCDebugMessageVisualizer () <NSWindowDelegate>
39 {
40     NSMutableArray * _msg_arr;
41     NSLock * _msg_lock;
42 }
43 - (void)processReceivedlibvlcMessage:(const vlc_log_t *) item ofType: (int)i_type withStr: (char *)str;
44
45 @end
46
47 static void MsgCallback(void *data, int type, const vlc_log_t *item, const char *format, va_list ap)
48 {
49     @autoreleasepool {
50
51         VLCDebugMessageVisualizer *visualizer = (__bridge VLCDebugMessageVisualizer*)data;
52
53         int canc = vlc_savecancel();
54         char *str;
55
56         if (vasprintf(&str, format, ap) == -1) {
57             vlc_restorecancel(canc);
58             return;
59         }
60
61         [visualizer processReceivedlibvlcMessage: item ofType: type withStr: str];
62
63         vlc_restorecancel(canc);
64         free(str);
65     }
66 }
67
68 @implementation VLCDebugMessageVisualizer
69
70 + (VLCDebugMessageVisualizer *)sharedInstance
71 {
72     static VLCDebugMessageVisualizer *sharedInstance = nil;
73     static dispatch_once_t pred;
74
75     dispatch_once(&pred, ^{
76         sharedInstance = [VLCDebugMessageVisualizer new];
77     });
78
79     return sharedInstance;
80 }
81
82 - (id)init
83 {
84     self = [super initWithWindowNibName:@"DebugMessageVisualizer"];
85     if (self) {
86         _msg_lock = [[NSLock alloc] init];
87         _msg_arr = [NSMutableArray arrayWithCapacity:600];
88     }
89     return self;
90 }
91
92 - (void)windowDidLoad
93 {
94     [self.window setExcludedFromWindowsMenu: YES];
95     [self.window setDelegate: self];
96     [self.window setTitle: _NS("Messages")];
97     [_msgs_save_btn setTitle: _NS("Save this Log...")];
98     [_msgs_refresh_btn setImage: [NSImage imageNamed: NSImageNameRefreshTemplate]];
99 }
100
101 #pragma mark - UI interaction
102
103 - (void)showWindow:(id)sender
104 {
105     /* subscribe to LibVLCCore's messages */
106     vlc_LogSet(VLCIntf->p_libvlc, MsgCallback, (__bridge void*)self);
107
108     [super showWindow:sender];
109 }
110
111 - (IBAction)updateMessagesPanel:(id)sender
112 {
113     [self windowDidBecomeKey:nil];
114 }
115
116 - (void)windowDidBecomeKey:(NSNotification *)notification
117 {
118     [_msgs_table reloadData];
119     [_msgs_table scrollRowToVisible: [_msg_arr count] - 1];
120 }
121
122 - (void)windowWillClose:(NSNotification *)notification
123 {
124     /* unsubscribe from LibVLCCore's messages */
125     vlc_LogSet( VLCIntf->p_libvlc, NULL, NULL );
126 }
127
128 - (IBAction)saveDebugLog:(id)sender
129 {
130     NSSavePanel * saveFolderPanel = [[NSSavePanel alloc] init];
131
132     [saveFolderPanel setCanSelectHiddenExtension: NO];
133     [saveFolderPanel setCanCreateDirectories: YES];
134     [saveFolderPanel setAllowedFileTypes: [NSArray arrayWithObject:@"rtf"]];
135     [saveFolderPanel setNameFieldStringValue:[NSString stringWithFormat: _NS("VLC Debug Log (%s).rtf"), VERSION_MESSAGE]];
136     [saveFolderPanel beginSheetModalForWindow: self.window completionHandler:^(NSInteger returnCode) {
137         if (returnCode == NSOKButton) {
138             NSUInteger count = [_msg_arr count];
139             NSMutableAttributedString * string = [[NSMutableAttributedString alloc] init];
140             for (NSUInteger i = 0; i < count; i++)
141                 [string appendAttributedString: [_msg_arr objectAtIndex:i]];
142
143             NSData *data = [string RTFFromRange:NSMakeRange(0, [string length])
144                              documentAttributes:[NSDictionary dictionaryWithObject: NSRTFTextDocumentType forKey: NSDocumentTypeDocumentAttribute]];
145
146             if ([data writeToFile: [[saveFolderPanel URL] path] atomically: YES] == NO)
147                 msg_Warn(VLCIntf, "Error while saving the debug log");
148         }
149     }];
150 }
151
152 #pragma mark - data handling
153
154 - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
155 {
156     if (aTableView == _msgs_table)
157         return [_msg_arr count];
158     return 0;
159 }
160
161 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
162 {
163     NSMutableAttributedString *result = NULL;
164
165     [_msg_lock lock];
166     if (rowIndex < [_msg_arr count])
167         result = [_msg_arr objectAtIndex:rowIndex];
168     [_msg_lock unlock];
169
170     if (result != NULL)
171         return result;
172     else
173         return @"";
174 }
175
176 - (void)processReceivedlibvlcMessage:(const vlc_log_t *) item ofType: (int)i_type withStr: (char *)str
177 {
178     if (_msg_arr) {
179         NSColor *_white = [NSColor whiteColor];
180         NSColor *_red = [NSColor redColor];
181         NSColor *_yellow = [NSColor yellowColor];
182         NSColor *_gray = [NSColor grayColor];
183         NSString * firstString, * secondString;
184
185         NSColor * pp_color[4] = { _white, _red, _yellow, _gray };
186         static const char * ppsz_type[4] = { ": ", " error: ", " warning: ", " debug: " };
187
188         NSDictionary *_attr;
189         NSMutableAttributedString *_msg_color;
190
191         [_msg_lock lock];
192
193         if ([_msg_arr count] > 10000) {
194             [_msg_arr removeObjectAtIndex: 0];
195             [_msg_arr removeObjectAtIndex: 1];
196         }
197         if (!item->psz_module)
198             return;
199         if (!str)
200             return;
201
202         firstString = [NSString stringWithFormat:@"%s%s", item->psz_module, ppsz_type[i_type]];
203         secondString = [NSString stringWithFormat:@"%@%s\n", firstString, str];
204
205         _attr = [NSDictionary dictionaryWithObject: pp_color[i_type]  forKey: NSForegroundColorAttributeName];
206         _msg_color = [[NSMutableAttributedString alloc] initWithString: secondString attributes: _attr];
207         _attr = [NSDictionary dictionaryWithObject: pp_color[3] forKey: NSForegroundColorAttributeName];
208         [_msg_color setAttributes: _attr range: NSMakeRange(0, [firstString length])];
209         [_msg_arr addObject:_msg_color];
210
211         [_msg_lock unlock];
212     }
213 }
214
215 @end