cd31646471239a0f72827fa2c8a2fff0c4473dbc
[vlc.git] / modules / gui / macosx / bookmarks.m
1 /*****************************************************************************
2  * bookmarks.m: MacOS X Bookmarks window
3  *****************************************************************************
4  * Copyright (C) 2005 - 2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Felix Paul K├╝hne <fkuehne at videolan dot org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24
25 /*****************************************************************************
26  * Note:
27  * the code used to bind with VLC's modules is heavily based upon
28  * ../wxwidgets/bookmarks.cpp, written by Gildas Bazin.
29  * (he is a member of the VideoLAN team)
30  *****************************************************************************/
31
32
33 /*****************************************************************************
34  * Preamble
35  *****************************************************************************/
36
37 #import "bookmarks.h"
38 #import "wizard.h"
39 #import "CompatibilityFixes.h"
40
41 @interface VLCBookmarks (Internal)
42 - (void)initStrings;
43 @end
44
45 @implementation VLCBookmarks
46
47 static VLCBookmarks *_o_sharedInstance = nil;
48
49 + (VLCBookmarks *)sharedInstance
50 {
51     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
52 }
53
54 - (id)init
55 {
56     if (_o_sharedInstance)
57         [self dealloc];
58     else
59         _o_sharedInstance = [super init];
60
61     return _o_sharedInstance;
62 }
63
64 /*****************************************************************************
65  * GUI methods
66  *****************************************************************************/
67
68 - (void)awakeFromNib
69 {
70     if (!OSX_SNOW_LEOPARD)
71         [o_bookmarks_window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
72
73     [self initStrings];
74
75     [[NSNotificationCenter defaultCenter] addObserver:self
76                                              selector:@selector(inputChangedEvent:)
77                                                  name:VLCInputChangedNotification
78                                                object:nil];
79 }
80
81 - (void)dealloc
82 {
83     if (p_old_input)
84         vlc_object_release(p_old_input);
85
86     [[NSNotificationCenter defaultCenter] removeObserver:self];
87
88     [super dealloc];
89 }
90
91 - (void)initStrings
92 {
93     /* localise the items */
94
95     /* main window */
96     [o_bookmarks_window setTitle: _NS("Bookmarks")];
97     [o_btn_add setTitle: _NS("Add")];
98     [o_btn_clear setTitle: _NS("Clear")];
99     [o_btn_edit setTitle: _NS("Edit")];
100     [o_btn_extract setTitle: _NS("Extract")];
101     [o_btn_rm setTitle: _NS("Remove")];
102     [[[o_tbl_dataTable tableColumnWithIdentifier:@"description"] headerCell]
103         setStringValue: _NS("Description")];
104     [[[o_tbl_dataTable tableColumnWithIdentifier:@"size_offset"] headerCell]
105         setStringValue: _NS("Position")];
106     [[[o_tbl_dataTable tableColumnWithIdentifier:@"time_offset"] headerCell]
107         setStringValue: _NS("Time")];
108
109     /* edit window */
110     [o_edit_btn_ok setTitle: _NS("OK")];
111     [o_edit_btn_cancel setTitle: _NS("Cancel")];
112     [o_edit_lbl_name setStringValue: _NS("Name")];
113     [o_edit_lbl_time setStringValue: _NS("Time")];
114     [o_edit_lbl_bytes setStringValue: _NS("Position")];
115 }
116
117 - (void)updateCocoaWindowLevel:(NSInteger)i_level
118 {
119     if (o_bookmarks_window && [o_bookmarks_window isVisible] && [o_bookmarks_window level] != i_level)
120         [o_bookmarks_window setLevel: i_level];
121 }
122
123 - (void)showBookmarks
124 {
125     /* show the window, called from intf.m */
126     [o_bookmarks_window displayIfNeeded];
127     [o_bookmarks_window setLevel: [[[VLCMain sharedInstance] voutController] currentStatusWindowLevel]];
128     [o_bookmarks_window makeKeyAndOrderFront:nil];
129 }
130
131 -(void)inputChangedEvent:(NSNotification *)o_notification
132 {
133     [o_tbl_dataTable reloadData];
134 }
135
136 - (IBAction)add:(id)sender
137 {
138     /* add item to list */
139     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
140
141     if (!p_input)
142         return;
143
144     seekpoint_t bookmark;
145
146     if (!input_Control(p_input, INPUT_GET_BOOKMARK, &bookmark)) {
147         bookmark.psz_name = _("Untitled");
148         input_Control(p_input, INPUT_ADD_BOOKMARK, &bookmark);
149     }
150
151     vlc_object_release(p_input);
152
153     [o_tbl_dataTable reloadData];
154 }
155
156 - (IBAction)clear:(id)sender
157 {
158     /* clear table */
159     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
160
161     if (!p_input)
162         return;
163
164     input_Control(p_input, INPUT_CLEAR_BOOKMARKS);
165
166     vlc_object_release(p_input);
167
168     [o_tbl_dataTable reloadData];
169 }
170
171 - (IBAction)edit:(id)sender
172 {
173     /* put values to the sheet's fields and show sheet */
174     /* we take the values from the core and not the table, because we cannot
175      * really trust it */
176     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
177     seekpoint_t **pp_bookmarks;
178     int i_bookmarks;
179     int row;
180     row = [o_tbl_dataTable selectedRow];
181
182     if (!p_input)
183         return;
184
185     if (row < 0) {
186         vlc_object_release(p_input);
187         return;
188     }
189
190     if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS) {
191         vlc_object_release(p_input);
192         return;
193     }
194
195     [o_edit_fld_name setStringValue: toNSStr(pp_bookmarks[row]->psz_name)];
196     int total = pp_bookmarks[row]->i_time_offset/ 1000000;
197     int hour = total / (60*60);
198     int min = (total - hour*60*60) / 60;
199     int sec = total - hour*60*60 - min*60;
200     [o_edit_fld_time setStringValue: [NSString stringWithFormat:@"%02d:%02d:%02d", hour, min, sec]];
201     [o_edit_fld_bytes setStringValue: [NSString stringWithFormat:@"%lli", pp_bookmarks[row]->i_byte_offset]];
202
203     /* Just keep the pointer value to check if it
204      * changes. Note, we don't need to keep a reference to the object.
205      * so release it now. */
206     p_old_input = p_input;
207     vlc_object_release(p_input);
208
209     [NSApp beginSheet: o_edit_window modalForWindow: o_bookmarks_window modalDelegate: o_edit_window didEndSelector: nil contextInfo: nil];
210
211     // Clear the bookmark list
212     for (int i = 0; i < i_bookmarks; i++)
213         vlc_seekpoint_Delete(pp_bookmarks[i]);
214     free(pp_bookmarks);
215 }
216
217 - (IBAction)edit_cancel:(id)sender
218 {
219     /* close sheet */
220     [NSApp endSheet:o_edit_window];
221     [o_edit_window close];
222 }
223
224 - (IBAction)edit_ok:(id)sender
225 {
226     /* save field contents and close sheet */
227      seekpoint_t **pp_bookmarks;
228     int i_bookmarks, i;
229     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
230
231     if (!p_input) {
232         NSBeginCriticalAlertSheet(_NS("No input"), _NS("OK"), @"", @"", o_bookmarks_window, nil, nil, nil, nil, @"%@",_NS("No input found. A stream must be playing or paused for bookmarks to work."));
233         return;
234     }
235     if (p_old_input != p_input) {
236         NSBeginCriticalAlertSheet(_NS("Input has changed"), _NS("OK"), @"", @"", o_bookmarks_window, nil, nil, nil, nil, @"%@",_NS("Input has changed, unable to save bookmark. Suspending playback with \"Pause\" while editing bookmarks to ensure to keep the same input."));
237         vlc_object_release(p_input);
238         return;
239     }
240
241     if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS) {
242         vlc_object_release(p_input);
243         return;
244     }
245
246     i = [o_tbl_dataTable selectedRow];
247
248     free(pp_bookmarks[i]->psz_name);
249
250     pp_bookmarks[i]->psz_name = strdup([[o_edit_fld_name stringValue] UTF8String]);
251     pp_bookmarks[i]->i_byte_offset = [[o_edit_fld_bytes stringValue] intValue];
252
253     NSArray * components = [[o_edit_fld_time stringValue] componentsSeparatedByString:@":"];
254     NSUInteger componentCount = [components count];
255     if (componentCount == 1)
256         pp_bookmarks[i]->i_time_offset = 1000000LL * ([[components objectAtIndex:0] longLongValue]);
257     else if (componentCount == 2)
258         pp_bookmarks[i]->i_time_offset = 1000000LL * ([[components objectAtIndex:0] longLongValue] * 60 + [[components objectAtIndex:1] longLongValue]);
259     else if (componentCount == 3)
260         pp_bookmarks[i]->i_time_offset = 1000000LL * ([[components objectAtIndex:0] longLongValue] * 3600 + [[components objectAtIndex:1] longLongValue] * 60 + [[components objectAtIndex:2] longLongValue]);
261     else {
262         msg_Err(VLCIntf, "Invalid string format for time");
263         goto clear;
264     }
265
266     if (input_Control(p_input, INPUT_CHANGE_BOOKMARK, pp_bookmarks[i], i) != VLC_SUCCESS) {
267         msg_Warn(VLCIntf, "Unable to change the bookmark");
268         goto clear;
269     }
270
271     [o_tbl_dataTable reloadData];
272     vlc_object_release(p_input);
273
274     [NSApp endSheet: o_edit_window];
275     [o_edit_window close];
276
277 clear:
278     // Clear the bookmark list
279     for (int i = 0; i < i_bookmarks; i++)
280         vlc_seekpoint_Delete(pp_bookmarks[i]);
281     free(pp_bookmarks);
282 }
283
284 - (IBAction)extract:(id)sender
285 {
286     if ([o_tbl_dataTable numberOfSelectedRows] < 2) {
287         NSBeginAlertSheet(_NS("Invalid selection"), _NS("OK"), @"", @"", o_bookmarks_window, nil, nil, nil, nil, @"%@",_NS("Two bookmarks have to be selected."));
288         return;
289     }
290     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
291     if (!p_input) {
292         NSBeginCriticalAlertSheet(_NS("No input found"), _NS("OK"), @"", @"", o_bookmarks_window, nil, nil, nil, nil, @"%@",_NS("The stream must be playing or paused for bookmarks to work."));
293         return;
294     }
295
296     seekpoint_t **pp_bookmarks;
297     int i_bookmarks ;
298     int i_first = -1;
299     int i_second = -1;
300     int c = 0;
301     for (NSUInteger x = 0; c != 2; x++) {
302         if ([o_tbl_dataTable isRowSelected:x]) {
303             if (i_first == -1) {
304                 i_first = x;
305                 c = 1;
306             } else if (i_second == -1) {
307                 i_second = x;
308                 c = 2;
309             }
310         }
311     }
312
313     if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS) {
314         vlc_object_release(p_input);
315         msg_Err(VLCIntf, "already defined bookmarks couldn't be retrieved");
316         return;
317     }
318
319     char *psz_uri = input_item_GetURI(input_GetItem(p_input));
320     [[[VLCMain sharedInstance] wizard] initWithExtractValuesFrom: [NSString stringWithFormat:@"%lli", pp_bookmarks[i_first]->i_time_offset/1000000] to: [NSString stringWithFormat:@"%lli", pp_bookmarks[i_second]->i_time_offset/1000000] ofItem: toNSStr(psz_uri)];
321     free(psz_uri);
322     vlc_object_release(p_input);
323
324     // Clear the bookmark list
325     for (int i = 0; i < i_bookmarks; i++)
326         vlc_seekpoint_Delete(pp_bookmarks[i]);
327     free(pp_bookmarks);
328 }
329
330 - (IBAction)goToBookmark:(id)sender
331 {
332     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
333
334     if (!p_input)
335         return;
336
337     input_Control(p_input, INPUT_SET_BOOKMARK, [o_tbl_dataTable selectedRow]);
338
339     vlc_object_release(p_input);
340 }
341
342 - (IBAction)remove:(id)sender
343 {
344     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
345
346     if (!p_input)
347         return;
348
349     int i_focused = [o_tbl_dataTable selectedRow];
350
351     if (i_focused >= 0)
352         input_Control(p_input, INPUT_DEL_BOOKMARK, i_focused);
353
354     vlc_object_release(p_input);
355
356     [o_tbl_dataTable reloadData];
357 }
358
359 /*****************************************************************************
360  * data source methods
361  *****************************************************************************/
362
363 - (NSInteger)numberOfRowsInTableView:(NSTableView *)theDataTable
364 {
365     /* return the number of bookmarks */
366     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
367     seekpoint_t **pp_bookmarks;
368     int i_bookmarks;
369
370     if (!p_input)
371         return 0;
372
373     int returnValue = input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks);
374     vlc_object_release(p_input);
375
376     if (returnValue != VLC_SUCCESS)
377         return 0;
378
379     for (int i = 0; i < i_bookmarks; i++)
380         vlc_seekpoint_Delete(pp_bookmarks[i]);
381     free(pp_bookmarks);
382
383     return i_bookmarks;
384 }
385
386 - (id)tableView:(NSTableView *)theDataTable objectValueForTableColumn: (NSTableColumn *)theTableColumn row: (NSInteger)row
387 {
388     /* return the corresponding data as NSString */
389     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
390     seekpoint_t **pp_bookmarks;
391     int i_bookmarks;
392     id ret = @"";
393
394     if (!p_input)
395         return @"";
396     else if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS)
397         ret = @"";
398     else if (row >= i_bookmarks)
399         ret = @"";
400     else {
401         NSString * identifier = [theTableColumn identifier];
402         if ([identifier isEqualToString: @"description"])
403             ret = toNSStr(pp_bookmarks[row]->psz_name);
404         else if ([identifier isEqualToString: @"size_offset"])
405             ret = [NSString stringWithFormat:@"%lli", pp_bookmarks[row]->i_byte_offset];
406         else if ([identifier isEqualToString: @"time_offset"]) {
407             int total = pp_bookmarks[row]->i_time_offset/ 1000000;
408             int hour = total / (60*60);
409             int min = (total - hour*60*60) / 60;
410             int sec = total - hour*60*60 - min*60;
411             ret = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, min, sec];
412         }
413
414         // Clear the bookmark list
415         for (int i = 0; i < i_bookmarks; i++)
416             vlc_seekpoint_Delete(pp_bookmarks[i]);
417         free(pp_bookmarks);
418     }
419     vlc_object_release(p_input);
420     return ret;
421 }
422
423 /*****************************************************************************
424  * delegate methods
425  *****************************************************************************/
426
427 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification
428 {
429     /* check whether a row is selected and en-/disable the edit/remove buttons */
430     if ([o_tbl_dataTable selectedRow] == -1) {
431         /* no row is selected */
432         [o_btn_edit setEnabled: NO];
433         [o_btn_rm setEnabled: NO];
434         [o_btn_extract setEnabled: NO];
435     } else {
436         /* a row is selected */
437         [o_btn_edit setEnabled: YES];
438         [o_btn_rm setEnabled: YES];
439         if ([o_tbl_dataTable numberOfSelectedRows] == 2)
440             [o_btn_extract setEnabled: YES];
441     }
442 }
443
444 @end