]> git.sesse.net Git - vlc/blob - modules/gui/macosx/bookmarks.m
Qt: use DIR_SEP for BDMV and VIDEO_TS detection
[vlc] / 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 <vlc_interface.h>
40 #import "CompatibilityFixes.h"
41
42 @interface VLCBookmarks (Internal)
43 - (void)initStrings;
44 @end
45
46 @implementation VLCBookmarks
47
48 static VLCBookmarks *_o_sharedInstance = nil;
49
50 + (VLCBookmarks *)sharedInstance
51 {
52     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
53 }
54
55 - (id)init
56 {
57     if (_o_sharedInstance)
58         [self dealloc];
59     else
60         _o_sharedInstance = [super init];
61
62     return _o_sharedInstance;
63 }
64
65 /*****************************************************************************
66  * GUI methods
67  *****************************************************************************/
68
69 - (void)awakeFromNib
70 {
71     if (!OSX_SNOW_LEOPARD)
72         [o_bookmarks_window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
73
74     [self initStrings];
75 }
76
77 - (void)dealloc
78 {
79     if (p_old_input)
80         vlc_object_release(p_old_input);
81
82     [super dealloc];
83 }
84
85 - (void)initStrings
86 {
87     /* localise the items */
88
89     /* main window */
90     [o_bookmarks_window setTitle: _NS("Bookmarks")];
91     [o_btn_add setTitle: _NS("Add")];
92     [o_btn_clear setTitle: _NS("Clear")];
93     [o_btn_edit setTitle: _NS("Edit")];
94     [o_btn_extract setTitle: _NS("Extract")];
95     [o_btn_rm setTitle: _NS("Remove")];
96     [[[o_tbl_dataTable tableColumnWithIdentifier:@"description"] headerCell]
97         setStringValue: _NS("Description")];
98     [[[o_tbl_dataTable tableColumnWithIdentifier:@"size_offset"] headerCell]
99         setStringValue: _NS("Position")];
100     [[[o_tbl_dataTable tableColumnWithIdentifier:@"time_offset"] headerCell]
101         setStringValue: _NS("Time")];
102
103     /* edit window */
104     [o_edit_btn_ok setTitle: _NS("OK")];
105     [o_edit_btn_cancel setTitle: _NS("Cancel")];
106     [o_edit_lbl_name setStringValue: _NS("Name")];
107     [o_edit_lbl_time setStringValue: _NS("Time")];
108     [o_edit_lbl_bytes setStringValue: _NS("Position")];
109 }
110
111 - (void)updateCocoaWindowLevel:(NSInteger)i_level
112 {
113     if (o_bookmarks_window && [o_bookmarks_window isVisible] && [o_bookmarks_window level] != i_level)
114         [o_bookmarks_window setLevel: i_level];
115 }
116
117 - (void)showBookmarks
118 {
119     /* show the window, called from intf.m */
120     [o_bookmarks_window displayIfNeeded];
121     [o_bookmarks_window setLevel: [[[VLCMain sharedInstance] voutController] currentWindowLevel]];
122     [o_bookmarks_window makeKeyAndOrderFront:nil];
123 }
124
125 - (IBAction)add:(id)sender
126 {
127     /* add item to list */
128     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
129
130     if (!p_input)
131         return;
132
133     seekpoint_t bookmark;
134
135     if (!input_Control(p_input, INPUT_GET_BOOKMARK, &bookmark)) {
136         bookmark.psz_name = _("Untitled");
137         input_Control(p_input, INPUT_ADD_BOOKMARK, &bookmark);
138     }
139
140     vlc_object_release(p_input);
141
142     [o_tbl_dataTable reloadData];
143 }
144
145 - (IBAction)clear:(id)sender
146 {
147     /* clear table */
148     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
149
150     if (!p_input)
151         return;
152
153     input_Control(p_input, INPUT_CLEAR_BOOKMARKS);
154
155     vlc_object_release(p_input);
156
157     [o_tbl_dataTable reloadData];
158 }
159
160 - (IBAction)edit:(id)sender
161 {
162     /* put values to the sheet's fields and show sheet */
163     /* we take the values from the core and not the table, because we cannot
164      * really trust it */
165     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
166     seekpoint_t **pp_bookmarks;
167     int i_bookmarks;
168     int row;
169     row = [o_tbl_dataTable selectedRow];
170
171     if (!p_input)
172         return;
173
174     if (row < 0) {
175         vlc_object_release(p_input);
176         return;
177     }
178
179     if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS) {
180         vlc_object_release(p_input);
181         return;
182     }
183
184     [o_edit_fld_name setStringValue: toNSStr(pp_bookmarks[row]->psz_name)];
185     int total = pp_bookmarks[row]->i_time_offset/ 1000000;
186     int hour = total / (60*60);
187     int min = (total - hour*60*60) / 60;
188     int sec = total - hour*60*60 - min*60;
189     [o_edit_fld_time setStringValue: [NSString stringWithFormat:@"%02d:%02d:%02d", hour, min, sec]];
190     [o_edit_fld_bytes setStringValue: [NSString stringWithFormat:@"%lli", pp_bookmarks[row]->i_byte_offset]];
191
192     /* Just keep the pointer value to check if it
193      * changes. Note, we don't need to keep a reference to the object.
194      * so release it now. */
195     p_old_input = p_input;
196     vlc_object_release(p_input);
197
198     [NSApp beginSheet: o_edit_window modalForWindow: o_bookmarks_window modalDelegate: o_edit_window didEndSelector: nil contextInfo: nil];
199
200     // Clear the bookmark list
201     for (int i = 0; i < i_bookmarks; i++)
202         vlc_seekpoint_Delete(pp_bookmarks[i]);
203     free(pp_bookmarks);
204 }
205
206 - (IBAction)edit_cancel:(id)sender
207 {
208     /* close sheet */
209     [NSApp endSheet:o_edit_window];
210     [o_edit_window close];
211 }
212
213 - (IBAction)edit_ok:(id)sender
214 {
215     /* save field contents and close sheet */
216      seekpoint_t **pp_bookmarks;
217     int i_bookmarks, i;
218     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
219
220     if (!p_input) {
221         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."));
222         return;
223     }
224     if (p_old_input != p_input) {
225         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."));
226         vlc_object_release(p_input);
227         return;
228     }
229
230     if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS) {
231         vlc_object_release(p_input);
232         return;
233     }
234
235     i = [o_tbl_dataTable selectedRow];
236
237     free(pp_bookmarks[i]->psz_name);
238
239     pp_bookmarks[i]->psz_name = strdup([[o_edit_fld_name stringValue] UTF8String]);
240     pp_bookmarks[i]->i_byte_offset = [[o_edit_fld_bytes stringValue] intValue];
241
242     NSArray * components = [[o_edit_fld_time stringValue] componentsSeparatedByString:@":"];
243     NSUInteger componentCount = [components count];
244     if (componentCount == 1)
245         pp_bookmarks[i]->i_time_offset = 1000000 * ([[components objectAtIndex:0] intValue]);
246     else if (componentCount == 2)
247         pp_bookmarks[i]->i_time_offset = 1000000 * ([[components objectAtIndex:0] intValue] * 60 + [[components objectAtIndex:1] intValue]);
248     else if (componentCount == 3)
249         pp_bookmarks[i]->i_time_offset = 1000000 * ([[components objectAtIndex:0] intValue] * 3600 + [[components objectAtIndex:1] intValue] * 60 + [[components objectAtIndex:2] intValue]);
250     else {
251         msg_Err(VLCIntf, "Invalid string format for time");
252         goto clear;
253     }
254
255     if (input_Control(p_input, INPUT_CHANGE_BOOKMARK, pp_bookmarks[i], i) != VLC_SUCCESS) {
256         msg_Warn(VLCIntf, "Unable to change the bookmark");
257         goto clear;
258     }
259
260     [o_tbl_dataTable reloadData];
261     vlc_object_release(p_input);
262
263     [NSApp endSheet: o_edit_window];
264     [o_edit_window close];
265
266 clear:
267     // Clear the bookmark list
268     for (int i = 0; i < i_bookmarks; i++)
269         vlc_seekpoint_Delete(pp_bookmarks[i]);
270     free(pp_bookmarks);
271 }
272
273 - (IBAction)extract:(id)sender
274 {
275     if ([o_tbl_dataTable numberOfSelectedRows] < 2) {
276         NSBeginAlertSheet(_NS("Invalid selection"), _NS("OK"), @"", @"", o_bookmarks_window, nil, nil, nil, nil, @"%@",_NS("Two bookmarks have to be selected."));
277         return;
278     }
279     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
280     if (!p_input) {
281         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."));
282         return;
283     }
284
285     seekpoint_t **pp_bookmarks;
286     int i_bookmarks ;
287     int i_first = -1;
288     int i_second = -1;
289     int c = 0;
290     for (NSUInteger x = 0; c != 2; x++) {
291         if ([o_tbl_dataTable isRowSelected:x]) {
292             if (i_first == -1) {
293                 i_first = x;
294                 c = 1;
295             } else if (i_second == -1) {
296                 i_second = x;
297                 c = 2;
298             }
299         }
300     }
301
302     if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS) {
303         vlc_object_release(p_input);
304         msg_Err(VLCIntf, "already defined bookmarks couldn't be retrieved");
305         return;
306     }
307
308     char *psz_uri = input_item_GetURI(input_GetItem(p_input));
309     [[[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)];
310     free(psz_uri);
311     vlc_object_release(p_input);
312
313     // Clear the bookmark list
314     for (int i = 0; i < i_bookmarks; i++)
315         vlc_seekpoint_Delete(pp_bookmarks[i]);
316     free(pp_bookmarks);
317 }
318
319 - (IBAction)goToBookmark:(id)sender
320 {
321     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
322
323     if (!p_input)
324         return;
325
326     input_Control(p_input, INPUT_SET_BOOKMARK, [o_tbl_dataTable selectedRow]);
327
328     vlc_object_release(p_input);
329 }
330
331 - (IBAction)remove:(id)sender
332 {
333     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
334
335     if (!p_input)
336         return;
337
338     int i_focused = [o_tbl_dataTable selectedRow];
339
340     if (i_focused >= 0)
341         input_Control(p_input, INPUT_DEL_BOOKMARK, i_focused);
342
343     vlc_object_release(p_input);
344
345     [o_tbl_dataTable reloadData];
346 }
347
348 /*****************************************************************************
349  * callback stuff
350  *****************************************************************************/
351
352 -(id)dataTable
353 {
354     return o_tbl_dataTable;
355 }
356
357 /*****************************************************************************
358  * data source methods
359  *****************************************************************************/
360
361 - (NSInteger)numberOfRowsInTableView:(NSTableView *)theDataTable
362 {
363     /* return the number of bookmarks */
364     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
365     seekpoint_t **pp_bookmarks;
366     int i_bookmarks;
367
368     if (!p_input)
369         return 0;
370
371     int returnValue = input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks);
372     vlc_object_release(p_input);
373
374     if (returnValue != VLC_SUCCESS)
375         return 0;
376
377     for (int i = 0; i < i_bookmarks; i++)
378         vlc_seekpoint_Delete(pp_bookmarks[i]);
379     free(pp_bookmarks);
380
381     return i_bookmarks;
382 }
383
384 - (id)tableView:(NSTableView *)theDataTable objectValueForTableColumn: (NSTableColumn *)theTableColumn row: (NSInteger)row
385 {
386     /* return the corresponding data as NSString */
387     input_thread_t * p_input = pl_CurrentInput(VLCIntf);
388     seekpoint_t **pp_bookmarks;
389     int i_bookmarks;
390     id ret;
391
392     if (!p_input)
393         return @"";
394     else if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS)
395         ret = @"";
396     else {
397         NSString * identifier = [theTableColumn identifier];
398         if ([identifier isEqualToString: @"description"])
399             ret = toNSStr(pp_bookmarks[row]->psz_name);
400         else if ([identifier isEqualToString: @"size_offset"])
401             ret = [NSString stringWithFormat:@"%lli", pp_bookmarks[row]->i_byte_offset];
402         else if ([identifier isEqualToString: @"time_offset"]) {
403             int total = pp_bookmarks[row]->i_time_offset/ 1000000;
404             int hour = total / (60*60);
405             int min = (total - hour*60*60) / 60;
406             int sec = total - hour*60*60 - min*60;
407             ret = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, min, sec];
408         }
409
410         // Clear the bookmark list
411         for (int i = 0; i < i_bookmarks; i++)
412             vlc_seekpoint_Delete(pp_bookmarks[i]);
413         free(pp_bookmarks);
414     }
415     vlc_object_release(p_input);
416     return ret;
417 }
418
419 /*****************************************************************************
420  * delegate methods
421  *****************************************************************************/
422
423 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification
424 {
425     /* check whether a row is selected and en-/disable the edit/remove buttons */
426     if ([o_tbl_dataTable selectedRow] == -1) {
427         /* no row is selected */
428         [o_btn_edit setEnabled: NO];
429         [o_btn_rm setEnabled: NO];
430         [o_btn_extract setEnabled: NO];
431     } else {
432         /* a row is selected */
433         [o_btn_edit setEnabled: YES];
434         [o_btn_rm setEnabled: YES];
435         if ([o_tbl_dataTable numberOfSelectedRows] == 2)
436             [o_btn_extract setEnabled: YES];
437     }
438 }
439
440 @end