* First string review of the OSX interface
[vlc.git] / modules / gui / macosx / bookmarks.m
1 /*****************************************************************************
2  * bookmarks.m: MacOS X Bookmarks window
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Felix K├╝hne <fkuehne@users.sf.net>
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 "intf.h"
39 #import "wizard.h"
40 #import <vlc/intf.h>
41
42 /*****************************************************************************
43  * VLCExtended implementation
44  *
45  * implements the GUI functions for the window, the data source and the
46  * delegate for o_tbl_dataTable
47  *****************************************************************************/
48
49 @implementation VLCBookmarks
50
51 static VLCBookmarks *_o_sharedInstance = nil;
52
53 + (VLCBookmarks *)sharedInstance
54 {
55     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
56 }
57
58 - (id)init
59 {
60     if (_o_sharedInstance) {
61         [self dealloc];
62     } else {
63         _o_sharedInstance = [super init];
64     }
65
66     return _o_sharedInstance;
67 }
68
69 /*****************************************************************************
70  * GUI methods
71  *****************************************************************************/
72
73 - (void)awakeFromNib
74 {
75     [self initStrings];
76 }
77
78 - (void)dealloc
79 {
80     if(p_old_input)
81     {
82         free(p_old_input);
83     }
84     [super dealloc];
85 }
86
87 - (void)initStrings
88 {
89     /* localise the items */
90     
91     /* main window */
92     [o_bookmarks_window setTitle: _NS("Bookmarks")];
93     [o_btn_add setTitle: _NS("Add")];
94     [o_btn_clear setTitle: _NS("Clear")];
95     [o_btn_edit setTitle: _NS("Edit")];
96     [o_btn_extract setTitle: _NS("Extract")];
97     [o_btn_rm setTitle: _NS("Remove")];
98     [[[o_tbl_dataTable tableColumnWithIdentifier:@"description"] headerCell] \
99         setStringValue: _NS("Description")];
100     [[[o_tbl_dataTable tableColumnWithIdentifier:@"size_offset"] headerCell] \
101         setStringValue: _NS("Position")];
102     [[[o_tbl_dataTable tableColumnWithIdentifier:@"time_offset"] headerCell] \
103         setStringValue: _NS("Time")];
104         
105     /* edit window */
106     [o_edit_btn_ok setTitle: _NS("OK")];
107     [o_edit_btn_cancel setTitle: _NS("Cancel")];
108     [o_edit_lbl_name setStringValue: _NS("Name")];
109     [o_edit_lbl_time setStringValue: _NS("Time")];
110     [o_edit_lbl_bytes setStringValue: _NS("Position")];
111 }
112
113 - (void)showBookmarks
114 {
115     /* show the window, called from intf.m */
116     [o_bookmarks_window displayIfNeeded];
117     [o_bookmarks_window makeKeyAndOrderFront:nil];
118 }
119
120 - (IBAction)add:(id)sender
121 {
122     /* add item to list */
123     intf_thread_t * p_intf = VLCIntf;
124     input_thread_t * p_input = (input_thread_t *)vlc_object_find( p_intf, \
125         VLC_OBJECT_INPUT, FIND_ANYWHERE );
126     if( !p_input )
127         return;
128     
129     seekpoint_t bookmark;
130     vlc_value_t pos;
131     bookmark.psz_name = NULL;
132     bookmark.i_byte_offset = 0;
133     bookmark.i_time_offset = 0;
134     
135     var_Get(p_intf, "position", &pos);
136     bookmark.psz_name = _("Untitled");
137     input_Control( p_input, INPUT_GET_BYTE_POSITION, &bookmark.i_byte_offset );
138     var_Get( p_input, "time", &pos );
139     bookmark.i_time_offset = pos.i_time;
140     input_Control( p_input, INPUT_ADD_BOOKMARK, &bookmark );
141     
142     vlc_object_release( p_input );
143     
144     [o_tbl_dataTable reloadData];
145 }
146
147 - (IBAction)clear:(id)sender
148 {
149     /* clear table */
150     intf_thread_t * p_intf = VLCIntf;
151     input_thread_t *p_input = (input_thread_t *)vlc_object_find( p_intf, \
152         VLC_OBJECT_INPUT, FIND_ANYWHERE );
153     
154     if( !p_input )
155         return;
156
157     input_Control( p_input, INPUT_CLEAR_BOOKMARKS );
158
159     vlc_object_release( p_input );
160     
161     [o_tbl_dataTable reloadData];
162 }
163
164 - (IBAction)edit:(id)sender
165 {
166     /* put values to the sheet's fields and show sheet */
167     /* we take the values from the core and not the table, because we cannot
168      * really trust it */
169     intf_thread_t * p_intf = VLCIntf;
170     input_thread_t * p_input = (input_thread_t *)vlc_object_find( p_intf, \
171         VLC_OBJECT_INPUT, FIND_ANYWHERE );
172     seekpoint_t **pp_bookmarks;
173     int i_bookmarks;
174     char * toBeReturned;
175     toBeReturned = "";
176     int i_toBeReturned;
177     i_toBeReturned = 0;
178     int row;
179     row = [o_tbl_dataTable selectedRow];
180     
181     if( !p_input )
182     {
183         return;
184     } 
185     else if( input_Control( p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, \
186         &i_bookmarks ) != VLC_SUCCESS )
187     {
188         vlc_object_release( p_input );
189         return;
190     } 
191     else if(row < 0)
192     {
193         vlc_object_release( p_input );
194         return;
195     } else {
196         [o_edit_fld_name setStringValue: [NSString stringWithUTF8String: \
197             pp_bookmarks[row]->psz_name]];
198         [o_edit_fld_time setStringValue: [[NSNumber numberWithInt: \
199             (pp_bookmarks[row]->i_time_offset / 1000000)] stringValue]];
200         [o_edit_fld_bytes setStringValue: [[NSNumber numberWithInt: \
201             pp_bookmarks[row]->i_byte_offset] stringValue]];
202     }
203     
204     p_old_input = p_input;
205     vlc_object_release( p_input );
206
207     [NSApp beginSheet: o_edit_window
208         modalForWindow: o_bookmarks_window
209         modalDelegate: o_edit_window
210         didEndSelector: nil
211         contextInfo: nil];
212 }
213
214 - (IBAction)edit_cancel:(id)sender
215 {
216     /* close sheet */
217     [NSApp endSheet:o_edit_window];
218     [o_edit_window close];
219 }
220
221 - (IBAction)edit_ok:(id)sender
222 {
223     /* save field contents and close sheet */
224     
225     intf_thread_t * p_intf = VLCIntf;
226     seekpoint_t **pp_bookmarks;
227     int i_bookmarks, i;
228     input_thread_t *p_input = (input_thread_t *)vlc_object_find( p_intf, \
229         VLC_OBJECT_INPUT, FIND_ANYWHERE );
230     
231     if( !p_input )
232     {
233         NSBeginCriticalAlertSheet(_NS("No input"), _NS("OK"), \
234                 @"", @"", o_bookmarks_window, nil, nil, nil, nil, _NS("No " \
235                 "input found. A stream must be playing or paused for " \
236                 "bookmarks to work."));
237         return;
238     }
239     if( p_old_input != p_input )
240     {
241         NSBeginCriticalAlertSheet(_NS("Input has changed"), _NS("OK"), \
242             @"", @"", o_bookmarks_window, nil, nil, nil, nil, _NS("Input " \
243             "has changed, unable to save bookmark. Suspending playback with " \
244             "\"Pause\" while editing bookmarks to ensure to keep the same " \
245             "input."));
246         vlc_object_release( p_input );
247         return;
248     }
249     
250     if( input_Control( p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, \
251         &i_bookmarks ) != VLC_SUCCESS )
252     {
253         vlc_object_release( p_input );
254         return;
255     } 
256
257     i = [o_tbl_dataTable selectedRow];
258     
259     if( pp_bookmarks[i]->psz_name ) 
260     {
261         free( pp_bookmarks[i]->psz_name );
262     }
263
264     pp_bookmarks[i]->psz_name = strdup([[o_edit_fld_name stringValue] UTF8String]); 
265     pp_bookmarks[i]->i_byte_offset = [[o_edit_fld_bytes stringValue] intValue];
266     pp_bookmarks[i]->i_time_offset = ([[o_edit_fld_time stringValue] intValue]  * 1000000);
267     
268     if( input_Control( p_input, INPUT_CHANGE_BOOKMARK, pp_bookmarks[i], i ) \
269         != VLC_SUCCESS )
270     {
271         msg_Warn( p_intf, "Unable to change the bookmark");
272         vlc_object_release( p_input );
273         return;
274     }
275     
276     [o_tbl_dataTable reloadData];
277     vlc_object_release( p_input );
278      
279     
280     [NSApp endSheet: o_edit_window];
281     [o_edit_window close];
282 }
283
284 - (IBAction)extract:(id)sender
285 {
286     /* extract */
287     
288     intf_thread_t * p_intf = VLCIntf;
289     
290     if( [o_tbl_dataTable numberOfSelectedRows] < 2 )
291     {
292         NSBeginAlertSheet(_NS("Invalid selection"), _NS("OK"), \
293             @"", @"", o_bookmarks_window, nil, nil, nil, nil, _NS("" \
294             "Two bookmarks have to be selected."));
295         return;
296     }
297     input_thread_t *p_input =
298         (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
299                                            FIND_ANYWHERE );
300     if( !p_input )
301     {
302         NSBeginCriticalAlertSheet(_NS("No input found"), _NS("OK"), \
303             @"", @"", o_bookmarks_window, nil, nil, nil, nil, _NS("" \
304             "The stream must be playing or paused for bookmarks to work."));
305         return;
306     }
307     
308     seekpoint_t **pp_bookmarks;
309     int i_bookmarks ;
310     int i_first = -1;
311     int i_second = -1;
312     int x = 0;
313     int c = 0;
314     while (c != 2)
315     {
316         if([o_tbl_dataTable isRowSelected:x])
317         {
318             if (i_first == -1)
319             {
320                 i_first = x;
321                 c = 1;
322             } 
323             else if (i_second == -1)
324             {
325                 i_second = x;
326                 c = 2;
327             }
328         }
329         x = (x + 1);
330     }
331     
332     msg_Dbg(p_intf, "got the bookmark-indexes");
333     
334     if( input_Control( p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, \
335         &i_bookmarks ) != VLC_SUCCESS )
336     {
337         vlc_object_release( p_input );
338         msg_Err(p_intf, "already defined bookmarks couldn't be retrieved");
339         return;
340     }
341     msg_Dbg(p_intf, "calling wizard");
342
343     [[[VLCMain sharedInstance] getWizard] initWithExtractValuesFrom: \
344             [[NSNumber numberWithInt: \
345             (pp_bookmarks[i_first]->i_time_offset/1000000)] stringValue] \
346             to: [[NSNumber numberWithInt: \
347             (pp_bookmarks[i_second]->i_time_offset/1000000)] stringValue] \
348             ofItem: [NSString stringWithUTF8String: \
349             p_input->input.p_item->psz_uri]];
350     vlc_object_release( p_input );
351     msg_Dbg(p_intf, "released input");
352 }
353
354 - (IBAction)goToBookmark:(id)sender
355 {
356     intf_thread_t * p_intf = VLCIntf;
357     input_thread_t *p_input =
358     (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
359     
360     if( !p_input ) 
361     {
362         return;
363     }
364
365     input_Control( p_input, INPUT_SET_BOOKMARK, [o_tbl_dataTable selectedRow] );
366
367     vlc_object_release( p_input );
368 }
369
370 - (IBAction)remove:(id)sender
371 {
372     /* remove selected item */
373     intf_thread_t * p_intf = VLCIntf;
374     input_thread_t *p_input =
375     (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
376     
377     if( !p_input ) return;
378
379     int i_focused = [o_tbl_dataTable selectedRow];
380     if( i_focused >= 0 )
381     {
382         input_Control( p_input, INPUT_DEL_BOOKMARK, i_focused );
383     }
384
385     vlc_object_release( p_input );
386     
387     [o_tbl_dataTable reloadData];
388 }
389
390 /*****************************************************************************
391  * callback stuff
392  *****************************************************************************/
393
394 -(id)getDataTable
395 {
396     return o_tbl_dataTable;
397 }
398
399 /*****************************************************************************
400  * data source methods
401  *****************************************************************************/
402
403 - (int)numberOfRowsInTableView:(NSTableView *)theDataTable
404 {
405     /* return the number of bookmarks */
406     intf_thread_t * p_intf = VLCIntf;
407     input_thread_t * p_input = (input_thread_t *)vlc_object_find( p_intf, \
408         VLC_OBJECT_INPUT, FIND_ANYWHERE );
409     seekpoint_t **pp_bookmarks;
410     int i_bookmarks;
411     
412     if( !p_input )
413     {
414         return 0;
415     }
416     else if( input_Control( p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, \
417                        &i_bookmarks ) != VLC_SUCCESS )
418     {
419         vlc_object_release( p_input );
420         return 0;
421     }
422     else {
423         vlc_object_release( p_input );
424         return i_bookmarks;
425     }
426 }
427
428 - (id)tableView:(NSTableView *)theDataTable objectValueForTableColumn: \
429     (NSTableColumn *)theTableColumn row: (int)row
430 {
431     /* return the corresponding data as NSString */
432     intf_thread_t * p_intf = VLCIntf;
433     input_thread_t * p_input = (input_thread_t *)vlc_object_find( p_intf, \
434         VLC_OBJECT_INPUT, FIND_ANYWHERE );
435     seekpoint_t **pp_bookmarks;
436     int i_bookmarks;
437     char * toBeReturned;
438     toBeReturned = "";
439     int i_toBeReturned;
440     i_toBeReturned = 0;
441     
442     if( !p_input )
443     {
444         return @"";
445     } 
446     else if( input_Control( p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, \
447                        &i_bookmarks ) != VLC_SUCCESS )
448     {
449         vlc_object_release( p_input );
450         return @"";
451     }
452     else
453     {
454         if ([[theTableColumn identifier] isEqualToString: @"description"])
455         {
456             toBeReturned = pp_bookmarks[row]->psz_name;
457             vlc_object_release( p_input );
458             return [NSString stringWithUTF8String: toBeReturned];
459         } 
460         else if ([[theTableColumn identifier] isEqualToString: @"size_offset"])
461         {
462             i_toBeReturned = pp_bookmarks[row]->i_byte_offset;
463             vlc_object_release( p_input );
464             return [[NSNumber numberWithInt: i_toBeReturned] stringValue];
465         }
466         else if ([[theTableColumn identifier] isEqualToString: @"time_offset"])
467         {
468             i_toBeReturned = pp_bookmarks[row]->i_time_offset;
469             vlc_object_release( p_input );
470             return [[NSNumber numberWithInt: (i_toBeReturned / 1000000)] \
471                 stringValue];
472         }
473         else
474         {
475             /* may not happen, but just in case */
476             vlc_object_release( p_input );
477             msg_Err(p_intf, "unknown table column identifier (%s) while" \
478                 "updating the bookmark table", [[theTableColumn identifier] \
479
480                 UTF8String] );
481             return @"unknown identifier";
482         }
483     }
484
485 }
486
487 /*****************************************************************************
488  * delegate methods
489  *****************************************************************************/
490
491 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification
492 {
493     /* check whether a row is selected and en-/disable the edit/remove buttons */
494     if ([o_tbl_dataTable selectedRow] == -1)
495     {
496         /* no row is selected */
497         [o_btn_edit setEnabled: NO];
498         [o_btn_rm setEnabled: NO];
499         [o_btn_extract setEnabled: NO];
500     }
501     else
502     {
503         /* a row is selected */
504         [o_btn_edit setEnabled: YES];
505         [o_btn_rm setEnabled: YES];
506         if ([o_tbl_dataTable numberOfSelectedRows] == 2)
507         {
508             [o_btn_extract setEnabled: YES];
509         }
510     }
511 }
512
513 @end