d5b48c632d5debc21794e00cbdd12be481e37de5
[vlc.git] / modules / gui / macosx / VLCDebugMessageWindowController.m
1 /*****************************************************************************
2  * VLCDebugMessageWindowController.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 "VLCDebugMessageWindowController.h"
27 #import "VLCMain.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 VLCDebugMessageWindowController () <NSWindowDelegate>
39 {
40     NSMutableArray * _messageArray;
41 }
42 - (void)appendMessage:(NSMutableAttributedString *) message;
43
44 @end
45
46 static void MsgCallback(void *data, int type, const vlc_log_t *item, const char *format, va_list ap)
47 {
48     @autoreleasepool {
49
50         VLCDebugMessageWindowController *visualizer = (__bridge VLCDebugMessageWindowController*)data;
51
52         int canc = vlc_savecancel();
53         char *str;
54         if (vasprintf(&str, format, ap) == -1) {
55             vlc_restorecancel(canc);
56             return;
57         }
58
59         if (!item->psz_module)
60             return;
61         if (!str)
62             return;
63
64         NSColor *_white = [NSColor whiteColor];
65         NSColor *_red = [NSColor redColor];
66         NSColor *_yellow = [NSColor yellowColor];
67         NSColor *_gray = [NSColor grayColor];
68
69         NSColor * pp_color[4] = { _white, _red, _yellow, _gray };
70         static const char * ppsz_type[4] = { ": ", " error: ", " warning: ", " debug: " };
71
72
73         NSString *firstString = [NSString stringWithFormat:@"%s%s", item->psz_module, ppsz_type[type]];
74         NSString *secondString = [NSString stringWithFormat:@"%@%s\n", firstString, str];
75
76         NSDictionary *colorAttrib = [NSDictionary dictionaryWithObject: pp_color[type]  forKey: NSForegroundColorAttributeName];
77         NSMutableAttributedString *coloredMsg = [[NSMutableAttributedString alloc] initWithString: secondString attributes: colorAttrib];
78         colorAttrib = [NSDictionary dictionaryWithObject: pp_color[3] forKey: NSForegroundColorAttributeName];
79         [coloredMsg setAttributes: colorAttrib range: NSMakeRange(0, [firstString length])];
80
81         [visualizer performSelectorOnMainThread:@selector(appendMessage:) withObject:coloredMsg waitUntilDone:NO];
82
83         vlc_restorecancel(canc);
84         free(str);
85     }
86 }
87
88 @implementation VLCDebugMessageWindowController
89
90 - (id)init
91 {
92     self = [super initWithWindowNibName:@"DebugMessageVisualizer"];
93     if (self) {
94         _messageArray = [NSMutableArray arrayWithCapacity:600];
95     }
96     return self;
97 }
98
99 - (void)dealloc
100 {
101     vlc_LogSet( getIntf()->obj.libvlc, NULL, NULL );
102 }
103
104 - (void)windowDidLoad
105 {
106     [self.window setExcludedFromWindowsMenu: YES];
107     [self.window setDelegate: self];
108     [self.window setTitle: _NS("Messages")];
109     [_saveButton setTitle: _NS("Save this Log...")];
110     [_clearButton setTitle:_NS("Clear")];
111     [_refreshButton setImage: [NSImage imageNamed: NSImageNameRefreshTemplate]];
112 }
113
114 #pragma mark - UI interaction
115
116 - (void)showWindow:(id)sender
117 {
118     /* subscribe to LibVLCCore's messages */
119     vlc_LogSet(getIntf()->obj.libvlc, MsgCallback, (__bridge void*)self);
120
121     [super showWindow:sender];
122 }
123
124 - (IBAction)updateMessagesPanel:(id)sender
125 {
126     [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidBecomeKeyNotification object:self.window];
127 }
128
129 - (void)windowDidBecomeKey:(NSNotification *)notification
130 {
131     [_messageTable reloadData];
132     [_messageTable scrollRowToVisible: [_messageArray count] - 1];
133 }
134
135 - (void)windowWillClose:(NSNotification *)notification
136 {
137     /* unsubscribe from LibVLCCore's messages */
138     vlc_LogSet( getIntf()->obj.libvlc, NULL, NULL );
139     [_messageArray removeAllObjects];
140 }
141
142 - (IBAction)saveDebugLog:(id)sender
143 {
144     NSSavePanel * saveFolderPanel = [[NSSavePanel alloc] init];
145
146     [saveFolderPanel setCanSelectHiddenExtension: NO];
147     [saveFolderPanel setCanCreateDirectories: YES];
148     [saveFolderPanel setAllowedFileTypes: [NSArray arrayWithObject:@"txt"]];
149     [saveFolderPanel setNameFieldStringValue:[NSString stringWithFormat: _NS("VLC Debug Log (%s).txt"), VERSION_MESSAGE]];
150     [saveFolderPanel beginSheetModalForWindow: self.window completionHandler:^(NSInteger returnCode) {
151         if (returnCode == NSOKButton) {
152             NSUInteger count = [_messageArray count];
153             NSMutableString *string = [[NSMutableString alloc] init];
154             for (NSUInteger i = 0; i < count; i++)
155                 [string appendString: [[_messageArray objectAtIndex:i] string]];
156
157             NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
158             if ([data writeToFile: [[saveFolderPanel URL] path] atomically: YES] == NO)
159                 msg_Warn(getIntf(), "Error while saving the debug log");
160         }
161     }];
162 }
163
164 - (IBAction)clearLog:(id)sender
165 {
166     [_messageArray removeAllObjects];
167
168     // Reregister handler, to write new header to log
169     vlc_LogSet(getIntf()->obj.libvlc, NULL, NULL);
170     vlc_LogSet(getIntf()->obj.libvlc, MsgCallback, (__bridge void*)self);
171
172     [_messageTable reloadData];
173 }
174
175 #pragma mark - data handling
176
177 - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
178 {
179     return [_messageArray count];
180 }
181
182 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
183 {
184     return [_messageArray objectAtIndex:rowIndex];
185 }
186
187 - (void)appendMessage:(NSMutableAttributedString *) message
188 {
189     if ([_messageArray count] > 1000000) {
190         [_messageArray removeObjectAtIndex: 0];
191         [_messageArray removeObjectAtIndex: 1];
192     }
193
194     [_messageArray addObject:message];
195 }
196
197 @end