macosx: Remove private API for sort indicator images
[vlc.git] / modules / gui / macosx / VLCBookmarksWindowController.m
blobb081b2337f65a7dc31994ea80a4fcb7775970ce1
1 /*****************************************************************************
2  * VLCBookmarksWindowController.m: MacOS X Bookmarks window
3  *****************************************************************************
4  * Copyright (C) 2005 - 2015 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  *****************************************************************************/
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  *****************************************************************************/
33 /*****************************************************************************
34  * Preamble
35  *****************************************************************************/
37 #import "VLCBookmarksWindowController.h"
38 #import "CompatibilityFixes.h"
40 @interface VLCBookmarksWindowController() <NSTableViewDataSource, NSTableViewDelegate>
42     input_thread_t *p_old_input;
44 @end
46 @implementation VLCBookmarksWindowController
48 /*****************************************************************************
49  * GUI methods
50  *****************************************************************************/
52 - (id)init
54     self = [super initWithWindowNibName:@"Bookmarks"];
56     return self;
59 - (void)dealloc
61     if (p_old_input)
62         vlc_object_release(p_old_input);
64     [[NSNotificationCenter defaultCenter] removeObserver:self];
67 - (void)windowDidLoad
69     [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
71     _dataTable.dataSource = self;
72     _dataTable.delegate = self;
73     _dataTable.action = @selector(goToBookmark:);
74     _dataTable.target = self;
76     /* main window */
77     [self.window setTitle: _NS("Bookmarks")];
78     [_addButton setTitle: _NS("Add")];
79     [_clearButton setTitle: _NS("Clear")];
80     [_editButton setTitle: _NS("Edit")];
81     [_removeButton setTitle: _NS("Remove")];
82     [[[_dataTable tableColumnWithIdentifier:@"description"] headerCell]
83      setStringValue: _NS("Description")];
84     [[[_dataTable tableColumnWithIdentifier:@"time_offset"] headerCell]
85      setStringValue: _NS("Time")];
87     /* edit window */
88     [_editOKButton setTitle: _NS("OK")];
89     [_editCancelButton setTitle: _NS("Cancel")];
90     [_editNameLabel setStringValue: _NS("Name")];
91     [_editTimeLabel setStringValue: _NS("Time")];
93     [[NSNotificationCenter defaultCenter] addObserver:self
94                                              selector:@selector(inputChangedEvent:)
95                                                  name:VLCInputChangedNotification
96                                                object:nil];
99 - (void)updateCocoaWindowLevel:(NSInteger)i_level
101     if (self.isWindowLoaded && [self.window isVisible] && [self.window level] != i_level)
102         [self.window setLevel: i_level];
105 - (IBAction)toggleWindow:(id)sender
107     if ([self.window isVisible])
108         [self.window orderOut:sender];
109     else {
110         [self.window setLevel: [[[VLCMain sharedInstance] voutProvider] currentStatusWindowLevel]];
111         [self.window makeKeyAndOrderFront:sender];
112     }
115 -(void)inputChangedEvent:(NSNotification *)o_notification
117     [_dataTable reloadData];
120 - (IBAction)add:(id)sender
122     /* add item to list */
123     input_thread_t * p_input = pl_CurrentInput(getIntf());
125     if (!p_input)
126         return;
128     seekpoint_t bookmark;
130     if (!input_Control(p_input, INPUT_GET_BOOKMARK, &bookmark)) {
131         bookmark.psz_name = _("Untitled");
132         input_Control(p_input, INPUT_ADD_BOOKMARK, &bookmark);
133     }
135     vlc_object_release(p_input);
137     [_dataTable reloadData];
140 - (IBAction)clear:(id)sender
142     /* clear table */
143     input_thread_t * p_input = pl_CurrentInput(getIntf());
145     if (!p_input)
146         return;
148     input_Control(p_input, INPUT_CLEAR_BOOKMARKS);
150     vlc_object_release(p_input);
152     [_dataTable reloadData];
155 - (IBAction)edit:(id)sender
157     /* put values to the sheet's fields and show sheet */
158     /* we take the values from the core and not the table, because we cannot
159      * really trust it */
160     input_thread_t * p_input = pl_CurrentInput(getIntf());
161     seekpoint_t **pp_bookmarks;
162     int i_bookmarks;
163     int row = (int)[_dataTable selectedRow];
165     if (!p_input)
166         return;
168     if (row < 0) {
169         vlc_object_release(p_input);
170         return;
171     }
173     if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS) {
174         vlc_object_release(p_input);
175         return;
176     }
178     [_editNameTextField setStringValue: toNSStr(pp_bookmarks[row]->psz_name)];
179     [_editTimeTextField setStringValue:[self timeStringForBookmark:pp_bookmarks[row]]];
181     /* Just keep the pointer value to check if it
182      * changes. Note, we don't need to keep a reference to the object.
183      * so release it now. */
184     p_old_input = p_input;
185     vlc_object_release(p_input);
187     [self.window beginSheet:_editBookmarksWindow completionHandler:nil];
189     // Clear the bookmark list
190     for (int i = 0; i < i_bookmarks; i++)
191         vlc_seekpoint_Delete(pp_bookmarks[i]);
192     free(pp_bookmarks);
195 - (IBAction)edit_cancel:(id)sender
197     /* close sheet */
198     [NSApp endSheet:_editBookmarksWindow];
199     [_editBookmarksWindow close];
202 - (IBAction)edit_ok:(id)sender
204     /* save field contents and close sheet */
205      seekpoint_t **pp_bookmarks;
206     int i_bookmarks;
207     NSInteger i;
208     input_thread_t * p_input = pl_CurrentInput(getIntf());
210     if (!p_input) {
211         NSAlert *alert = [[NSAlert alloc] init];
212         [alert setAlertStyle:NSCriticalAlertStyle];
213         [alert setMessageText:_NS("No input")];
214         [alert setInformativeText:_NS("No input found. A stream must be playing or paused for bookmarks to work.")];
215         [alert beginSheetModalForWindow:self.window
216                       completionHandler:nil];
217         return;
218     }
219     if (p_old_input != p_input) {
220         NSAlert *alert = [[NSAlert alloc] init];
221         [alert setAlertStyle:NSCriticalAlertStyle];
222         [alert setMessageText:_NS("Input has changed")];
223         [alert setInformativeText:_NS("Input has changed, unable to save bookmark. Suspending playback with \"Pause\" while editing bookmarks to ensure to keep the same input.")];
224         [alert beginSheetModalForWindow:self.window
225                       completionHandler:nil];
226         vlc_object_release(p_input);
227         return;
228     }
230     if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS) {
231         vlc_object_release(p_input);
232         return;
233     }
235     i = [_dataTable selectedRow];
237     free(pp_bookmarks[i]->psz_name);
239     pp_bookmarks[i]->psz_name = strdup([[_editNameTextField stringValue] UTF8String]);
241     NSArray * components = [[_editTimeTextField stringValue] componentsSeparatedByString:@":"];
242     NSUInteger componentCount = [components count];
243     if (componentCount == 1)
244         pp_bookmarks[i]->i_time_offset = vlc_tick_from_sec([[components firstObject] floatValue]);
245     else if (componentCount == 2)
246         pp_bookmarks[i]->i_time_offset = vlc_tick_from_sec([[components firstObject] longLongValue] * 60 + [[components objectAtIndex:1] longLongValue]);
247     else if (componentCount == 3)
248         pp_bookmarks[i]->i_time_offset = vlc_tick_from_sec([[components firstObject] longLongValue] * 3600 + [[components objectAtIndex:1] longLongValue] * 60 + [[components objectAtIndex:2] floatValue]);
249     else {
250         msg_Err(getIntf(), "Invalid string format for time");
251         goto clear;
252     }
254     if (input_Control(p_input, INPUT_CHANGE_BOOKMARK, pp_bookmarks[i], i) != VLC_SUCCESS) {
255         msg_Warn(getIntf(), "Unable to change the bookmark");
256         goto clear;
257     }
259     [_dataTable reloadData];
260     vlc_object_release(p_input);
262     [NSApp endSheet: _editBookmarksWindow];
263     [_editBookmarksWindow close];
265 clear:
266     // Clear the bookmark list
267     for (int i = 0; i < i_bookmarks; i++)
268         vlc_seekpoint_Delete(pp_bookmarks[i]);
269     free(pp_bookmarks);
273 - (IBAction)goToBookmark:(id)sender
275     input_thread_t * p_input = pl_CurrentInput(getIntf());
277     if (!p_input)
278         return;
280     input_Control(p_input, INPUT_SET_BOOKMARK, [_dataTable selectedRow]);
282     vlc_object_release(p_input);
285 - (IBAction)remove:(id)sender
287     input_thread_t * p_input = pl_CurrentInput(getIntf());
289     if (!p_input)
290         return;
292     int i_focused = (int)[_dataTable selectedRow];
294     if (i_focused >= 0)
295         input_Control(p_input, INPUT_DEL_BOOKMARK, i_focused);
297     vlc_object_release(p_input);
299     [_dataTable reloadData];
302 - (NSString *)timeStringForBookmark:(seekpoint_t *)bookmark
304     assert(bookmark != NULL);
306     vlc_tick_t total = bookmark->i_time_offset;
307     uint64_t hour = ( total / VLC_TICK_FROM_SEC(3600) );
308     uint64_t min = ( total % VLC_TICK_FROM_SEC(3600) ) / VLC_TICK_FROM_SEC(60);
309     float    sec = secf_from_vlc_tick( total % VLC_TICK_FROM_SEC(60) );
311     return [NSString stringWithFormat:@"%02llu:%02llu:%06.3f", hour, min, sec];
314 /*****************************************************************************
315  * data source methods
316  *****************************************************************************/
318 - (NSInteger)numberOfRowsInTableView:(NSTableView *)theDataTable
320     /* return the number of bookmarks */
321     input_thread_t * p_input = pl_CurrentInput(getIntf());
322     seekpoint_t **pp_bookmarks;
323     int i_bookmarks;
325     if (!p_input)
326         return 0;
328     int returnValue = input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks);
329     vlc_object_release(p_input);
331     if (returnValue != VLC_SUCCESS)
332         return 0;
334     for (int i = 0; i < i_bookmarks; i++)
335         vlc_seekpoint_Delete(pp_bookmarks[i]);
336     free(pp_bookmarks);
338     return i_bookmarks;
341 - (id)tableView:(NSTableView *)theDataTable objectValueForTableColumn: (NSTableColumn *)theTableColumn row: (NSInteger)row
343     /* return the corresponding data as NSString */
344     input_thread_t * p_input = pl_CurrentInput(getIntf());
345     seekpoint_t **pp_bookmarks;
346     int i_bookmarks;
347     id ret = @"";
349     if (!p_input)
350         return @"";
351     else if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS)
352         ret = @"";
353     else if (row >= i_bookmarks)
354         ret = @"";
355     else {
356         NSString * identifier = [theTableColumn identifier];
357         if ([identifier isEqualToString: @"description"])
358             ret = toNSStr(pp_bookmarks[row]->psz_name);
359                 else if ([identifier isEqualToString: @"time_offset"]) {
360             ret = [self timeStringForBookmark:pp_bookmarks[row]];
361         }
363         // Clear the bookmark list
364         for (int i = 0; i < i_bookmarks; i++)
365             vlc_seekpoint_Delete(pp_bookmarks[i]);
366         free(pp_bookmarks);
367     }
368     vlc_object_release(p_input);
369     return ret;
372 /*****************************************************************************
373  * delegate methods
374  *****************************************************************************/
376 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification
378     /* check whether a row is selected and en-/disable the edit/remove buttons */
379     if ([_dataTable selectedRow] == -1) {
380         /* no row is selected */
381         [_editButton setEnabled: NO];
382         [_removeButton setEnabled: NO];
383     } else {
384         /* a row is selected */
385         [_editButton setEnabled: YES];
386         [_removeButton setEnabled: YES];
387     }
390 /* Called when the user hits CMD + C or copy is clicked in the edit menu
391  */
392 - (void) copy:(id)sender {
393     NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
394     NSIndexSet *selectionIndices = [_dataTable selectedRowIndexes];
397     input_thread_t *p_input = pl_CurrentInput(getIntf());
398     int i_bookmarks;
399     seekpoint_t **pp_bookmarks;
401     if (input_Control(p_input, INPUT_GET_BOOKMARKS, &pp_bookmarks, &i_bookmarks) != VLC_SUCCESS)
402         return;
404     [pasteBoard clearContents];
405     NSUInteger index = [selectionIndices firstIndex];
407     while(index != NSNotFound) {
408         /* Get values */
409         if (index >= i_bookmarks)
410             break;
411         NSString *name = toNSStr(pp_bookmarks[index]->psz_name);
412         NSString *time = [self timeStringForBookmark:pp_bookmarks[index]];
414         NSString *message = [NSString stringWithFormat:@"%@ - %@", name, time];
415         [pasteBoard writeObjects:@[message]];
417         /* Get next index */
418         index = [selectionIndices indexGreaterThanIndex:index];
419     }
421     // Clear the bookmark list
422     for (int i = 0; i < i_bookmarks; i++)
423         vlc_seekpoint_Delete(pp_bookmarks[i]);
424     free(pp_bookmarks);
427 #pragma mark -
428 #pragma mark UI validation
430 /* Validate the copy menu item
431  */
432 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
434     SEL theAction = [anItem action];
436     if (theAction == @selector(copy:)) {
437         if ([[_dataTable selectedRowIndexes] count] > 0) {
438             return YES;
439         }
440         return NO;
441     }
442     /* Indicate that we handle the validation method,
443      * even if we don’t implement the action
444      */
445     return YES;
448 @end