add single instance mode
[uwplayer.git] / application / window.c
1 /*
2  * Copyright 2022 Olaf Wintermann
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a 
5  * copy of this software and associated documentation files (the "Software"), 
6  * to deal in the Software without restriction, including without limitation 
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
8  * and/or sell copies of the Software, and to permit persons to whom the 
9  * Software is furnished to do so, subject to the following conditions:
10  * 
11  * The above copyright notice and this permission notice shall be included in 
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
20  * DEALINGS IN THE SOFTWARE.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "window.h"
28 #include "main.h"
29 #include "player.h"
30 #include "playlist.h"
31 #include "xdnd.h"
32 #include "settings.h"
33
34 #include "Fsb.h"
35 #include "Sidebar.h"
36
37 static MainWindow *main_window;
38
39 static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *args, int nargs);
40
41 static void FileOpenCB(Widget w, void *udata, void *cdata);
42 static void FileQuitCB(Widget w, void *udata, void *cdata);
43 static void PlayRepeatCB(Widget w, void *udata, void *cdata);
44 static void PlayRepeatListCB(Widget w, void *udata, void *cdata);
45 static void PlayAutoPlayCB(Widget w, void *udata, void *cdata);
46 static void PlayRandomCB(Widget w, void *udata, void *cdata);
47 static void ViewFullscreenCB(Widget w, void *udata, void *cdata);
48 static void ViewSidebarCB(Widget w, void *udata, void *cdata);
49 static void ViewAdjustWindowSizeCB(Widget w, void *udata, void *cdata);
50 static void PrefSingleInstanceCB(Widget w, void *udata, void *cdata);
51
52 static void WindowRealized(MainWindow *win);
53
54 static int blank_cursor_init = 0;
55 static Pixmap blank_cursor_pixmap;
56 static Cursor blank_cursor;
57
58 static void init_blank_cursor(Widget w) {
59     char data = 0;
60     
61     XColor c;
62     
63     blank_cursor_pixmap = XCreateBitmapFromData(XtDisplay(w), XtWindow(w), &data, 1, 1);
64     if(!blank_cursor_pixmap) return;
65     
66     blank_cursor = XCreatePixmapCursor(XtDisplay(w), blank_cursor_pixmap, blank_cursor_pixmap, &c, &c, 0, 0);
67     
68     XFreePixmap(XtDisplay(w), blank_cursor_pixmap);
69     blank_cursor_init = 1;
70 }
71
72 static void window_close_handler(Widget window, void *udata, void *cdata) {
73     FileQuitCB(window, NULL, NULL);
74 }
75
76 static unsigned int keycodeF;
77
78 static void windowKeyEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
79     MainWindow *win = data;
80     if(win->fullscreen && event->xkey.keycode == keycodeF) {
81         WindowFullscreen(main_window, FALSE);
82         *dispatch = FALSE;
83     }
84 }
85
86 static int main_window_is_realized = 0;
87
88 static void resizeEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {  
89     if(!main_window_is_realized) {
90         if(XtIsRealized(widget)) {
91             main_window_is_realized = 1;
92             WindowRealized(data);
93         }
94     }
95     WindowAdjustAspectRatio(data);
96 }
97
98 static void WindowRealized(MainWindow *win) {
99     char *open_file = GetOpenFileArg();
100     if(open_file) {
101         PlayListAddFile(win, open_file);
102         PlayListPlayNext(win, true);
103         CleanOpenFileArg();
104     }
105     
106     if(!blank_cursor_init) {
107         init_blank_cursor(win->player_widget);
108     }
109     
110     XdndEnable(win->window);
111 }
112
113 static void playerWidgetInputCB(Widget widget, XtPointer u, XtPointer c) {
114     MainWindow *win = u;
115     XmDrawingAreaCallbackStruct *cb = c;
116     
117     if(win->player && win->player->isactive) {
118         PlayerHandleInput(win, win->player, cb);
119     }
120 }
121
122 static void windowGrabButton(MainWindow *win) {
123     //printf("grab\n");
124     XtGrabButton(
125                 win->player_widget,
126                 AnyButton,
127                 AnyModifier,
128                 True,
129                 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | EnterWindowMask | LeaveWindowMask,
130                 GrabModeAsync,
131                 GrabModeAsync,
132                 None,
133                 None);
134     win->buttongrab = True;
135 }
136
137 static void playerEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
138     MainWindow *win = data;
139     int etype = event->type;
140     
141     ///*
142     if(etype == EnterNotify) {
143         //printf("enter\n");
144         windowGrabButton(win);
145         return;
146     }
147     if(etype == LeaveNotify) {
148         //printf("leave\n");
149         //XtUngrabButton(win->player_widget, AnyButton, AnyModifier); 
150         //win->buttongrab = False;
151         return;
152     }
153     
154     int pass = 0;
155     if(etype == ButtonPress || etype == ButtonRelease || etype == KeyPress || etype == KeyRelease) {
156         //printf("button press\n");
157         pass = 1;
158     }
159     
160     if(!win->player || win->player->window == 0) return;
161     
162     WindowHandlePlayerEvent(win, event);
163     
164     if(pass) {
165         // redirect key events to the player window
166         //printf("redirect\n");
167         event->xkey.window = win->player->window;
168         XSendEvent(
169                 XtDisplay(win->player_widget),
170                 win->player->window,
171                 True,
172                 0,
173                 event);
174     }
175 }
176
177 #define IGNORE_MOTION_THRESHOLD_MS 1000
178 #define MOTION_POS_THRESHOLD_PIX   5
179 #define OSD_BOTTOM_THRESHOLD       0.09
180
181 #define DOUBLE_CLICK_TIME_MS       500
182
183 void WindowHandlePlayerEvent(MainWindow *win, XEvent *event) {
184     // event handler for intercepted player mouse events
185     // win->player is not NULL
186     
187     int etype = event->type;
188     
189     if(etype == MotionNotify) {
190         Time cur_motion_time = event->xmotion.time;
191         if(win->player) {
192             win->motion_playback_time = win->player->playback_time;
193         }
194         
195         int x = event->xmotion.x_root;
196         int y = event->xmotion.y_root;
197         if(win->cursorhidden && cur_motion_time - win->player_event_time < IGNORE_MOTION_THRESHOLD_MS) {
198             int diff_x = abs(x - win->mouse_x);
199             int diff_y = abs(y - win->mouse_y);
200             if(diff_x > MOTION_POS_THRESHOLD_PIX || diff_y > MOTION_POS_THRESHOLD_PIX) {
201                 WindowShowPlayerCursor(win);
202             }
203         } else {
204             win->mouse_x = x;
205             win->mouse_y = y;
206         }
207         win->player_event_time = cur_motion_time;
208         win->motion_playback_time = win->player->playback_time;
209         
210         
211         
212         if(win->pwbuttonpressed) {
213             Display *dp = XtDisplay(win->window);
214                 
215             XtUngrabPointer(win->player_widget, CurrentTime);
216
217             XEvent xev;
218             memset(&xev, 0, sizeof(xev));
219             xev.type = ClientMessage;
220             xev.xclient.message_type = XInternAtom(dp, "_NET_WM_MOVERESIZE", False);
221             xev.xclient.window = XtWindow(win->window);
222             xev.xclient.format = 32;
223             xev.xclient.data.l[0] = x;
224             xev.xclient.data.l[1] = y;
225             xev.xclient.data.l[2] = 8; // _NET_WM_MOVERESIZE_MOVE
226             xev.xclient.data.l[3] = 1; // button1
227             xev.xclient.data.l[4] = 1; // source indication
228
229             XSendEvent(dp, DefaultRootWindow(dp), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
230
231             win->pwbuttonpressed = FALSE;  
232         }
233     } else if(etype == ButtonPress) {
234         Time t = event->xbutton.time;
235         
236         int yi = win->player_widget->core.height - event->xbutton.y;
237         if((float)yi/(float)win->player_widget->core.height < OSD_BOTTOM_THRESHOLD) {
238             win->button_press_time = 0;
239         } else {
240             if(t - win->button_press_time < DOUBLE_CLICK_TIME_MS) {
241                 // double click
242                 WindowFullscreen(main_window, !win->fullscreen);
243                 win->button_press_time = 0;
244             } else {
245                 win->button_press_time = t;
246             }
247             win->pwbuttonpressed = 1;
248         }
249     } else if(etype == ButtonRelease) {
250         win->player_event_time = event->xbutton.time;
251         win->pwbuttonpressed = FALSE;
252     }
253 }
254
255
256
257 MainWindow* WindowCreate(Display *display) {
258     Arg args[32];
259     int n;
260      
261     MainWindow *window = malloc(sizeof(MainWindow));
262     memset(window, 0, sizeof(MainWindow));
263     main_window = window;
264       
265     // toplevel window
266     n = 0;
267     XtSetArg(args[n], XmNtitle, APP_NAME); n++;
268     window->window = XtAppCreateShell(
269             APP_NAME,
270             APP_CLASS,
271             applicationShellWidgetClass,
272             //vendorShellWidgetClass,
273             display,
274             args,
275             n);
276     
277     // close handler
278     Atom wm_delete_window;
279     wm_delete_window = XmInternAtom(
280             display,
281             "WM_DELETE_WINDOW",
282             0);
283     XmAddWMProtocolCallback(
284             window->window,
285             wm_delete_window,
286             window_close_handler,
287             window);
288     
289     // resize handler
290     XtAddEventHandler(window->window, StructureNotifyMask, False, resizeEH, window);
291     
292     n = 0;
293     XtSetArg(args[n], XmNwidth, 360); n++;
294     XtSetArg(args[n], XmNheight, 220); n++;
295     XtSetArg(args[n], XmNshadowThickness, 0); n++;
296     Widget container = XmCreateForm(window->window, "form", args, n);
297     XtManageChild(container);
298     XtAddEventHandler(container, KeyPressMask, FALSE, windowKeyEH, window);
299     
300     n = 0;
301     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
302     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
303     XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
304     WindowCreateMenu(window, container, args, n);
305     
306     n = 0;
307     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
308     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
309     XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
310     XtSetArg(args[n], XmNtopWidget, window->menubar); n++;
311     XtSetArg(args[n], XmNwidth, 300); n++;
312     XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); n++;
313     XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmAS_NEEDED); n++;
314     XtSetArg(args[n], XmNspacing, 0); n++;
315     XtSetArg(args[n], XmNshadowThickness, 0); n++;
316     window->sidebar_scrolledwindow = XmCreateScrolledWindow(container, "sw_sidebar", args, n);
317     window->sidebar = CreateSidebar(window->sidebar_scrolledwindow, "sidebar", args, 0);
318     SidebarSetWindow(window->sidebar, window);
319     XtManageChild(window->sidebar);
320     //XtManageChild(window->sidebar);
321         
322     n = 0;
323     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
324     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
325     XtSetArg(args[n], XmNrightWidget, window->sidebar); n++;
326     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
327     XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
328     XtSetArg(args[n], XmNtopWidget, window->menubar); n++;
329     XtSetArg(args[n], XmNbackground, BlackPixelOfScreen(XtScreen(window->window))); n++;
330     window->player_widget = XmCreateDrawingArea(container, "player", args, n);
331     XtManageChild(window->player_widget);
332     XtAddCallback(window->player_widget, XmNinputCallback, playerWidgetInputCB, window);
333     XmProcessTraversal(window->player_widget, XmTRAVERSE_CURRENT);
334     XtAddEventHandler(window->player_widget, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
335                  EnterWindowMask | KeyPressMask | KeyReleaseMask |
336                   LeaveWindowMask, FALSE, playerEH, window);
337     
338      
339     // get F keycode
340     keycodeF = XKeysymToKeycode(XtDisplay(window->window), XStringToKeysym("F"));
341     
342     
343     PlayListInit(window);
344     
345     window->adjustWindowSize = true; // auto adjust window size by default
346     
347     return window;
348 }
349
350 MainWindow* GetMainWindow(void) {
351     return main_window;
352 }
353
354 void WindowShow(MainWindow *win) {
355     XtRealizeWidget(win->window);
356 }
357
358 /*
359  * Creates a XmPushButton menu item
360  */
361 static Widget createMenuItem(
362         Widget menu,
363         char *name,
364         char *label,
365         char mnemonic,
366         const char *accelerator,
367         char *accelerator_text,
368         XtCallbackProc callback,
369         void *cbData)
370 {
371     Arg args[16];
372     int n = 0;
373     
374     XmString s1 = XmStringCreateSimple(label);
375     XtSetArg(args[n], XmNlabelString, s1); n++;
376     XtSetArg(args[n], XmNmnemonic, mnemonic); n++;
377     XmString at = NULL;
378     if(accelerator && accelerator_text) {
379         at = XmStringCreateSimple(accelerator_text);
380         XtSetArg(args[n], XmNaccelerator, accelerator); n++;
381         XtSetArg(args[n], XmNacceleratorText, at); n++;
382     }
383     
384     Widget menuItem = XmCreatePushButtonGadget(menu, name, args, n);
385     XtManageChild(menuItem);
386     XmStringFree(s1);
387     if(at) XmStringFree(at);
388     
389     if(callback) {
390         XtAddCallback(menuItem, XmNactivateCallback, (XtCallbackProc)callback, cbData);
391     }
392     
393     return menuItem;
394 }
395
396 /*
397  * Creates a XmToggleButton menu item
398  */
399 static Widget createToggleMenuItem(
400         Widget menu,
401         char *name,
402         char *label,
403         char mnemonic,
404         Boolean defaultValue,
405         const char *accelerator,
406         char *accelerator_text,
407         XtCallbackProc callback,
408         void *cbData)
409 {
410     Arg args[16];
411     int n = 0;
412     
413     XmString s1 = XmStringCreateSimple(label);
414     XtSetArg(args[n], XmNlabelString, s1); n++;
415     XtSetArg(args[n], XmNmnemonic, mnemonic); n++;
416     XtSetArg(args[n], XmNset, defaultValue); n++;
417     XmString at = NULL;
418     if(accelerator && accelerator_text) {
419         at = XmStringCreateSimple(accelerator_text);
420         XtSetArg(args[n], XmNaccelerator, accelerator); n++;
421         XtSetArg(args[n], XmNacceleratorText, at); n++;
422     }
423     
424     Widget menuItem = XmCreateToggleButtonGadget(menu, name, args, n);
425     XtManageChild(menuItem);
426     XmStringFree(s1);
427     if(at) XmStringFree(at);
428     
429     if(callback) {
430         XtAddCallback(menuItem, XmNvalueChangedCallback, (XtCallbackProc)callback, cbData);
431     }
432     
433     return menuItem;
434 }
435
436 /*
437  * Creates a menu separator
438  */
439 static Widget createMenuSeparator(Widget menu) {
440     Widget w = XmCreateSeparator(menu, "separator", NULL, 0);
441     XtManageChild(w);
442     return w;
443 }
444
445 static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *mbargs, int nmbargs) {
446     Widget menubar = XmCreateMenuBar(parent, "menubar", mbargs, nmbargs);
447     XtManageChild(menubar);
448     win->menubar = menubar;
449     
450     Arg args[16];
451     int n;
452     
453     // menus
454     XmString s = XmStringCreateSimple("File");
455     XtVaCreateManagedWidget(
456             "menuitem",
457             xmCascadeButtonWidgetClass,
458             menubar,
459             XmNlabelString, s,
460             NULL);
461     XmStringFree(s);
462     Widget fileMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 0, NULL, NULL);
463     
464     s = XmStringCreateSimple("Playback");
465     XtVaCreateManagedWidget(
466             "menuitem",
467             xmCascadeButtonWidgetClass,
468             menubar,
469             XmNlabelString, s,
470             NULL);
471     XmStringFree(s);
472     Widget playMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 1, NULL, NULL);
473     
474     s = XmStringCreateSimple("View");
475     Widget viewMenuItem = XtVaCreateManagedWidget(
476             "menuitem",
477             xmCascadeButtonWidgetClass,
478             menubar,
479             XmNlabelString, s,
480             NULL);
481     XmStringFree(s);
482     Widget viewMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 2, NULL, NULL);
483     
484     s = XmStringCreateSimple("Preferences");
485     Widget prefMenuItem = XtVaCreateManagedWidget(
486             "menuitem",
487             xmCascadeButtonWidgetClass,
488             menubar,
489             XmNlabelString, s,
490             NULL);
491     XmStringFree(s);
492     Widget prefMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 3, NULL, NULL); 
493     
494     // file menu
495     createMenuItem(fileMenu, "fileOpen", "Open...", 'O', "Ctrl<Key>O", "Ctrl+O", FileOpenCB, NULL);
496     createMenuItem(fileMenu, "fileQuit", "Exit", 'E', "Ctrl<Key>Q", "Ctrl+Q", FileQuitCB, NULL);
497     
498     // play menu
499     win->playRepeatTrackButton = createToggleMenuItem(playMenu, "playRepeatTrack", "Repeat", 'R', False, NULL, NULL, PlayRepeatCB, win);
500     win->playRepeatListButton = createToggleMenuItem(playMenu, "playRepeatList", "Repeat List", 'L', False, NULL, NULL, PlayRepeatListCB, win);
501     win->playRandom = createToggleMenuItem(playMenu, "playRandom", "Random Playback", 'P', False, NULL, NULL, PlayRandomCB, win);
502     win->playAutoPlayButton = createToggleMenuItem(playMenu, "playAutoNext", "Autoplay Folder", 'A', False, NULL, NULL, PlayAutoPlayCB, win);
503     XtVaSetValues(win->playRepeatTrackButton, XmNindicatorType, XmONE_OF_MANY, NULL);
504     XtVaSetValues(win->playRepeatListButton, XmNindicatorType, XmONE_OF_MANY, NULL);
505     XtVaSetValues(win->playAutoPlayButton, XmNindicatorType, XmONE_OF_MANY, NULL);
506     XtVaSetValues(win->playRandom, XmNindicatorType, XmONE_OF_MANY, NULL);
507     
508     // view menu
509     createMenuItem(viewMenu, "viewFullscreen", "Fullscreen", 'F', "<Key>F", "F", ViewFullscreenCB, NULL);
510     win->viewSidebarButton = createToggleMenuItem(viewMenu, "viewSidebar", "View Sidebar", 'S', False, NULL, NULL, ViewSidebarCB, win);
511     
512     createMenuSeparator(viewMenu);
513     
514     win->viewAdjustWindowSize = createToggleMenuItem(viewMenu, "viewAdjustWindowSize", "Adjust Window Size", 'W', TRUE, NULL, NULL, ViewAdjustWindowSizeCB, win);
515     
516     // preferences menu
517      win->prefSingleInstanceButton = createToggleMenuItem(prefMenu, "prefSingleInstance", "Single Instance", 'S', FALSE, NULL, NULL, PrefSingleInstanceCB, win);
518 }
519
520 void go_fullscreen(Display *dsp, Window win)
521 {
522   XEvent xev;
523   Atom wm_state = XInternAtom(dsp, "_NET_WM_STATE", False);
524   Atom fullscreen = XInternAtom(dsp, "_NET_WM_STATE_FULLSCREEN", False);
525   memset(&xev, 0, sizeof(xev));
526   xev.type = ClientMessage;
527   xev.xclient.window = win;
528   xev.xclient.message_type = wm_state;
529   xev.xclient.format = 32;
530   xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
531   xev.xclient.data.l[1] = fullscreen;
532   xev.xclient.data.l[2] = 0;
533   XSendEvent(dsp, DefaultRootWindow(dsp), False,
534     SubstructureNotifyMask, &xev);
535 }
536
537 static Atom net_wm_state;
538 static Atom net_wm_state_fullscreen;
539 static int net_wm_atoms_initialized = 0;
540
541 void WindowFullscreen(MainWindow *win, bool enableFullscreen) {
542     Display *dpy = XtDisplay(win->window);
543     
544     // init net_wm_state atoms
545     if(!net_wm_atoms_initialized) {
546         net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
547         net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
548         net_wm_atoms_initialized = 1;
549     }
550     
551     WindowMenubarSetVisible(win, !enableFullscreen);
552     if(enableFullscreen && !win->fullscreen) {
553         XtUnmanageChild(main_window->menubar);
554         main_window->fullscreen = TRUE;
555     } else if(!enableFullscreen && win->fullscreen) {
556         XtManageChild(main_window->menubar);
557         main_window->fullscreen = FALSE;
558     }
559     
560     WindowShowSidebar(win, enableFullscreen ? false : win->sidebarvisible);
561     
562     XEvent ev;
563     memset(&ev, 0, sizeof(XEvent));
564     ev.type = ClientMessage;
565     ev.xclient.window = XtWindow(win->window);
566     ev.xclient.message_type = net_wm_state;
567     ev.xclient.format = 32;
568     ev.xclient.data.l[0] = enableFullscreen ? 1 : 0;
569     ev.xclient.data.l[1] = net_wm_state_fullscreen;
570     ev.xclient.data.l[2] = 0;
571     XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask | SubstructureRedirectMask, &ev);
572 }
573
574 void WindowMenubarSetVisible(MainWindow *win, bool visible) {
575     if(visible) {
576         XtManageChild(main_window->menubar);
577         XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, main_window->menubar, NULL);
578     } else {
579         XtUnmanageChild(main_window->menubar);
580         XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_FORM, NULL);
581     }
582 }
583
584 static void filedialog_end(
585         Widget widget,
586         MainWindow *data,
587         XmFileSelectionBoxCallbackStruct *selection)
588 {
589     XtUnmanageChild(widget);
590     XtDestroyWidget(widget);
591 }
592
593 static void filedialog_select(
594         Widget widget,
595         MainWindow *data,
596         XmFileSelectionBoxCallbackStruct *selection)
597 {
598     char *value = NULL;
599     if(selection->value) {
600         XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &value);
601         if(value) {
602             PlayListAddFile(data, value);
603             PlayListPlayNext(data, true);
604             XtFree(value);
605         }
606     }
607     filedialog_end(widget, data, NULL);
608 }
609
610
611
612
613 static void FileOpenCB(Widget w, void *udata, void *cdata) {
614     MainWindow *win = main_window;
615     
616     Arg args[16];
617     int n = 0;
618     
619     XtSetArg(args[n], XnNshowViewMenu, 1); n++;
620     Widget dialog = XnCreateFileSelectionDialog(win->window, "dialog", args, n);
621     XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, win);
622     XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_end, win);
623     
624     Widget dirUp = XnFileSelectionBoxGetChild(dialog, XnFSB_DIR_UP_BUTTON);
625     XtUnmanageChild(dirUp);
626     
627     XtManageChild(dialog);
628 }
629
630 static void FileQuitCB(Widget w, void *udata, void *cdata) {
631     WindowClosePlayer(main_window);
632     ApplicationExit();
633 }
634
635 static void PlayRepeatCB(Widget w, void *udata, void *cdata) {
636     MainWindow *win = udata;
637     win->playlist.repeatTrack = XmToggleButtonGadgetGetState(w);
638     win->playlist.repeatList = 0;
639     win->playlist.autoplayFolder = 0;
640     win->playlist.random = 0;
641     XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL);
642     XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL);
643     XtVaSetValues(win->playRandom, XmNset, 0, NULL);
644 }
645
646 static void PlayRepeatListCB(Widget w, void *udata, void *cdata) {
647     MainWindow *win = udata;
648     win->playlist.repeatList = XmToggleButtonGadgetGetState(w);
649     win->playlist.repeatTrack = 0;
650     win->playlist.autoplayFolder = 0;
651     win->playlist.random = 0;
652     XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL);
653     XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL);
654     XtVaSetValues(win->playRandom, XmNset, 0, NULL);
655 }
656
657 static void PlayAutoPlayCB(Widget w, void *udata, void *cdata) {
658     MainWindow *win = udata;
659     win->playlist.autoplayFolder = XmToggleButtonGadgetGetState(w);
660     win->playlist.repeatTrack = 0;
661     win->playlist.repeatList = 0;
662     win->playlist.random = 0;
663     XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL);
664     XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL);
665     XtVaSetValues(win->playRandom, XmNset, 0, NULL);
666 }
667
668 static void PlayRandomCB(Widget w, void *udata, void *cdata) {
669     MainWindow *win = udata;
670     win->playlist.random = XmToggleButtonGadgetGetState(w);
671     win->playlist.repeatTrack = 0;
672     win->playlist.repeatList = 0;
673     win->playlist.autoplayFolder = 0;
674     XtVaSetValues(win->playRepeatTrackButton, XmNset, 0, NULL);
675     XtVaSetValues(win->playRepeatListButton, XmNset, 0, NULL);
676     XtVaSetValues(win->playAutoPlayButton, XmNset, 0, NULL);
677 }
678
679 static void ViewFullscreenCB(Widget w, void *udata, void *cdata) {
680     if(main_window->fullscreen) {
681         WindowFullscreen(main_window, FALSE);
682     } else {
683         WindowFullscreen(main_window, TRUE);
684     }   
685 }
686
687 static void ViewSidebarCB(Widget w, void *udata, void *cdata) {
688     MainWindow *win = udata;
689     XmToggleButtonCallbackStruct *cb = cdata;
690     win->sidebarvisible = cb->set;
691     WindowShowSidebar(win, cb->set);
692 }
693
694 static void ViewAdjustWindowSizeCB(Widget w, void *udata, void *cdata) {
695     MainWindow *win = udata;
696     win->adjustWindowSize = XmToggleButtonGadgetGetState(w);
697 }
698
699 static void PrefSingleInstanceCB(Widget w, void *udata, void *cdata) {
700     MainWindow *win = udata;
701     win->singleInstance = XmToggleButtonGadgetGetState(w);
702     
703     Display *dp = XtDisplay(w);
704     
705     if(!win->singleInstance) {
706         ShutdownInstanceSocket(dp);
707         return;
708     }
709     
710     Bool disable_item = False;
711     if(CreateSingleInstanceSocket(dp, &disable_item)) {
712         // TODO: err
713         disable_item = True;
714     }
715     if(disable_item) {
716         win->singleInstance = 0;
717         XmToggleButtonGadgetSetState(w, False, False);
718     }
719 }
720
721 void WindowAdjustAspectRatio(MainWindow *win) {
722     if(!win->player) return;
723     if(!win->player->isactive || win->player->width <= 0 || win->player->height <= 0) return;
724       
725     // we have a running player width video
726     // adjust window aspect ratio (the window aspect ratio is different from
727     // the video, because of window decoration, menubar and other extra controls)
728     
729     Dimension win_width, win_height;
730     XtVaGetValues(win->window, XmNwidth, &win_width, XmNheight, &win_height, NULL);
731     Dimension player_width, player_height;
732     XtVaGetValues(win->player_widget, XmNwidth, &player_width, XmNheight, &player_height, NULL);
733     
734     double r = (double)win->player->width / (double)win->player->height;
735     double p_width = player_width;
736     double p_height = p_width / r;
737     
738     Dimension new_width = p_width + win_width - player_width;
739     Dimension new_height = p_height + win_height - player_height;
740     
741     XSizeHints hints;
742     hints.flags = PAspect;
743     hints.min_aspect.x = new_width;
744     hints.min_aspect.y = new_height;
745     hints.max_aspect.x = new_width;
746     hints.max_aspect.y = new_height;
747     XSetWMNormalHints(XtDisplay(win->window), XtWindow(win->window), &hints);
748 }
749
750 void WindowClosePlayer(MainWindow *win) {
751     if(win->player) {
752         PlayerDestroy(win->player);
753     }
754     win->player = NULL;
755     WindowShowPlayerCursor(win);
756 }
757
758 void WindowHidePlayerCursor(MainWindow *win) {
759     if(!win->cursorhidden && win->player && win->player->window != 0) {
760         XDefineCursor(XtDisplay(win->player_widget), XtWindow(win->player_widget), blank_cursor);
761         win->cursorhidden = True;
762         XFlush(XtDisplay(win->player_widget));
763     }
764 }
765
766 void WindowShowPlayerCursor(MainWindow *win) {
767     if(win->cursorhidden && win->player && win->player->window != 0) {
768         XDefineCursor(XtDisplay(win->player_widget), XtWindow(win->player_widget), None);
769         XFlush(XtDisplay(win->player_widget));
770     }
771     win->cursorhidden = False;
772 }
773
774 void WindowShowSidebar(MainWindow *win, bool visible) {
775     if(visible) {
776         XtManageChild(win->sidebar_scrolledwindow);
777         XtVaSetValues(win->player_widget, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, win->sidebar_scrolledwindow, NULL);
778     } else {
779         XtUnmanageChild(win->sidebar_scrolledwindow);
780         XtVaSetValues(win->player_widget, XmNrightAttachment, XmATTACH_FORM, NULL);
781     }
782 }
783
784 void WindowUpdate(MainWindow *win) {
785     SidebarRepaint(win->sidebar);
786 }