1 /*  Audacious - Cross-platform multimedia player
2  *  Copyright (C) 2005-2011  Audacious development team.
3  *
4  *  BMP - Cross-platform multimedia player
5  *  Copyright (C) 2003-2004  BMP development team.
6  *
7  *  Based on XMMS:
8  *  Copyright (C) 1998-2003  XMMS development team.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; under version 3 of the License.
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, see <http://www.gnu.org/licenses>.
21  *
22  *  The Audacious team does not consider modular code linking to
23  *  Audacious or using our public API to be a derived work.
24  */
25 
26 #include <math.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/time.h>
30 
31 #include <gdk/gdkkeysyms.h>
32 #include <gtk/gtk.h>
33 
34 #include <libaudcore/audstrings.h>
35 #include <libaudcore/drct.h>
36 #include <libaudcore/hook.h>
37 #include <libaudcore/i18n.h>
38 #include <libaudcore/mainloop.h>
39 #include <libaudcore/plugins.h>
40 #include <libaudcore/runtime.h>
41 #include <libaudgui/libaudgui.h>
42 
43 #include "actions-mainwin.h"
44 #include "actions-playlist.h"
45 #include "dnd.h"
46 #include "menus.h"
47 #include "plugin.h"
48 #include "skins_cfg.h"
49 #include "equalizer.h"
50 #include "main.h"
51 #include "vis-callbacks.h"
52 #include "playlistwin.h"
53 #include "button.h"
54 #include "hslider.h"
55 #include "menurow.h"
56 #include "monostereo.h"
57 #include "number.h"
58 #include "playlist-widget.h"
59 #include "playstatus.h"
60 #include "textbox.h"
61 #include "window.h"
62 #include "vis.h"
63 #include "skins_util.h"
64 #include "view.h"
65 
66 #include "../ui-common/menu-ops.h"
67 
68 #define SEEK_THRESHOLD 200 /* milliseconds */
69 #define SEEK_SPEED 50 /* milliseconds per pixel */
70 
71 class MainWindow : public Window
72 {
73 public:
MainWindow(bool shaded)74     MainWindow (bool shaded) :
75         Window (WINDOW_MAIN, & config.player_x, & config.player_y,
76          shaded ? MAINWIN_SHADED_WIDTH : skin.hints.mainwin_width,
77          shaded ? MAINWIN_SHADED_HEIGHT : skin.hints.mainwin_height, shaded) {}
78 
79 private:
80     void draw (cairo_t * cr);
81     bool button_press (GdkEventButton * event);
82     bool scroll (GdkEventScroll * event);
83     bool motion (GdkEventMotion * event);
84     bool leave ();
85 
86     QueuedFunc m_popup_timer;
87     bool m_popup_shown = false;
88 };
89 
90 Window * mainwin;
91 
92 Button * mainwin_eq, * mainwin_pl;
93 TextBox * mainwin_info;
94 MenuRow * mainwin_menurow;
95 
96 SkinnedVis * mainwin_vis;
97 SmallVis * mainwin_svis;
98 
99 static bool seeking = false;
100 static int seek_start, seek_time;
101 
102 static TextBox * locked_textbox = nullptr;
103 static String locked_old_text;
104 
105 static QueuedFunc status_message_timeout;
106 static QueuedFunc mainwin_volume_release_timeout;
107 
108 static Button * mainwin_menubtn, * mainwin_minimize, * mainwin_shade, * mainwin_close;
109 static Button * mainwin_shaded_menubtn, * mainwin_shaded_minimize, * mainwin_shaded_shade, * mainwin_shaded_close;
110 
111 static Button * mainwin_rew, * mainwin_fwd;
112 static Button * mainwin_eject;
113 static Button * mainwin_play, * mainwin_pause, * mainwin_stop;
114 static Button * mainwin_shuffle, * mainwin_repeat;
115 
116 static TextBox * mainwin_stime_min, * mainwin_stime_sec;
117 static TextBox * mainwin_rate_text, * mainwin_freq_text, * mainwin_othertext;
118 
119 static PlayStatus * mainwin_playstatus;
120 static SkinnedNumber * mainwin_minus_num, * mainwin_10min_num, * mainwin_min_num;
121 static SkinnedNumber * mainwin_10sec_num, * mainwin_sec_num;
122 static HSlider * mainwin_position, * mainwin_sposition;
123 
124 static HSlider * mainwin_volume, * mainwin_balance;
125 static MonoStereo * mainwin_monostereo;
126 
127 static Button * mainwin_srew, * mainwin_splay, * mainwin_spause;
128 static Button * mainwin_sstop, * mainwin_sfwd, * mainwin_seject, * mainwin_about;
129 
130 static void mainwin_position_motion_cb ();
131 static void mainwin_position_release_cb ();
132 static void seek_timeout (void * rewind);
133 
134 /* always returns a 6-character string */
format_time(int time,int length)135 static StringBuf format_time (int time, int length)
136 {
137     bool zero = aud_get_bool ("leading_zero");
138     bool remaining = aud_get_bool ("skins", "show_remaining_time");
139 
140     if (remaining && length > 0)
141     {
142         time = (length - time) / 1000;
143         time = aud::clamp(0, time, 359999); // 99:59:59
144 
145         if (time < 60)
146             return str_printf (zero ? "-00:%02d" : " -0:%02d", time);
147         else if (time < 6000)
148             return str_printf (zero ? "%03d:%02d" : "%3d:%02d", -time / 60, time % 60);
149         else
150             return str_printf ("%3d:%02d", -time / 3600, time / 60 % 60);
151     }
152     else
153     {
154         time /= 1000;
155         time = aud::clamp(0, time, 3599999); // 999:59:59
156 
157         if (time < 6000)
158             return str_printf (zero ? " %02d:%02d" : " %2d:%02d", time / 60, time % 60);
159         else if (time < 60000)
160             return str_printf ("%3d:%02d", time / 60, time % 60);
161         else
162             return str_printf ("%3d:%02d", time / 3600, time / 60 % 60);
163     }
164 }
165 
mainwin_menubtn_cb()166 static void mainwin_menubtn_cb ()
167 {
168     int x, y;
169     mainwin->getPosition (& x, & y);
170     menu_popup (UI_MENU_MAIN, x + 6 * config.scale,
171      y + MAINWIN_SHADED_HEIGHT * config.scale, false, false, 1, GDK_CURRENT_TIME);
172 }
173 
mainwin_minimize_cb()174 static void mainwin_minimize_cb ()
175 {
176     gtk_window_iconify ((GtkWindow *) mainwin->gtk ());
177 }
178 
mainwin_shade_toggle()179 static void mainwin_shade_toggle ()
180 {
181     view_set_player_shaded (! aud_get_bool ("skins", "player_shaded"));
182 }
183 
mainwin_lock_info_text(const char * text)184 static void mainwin_lock_info_text (const char * text)
185 {
186     if (! locked_textbox)
187     {
188         locked_textbox = skin.hints.mainwin_othertext_is_status ? mainwin_othertext : mainwin_info;
189         locked_old_text = locked_textbox->get_text ();
190     }
191 
192     locked_textbox->set_text (text);
193 }
194 
mainwin_release_info_text()195 static void mainwin_release_info_text ()
196 {
197     if (locked_textbox)
198     {
199         locked_textbox->set_text (locked_old_text);
200         locked_textbox = nullptr;
201         locked_old_text = String ();
202     }
203 }
204 
set_info_text(TextBox * textbox,const char * text)205 static void set_info_text (TextBox * textbox, const char * text)
206 {
207     if (textbox == locked_textbox)
208         locked_old_text = String (text);
209     else
210         textbox->set_text (text);
211 }
212 
213 #define mainwin_set_info_text(t) set_info_text (mainwin_info, (t))
214 #define mainwin_set_othertext(t) set_info_text (mainwin_othertext, (t))
215 
mainwin_show_status_message(const char * message)216 void mainwin_show_status_message (const char * message)
217 {
218     mainwin_lock_info_text (message);
219     status_message_timeout.queue (1000, mainwin_release_info_text);
220 }
221 
mainwin_set_song_title(const char * title)222 static void mainwin_set_song_title (const char * title)
223 {
224     StringBuf buf;
225 
226     if (title)
227         buf = str_printf (_("%s - Audacious"), title);
228     else
229         buf = str_copy (_("Audacious"));
230 
231     int instance = aud_get_instance ();
232     if (instance != 1)
233         str_append_printf (buf, " (%d)", instance);
234 
235     mainwin->setWindowTitle ((const char *) buf);
236     mainwin_set_info_text (title ? title : "");
237 }
238 
title_change()239 static void title_change ()
240 {
241     if (aud_drct_get_ready ())
242         mainwin_set_song_title (aud_drct_get_title ());
243     else
244         mainwin_set_song_title ("Buffering ...");
245 }
246 
setup_widget(Widget * widget,int x,int y,bool show)247 static void setup_widget (Widget * widget, int x, int y, bool show)
248 {
249     int width, height;
250 
251     /* use get_size_request(), not get_preferred_size() */
252     /* get_preferred_size() will return 0x0 for hidden widgets */
253     gtk_widget_get_size_request (widget->gtk (), & width, & height);
254 
255     width /= config.scale;
256     height /= config.scale;
257 
258     /* hide widgets that are outside the window boundary */
259     if (x < 0 || x + width > skin.hints.mainwin_width ||
260      y < 0 || y + height > skin.hints.mainwin_height)
261         show = false;
262 
263     widget->setVisible (show);
264     mainwin->move_widget (false, widget, x, y);
265 }
266 
mainwin_refresh_hints()267 void mainwin_refresh_hints ()
268 {
269     const SkinHints * p = & skin.hints;
270 
271     mainwin_menurow->setVisible (p->mainwin_menurow_visible);
272     mainwin_rate_text->setVisible (p->mainwin_streaminfo_visible);
273     mainwin_freq_text->setVisible (p->mainwin_streaminfo_visible);
274     mainwin_monostereo->setVisible (p->mainwin_streaminfo_visible);
275 
276     mainwin_info->set_width (p->mainwin_text_width);
277 
278     setup_widget (mainwin_vis, p->mainwin_vis_x, p->mainwin_vis_y, p->mainwin_vis_visible);
279     setup_widget (mainwin_info, p->mainwin_text_x, p->mainwin_text_y, p->mainwin_text_visible);
280     setup_widget (mainwin_othertext, p->mainwin_infobar_x, p->mainwin_infobar_y, p->mainwin_othertext_visible);
281 
282     bool playing = aud_drct_get_playing ();
283     bool can_seek = aud_drct_get_length () > 0;
284 
285     setup_widget (mainwin_minus_num, p->mainwin_number_0_x, p->mainwin_number_0_y, playing);
286     setup_widget (mainwin_10min_num, p->mainwin_number_1_x, p->mainwin_number_1_y, playing);
287     setup_widget (mainwin_min_num, p->mainwin_number_2_x, p->mainwin_number_2_y, playing);
288     setup_widget (mainwin_10sec_num, p->mainwin_number_3_x, p->mainwin_number_3_y, playing);
289     setup_widget (mainwin_sec_num, p->mainwin_number_4_x, p->mainwin_number_4_y, playing);
290     setup_widget (mainwin_position, p->mainwin_position_x, p->mainwin_position_y, can_seek);
291 
292     setup_widget (mainwin_playstatus, p->mainwin_playstatus_x, p->mainwin_playstatus_y, true);
293     setup_widget (mainwin_volume, p->mainwin_volume_x, p->mainwin_volume_y, true);
294     setup_widget (mainwin_balance, p->mainwin_balance_x, p->mainwin_balance_y, true);
295     setup_widget (mainwin_rew, p->mainwin_previous_x, p->mainwin_previous_y, true);
296     setup_widget (mainwin_play, p->mainwin_play_x, p->mainwin_play_y, true);
297     setup_widget (mainwin_pause, p->mainwin_pause_x, p->mainwin_pause_y, true);
298     setup_widget (mainwin_stop, p->mainwin_stop_x, p->mainwin_stop_y, true);
299     setup_widget (mainwin_fwd, p->mainwin_next_x, p->mainwin_next_y, true);
300     setup_widget (mainwin_eject, p->mainwin_eject_x, p->mainwin_eject_y, true);
301     setup_widget (mainwin_eq, p->mainwin_eqbutton_x, p->mainwin_eqbutton_y, true);
302     setup_widget (mainwin_pl, p->mainwin_plbutton_x, p->mainwin_plbutton_y, true);
303     setup_widget (mainwin_shuffle, p->mainwin_shuffle_x, p->mainwin_shuffle_y, true);
304     setup_widget (mainwin_repeat, p->mainwin_repeat_x, p->mainwin_repeat_y, true);
305     setup_widget (mainwin_about, p->mainwin_about_x, p->mainwin_about_y, true);
306     setup_widget (mainwin_minimize, p->mainwin_minimize_x, p->mainwin_minimize_y, true);
307     setup_widget (mainwin_shade, p->mainwin_shade_x, p->mainwin_shade_y, true);
308     setup_widget (mainwin_close, p->mainwin_close_x, p->mainwin_close_y, true);
309 
310     if (aud_get_bool ("skins", "player_shaded"))
311         mainwin->resize (MAINWIN_SHADED_WIDTH, MAINWIN_SHADED_HEIGHT);
312     else
313         mainwin->resize (p->mainwin_width, p->mainwin_height);
314 
315     mainwin_vis->set_colors ();
316 }
317 
318 /* note that the song info is not translated since it is displayed using
319  * the skinned bitmap font, which supports only the English alphabet */
mainwin_set_song_info(int bitrate,int samplerate,int channels)320 static void mainwin_set_song_info (int bitrate, int samplerate, int channels)
321 {
322     char scratch[32];
323     int length;
324 
325     if (bitrate > 0)
326     {
327         if (bitrate < 1000000)
328             snprintf (scratch, sizeof scratch, "%3d", bitrate / 1000);
329         else
330             snprintf (scratch, sizeof scratch, "%2dH", bitrate / 100000);
331 
332         mainwin_rate_text->set_text (scratch);
333     }
334     else
335         mainwin_rate_text->set_text (nullptr);
336 
337     if (samplerate > 0)
338     {
339         snprintf (scratch, sizeof scratch, "%2d", samplerate / 1000);
340         mainwin_freq_text->set_text (scratch);
341     }
342     else
343         mainwin_freq_text->set_text (nullptr);
344 
345     mainwin_monostereo->set_num_channels (channels);
346 
347     if (bitrate > 0)
348         snprintf (scratch, sizeof scratch, "%d kbps", bitrate / 1000);
349     else
350         scratch[0] = 0;
351 
352     if (samplerate > 0)
353     {
354         length = strlen (scratch);
355         snprintf (scratch + length, sizeof scratch - length, "%s%d kHz", length ?
356          ", " : "", samplerate / 1000);
357     }
358 
359     if (channels > 0)
360     {
361         length = strlen (scratch);
362         snprintf (scratch + length, sizeof scratch - length, "%s%s", length ?
363          ", " : "", channels > 2 ? "surround" : channels > 1 ? "stereo" : "mono");
364     }
365 
366     mainwin_set_othertext (scratch);
367 }
368 
info_change()369 static void info_change ()
370 {
371     int bitrate, samplerate, channels;
372     aud_drct_get_info (bitrate, samplerate, channels);
373     mainwin_set_song_info (bitrate, samplerate, channels);
374 }
375 
playback_pause()376 static void playback_pause ()
377 {
378     mainwin_playstatus->set_status (STATUS_PAUSE);
379 }
380 
playback_unpause()381 static void playback_unpause ()
382 {
383     mainwin_playstatus->set_status (STATUS_PLAY);
384 }
385 
mainwin_playback_begin()386 void mainwin_playback_begin ()
387 {
388     mainwin_update_song_info ();
389 
390     mainwin_stime_min->show ();
391     mainwin_stime_sec->show ();
392     mainwin_minus_num->show ();
393     mainwin_10min_num->show ();
394     mainwin_min_num->show ();
395     mainwin_10sec_num->show ();
396     mainwin_sec_num->show ();
397 
398     if (aud_drct_get_length () > 0)
399     {
400         mainwin_position->show ();
401         mainwin_sposition->show ();
402     }
403 
404     if (aud_drct_get_paused ())
405         playback_pause ();
406     else
407         playback_unpause ();
408 
409     title_change ();
410     info_change ();
411 }
412 
mainwin_playback_stop()413 static void mainwin_playback_stop ()
414 {
415     seeking = false;
416     timer_remove (TimerRate::Hz10, seek_timeout);
417 
418     mainwin_set_song_title (nullptr);
419 
420     mainwin_vis->clear ();
421     mainwin_svis->clear ();
422 
423     mainwin_minus_num->hide ();
424     mainwin_10min_num->hide ();
425     mainwin_min_num->hide ();
426     mainwin_10sec_num->hide ();
427     mainwin_sec_num->hide ();
428     mainwin_stime_min->hide ();
429     mainwin_stime_sec->hide ();
430     mainwin_position->hide ();
431     mainwin_sposition->hide ();
432 
433     mainwin_position->set_pressed (false);
434     mainwin_sposition->set_pressed (false);
435 
436     /* clear sampling parameter displays */
437     mainwin_rate_text->set_text (nullptr);
438     mainwin_freq_text->set_text (nullptr);
439     mainwin_monostereo->set_num_channels (0);
440     mainwin_set_othertext ("");
441 
442     mainwin_playstatus->set_status (STATUS_STOP);
443 
444     playlistwin_hide_timer();
445 }
446 
record_toggled()447 static void record_toggled ()
448 {
449     if (aud_drct_get_record_enabled ())
450     {
451         if (aud_get_bool ("record"))
452             mainwin_show_status_message (_("Recording on"));
453         else
454             mainwin_show_status_message (_("Recording off"));
455     }
456 }
457 
repeat_toggled()458 static void repeat_toggled ()
459 {
460     mainwin_repeat->set_active (aud_get_bool ("repeat"));
461 }
462 
shuffle_toggled()463 static void shuffle_toggled ()
464 {
465     mainwin_shuffle->set_active (aud_get_bool ("shuffle"));
466 }
467 
no_advance_toggled()468 static void no_advance_toggled ()
469 {
470     if (aud_get_bool ("no_playlist_advance"))
471         mainwin_show_status_message (_("Single mode."));
472     else
473         mainwin_show_status_message (_("Playlist mode."));
474 }
475 
stop_after_song_toggled()476 static void stop_after_song_toggled ()
477 {
478     if (aud_get_bool ("stop_after_current_song"))
479         mainwin_show_status_message (_("Stopping after song."));
480 }
481 
scroll(GdkEventScroll * event)482 bool MainWindow::scroll (GdkEventScroll * event)
483 {
484     switch (event->direction)
485     {
486         case GDK_SCROLL_UP:
487             mainwin_set_volume_diff (aud_get_int ("volume_delta"));
488             break;
489         case GDK_SCROLL_DOWN:
490             mainwin_set_volume_diff (-aud_get_int ("volume_delta"));
491             break;
492         case GDK_SCROLL_LEFT:
493             aud_drct_seek (aud_drct_get_time () - aud_get_int ("step_size") * 1000);
494             break;
495         case GDK_SCROLL_RIGHT:
496             aud_drct_seek (aud_drct_get_time () + aud_get_int ("step_size") * 1000);
497             break;
498         default:
499             break;
500     }
501 
502     return true;
503 }
504 
button_press(GdkEventButton * event)505 bool MainWindow::button_press (GdkEventButton * event)
506 {
507     if (event->button == 1 && event->type == GDK_2BUTTON_PRESS &&
508      event->window == gtk_widget_get_window (gtk ()) &&
509      event->y < 14 * config.scale)
510     {
511         mainwin_shade_toggle ();
512         return true;
513     }
514 
515     if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
516     {
517         menu_popup (UI_MENU_MAIN, event->x_root, event->y_root, false, false,
518          event->button, event->time);
519         return true;
520     }
521 
522     return Window::button_press (event);
523 }
524 
motion(GdkEventMotion * event)525 bool MainWindow::motion (GdkEventMotion * event)
526 {
527     if (is_shaded () &&
528         event->x >= 79 * config.scale &&
529         event->x <= 157 * config.scale &&
530         aud_get_bool ("show_filepopup_for_tuple"))
531     {
532         if (! m_popup_shown)
533         {
534             m_popup_timer.queue (aud_get_int ("filepopup_delay") * 100,
535              audgui_infopopup_show_current);
536             m_popup_shown = true;
537         }
538     }
539     else if (m_popup_shown)
540     {
541         audgui_infopopup_hide ();
542         m_popup_timer.stop ();
543         m_popup_shown = false;
544     }
545 
546     return Window::motion (event);
547 }
548 
leave()549 bool MainWindow::leave ()
550 {
551     if (m_popup_shown)
552     {
553         audgui_infopopup_hide ();
554         m_popup_timer.stop ();
555         m_popup_shown = false;
556     }
557 
558     return Window::leave ();
559 }
560 
mainwin_playback_rpress(Button * button,GdkEventButton * event)561 static void mainwin_playback_rpress (Button * button, GdkEventButton * event)
562 {
563     menu_popup (UI_MENU_PLAYBACK, event->x_root, event->y_root, false, false,
564      event->button, event->time);
565 }
566 
keypress(GdkEventKey * event)567 bool Window::keypress (GdkEventKey * event)
568 {
569     if (playlistwin_list->handle_keypress (event))
570         return true;
571 
572     switch (event->keyval)
573     {
574         case GDK_KEY_Left:
575         case GDK_KEY_KP_Left:
576         case GDK_KEY_KP_7:
577             aud_drct_seek (aud_drct_get_time () - aud_get_int ("step_size") * 1000);
578             break;
579         case GDK_KEY_Right:
580         case GDK_KEY_KP_Right:
581         case GDK_KEY_KP_9:
582             aud_drct_seek (aud_drct_get_time () + aud_get_int ("step_size") * 1000);
583             break;
584         case GDK_KEY_KP_4:
585             aud_drct_pl_prev ();
586             break;
587         case GDK_KEY_KP_6:
588             aud_drct_pl_next ();
589             break;
590         case GDK_KEY_KP_Insert:
591             audgui_jump_to_track ();
592             break;
593         case GDK_KEY_space:
594             aud_drct_pause ();
595             break;
596         case GDK_KEY_Tab: /* GtkUIManager does not handle tab, apparently. */
597             if (event->state & GDK_SHIFT_MASK)
598                 pl_prev ();
599             else
600                 pl_next ();
601 
602             break;
603         case GDK_KEY_ISO_Left_Tab:
604             pl_prev ();
605             break;
606         default:
607             return false;
608     }
609 
610     return true;
611 }
612 
mainwin_drag_data_received(GtkWidget * widget,GdkDragContext * context,int x,int y,GtkSelectionData * selection_data,unsigned info,unsigned time,void *)613 void mainwin_drag_data_received (GtkWidget * widget, GdkDragContext * context,
614  int x, int y, GtkSelectionData * selection_data, unsigned info, unsigned time, void *)
615 {
616     g_return_if_fail (selection_data != nullptr);
617 
618     const char * data = (const char *) gtk_selection_data_get_data (selection_data);
619     g_return_if_fail (data);
620 
621     if (str_has_prefix_nocase (data, "file:///"))
622     {
623         if (str_has_suffix_nocase (data, ".wsz\r\n") || str_has_suffix_nocase
624          (data, ".zip\r\n"))
625         {
626             on_skin_view_drag_data_received (0, context, x, y, selection_data, info, time, 0);
627             return;
628         }
629     }
630 
631     audgui_urilist_open (data);
632 }
633 
time_now()634 static int time_now ()
635 {
636     struct timeval tv;
637     gettimeofday (& tv, nullptr);
638     return (tv.tv_sec % (24 * 3600) * 1000 + tv.tv_usec / 1000);
639 }
640 
time_diff(int a,int b)641 static int time_diff (int a, int b)
642 {
643     if (a > 18 * 3600 * 1000 && b < 6 * 3600 * 1000) /* detect midnight */
644         b += 24 * 3600 * 1000;
645     return (b > a) ? b - a : 0;
646 }
647 
seek_timeout(void * rewind)648 static void seek_timeout (void * rewind)
649 {
650     int held = time_diff (seek_time, time_now ());
651     if (held < SEEK_THRESHOLD)
652         return;
653 
654     int position;
655     if (aud::from_ptr<bool> (rewind))
656         position = seek_start - held / SEEK_SPEED;
657     else
658         position = seek_start + held / SEEK_SPEED;
659 
660     position = aud::clamp (position, 0, 219);
661     mainwin_position->set_pos (position);
662     mainwin_position_motion_cb ();
663 }
664 
seek_press(GdkEventButton * event,bool rewind)665 static void seek_press (GdkEventButton * event, bool rewind)
666 {
667     if (event->button != 1 || seeking)
668         return;
669 
670     seeking = true;
671     seek_start = mainwin_position->get_pos ();
672     seek_time = time_now ();
673     timer_add (TimerRate::Hz10, seek_timeout, aud::to_ptr (rewind));
674 }
675 
seek_release(GdkEventButton * event,bool rewind)676 static void seek_release (GdkEventButton * event, bool rewind)
677 {
678     if (event->button != 1 || ! seeking)
679         return;
680 
681     if (! aud_drct_get_playing () || time_diff (seek_time, time_now ()) <
682      SEEK_THRESHOLD)
683     {
684         if (rewind)
685             aud_drct_pl_prev ();
686         else
687             aud_drct_pl_next ();
688     }
689     else
690         mainwin_position_release_cb ();
691 
692     seeking = false;
693     timer_remove (TimerRate::Hz10, seek_timeout);
694 }
695 
mainwin_rew_press(Button * button,GdkEventButton * event)696 static void mainwin_rew_press (Button * button, GdkEventButton * event)
697     { seek_press (event, true); }
mainwin_rew_release(Button * button,GdkEventButton * event)698 static void mainwin_rew_release (Button * button, GdkEventButton * event)
699     { seek_release (event, true); }
mainwin_fwd_press(Button * button,GdkEventButton * event)700 static void mainwin_fwd_press (Button * button, GdkEventButton * event)
701     { seek_press (event, false); }
mainwin_fwd_release(Button * button,GdkEventButton * event)702 static void mainwin_fwd_release (Button * button, GdkEventButton * event)
703     { seek_release (event, false); }
704 
mainwin_shuffle_cb(Button * button,GdkEventButton * event)705 static void mainwin_shuffle_cb (Button * button, GdkEventButton * event)
706     { aud_set_bool ("shuffle", button->get_active ()); }
mainwin_repeat_cb(Button * button,GdkEventButton * event)707 static void mainwin_repeat_cb (Button * button, GdkEventButton * event)
708     { aud_set_bool ("repeat", button->get_active ()); }
mainwin_eq_cb(Button * button,GdkEventButton * event)709 static void mainwin_eq_cb (Button * button, GdkEventButton * event)
710     { view_set_show_equalizer (button->get_active ()); }
mainwin_pl_cb(Button * button,GdkEventButton * event)711 static void mainwin_pl_cb (Button * button, GdkEventButton * event)
712     { view_set_show_playlist (button->get_active ()); }
713 
mainwin_spos_set_knob()714 static void mainwin_spos_set_knob ()
715 {
716     int pos = mainwin_sposition->get_pos ();
717     int x = (pos < 6) ? 17 : (pos < 9) ? 20 : 23;
718     mainwin_sposition->set_knob (x, 36, x, 36);
719 }
720 
mainwin_spos_motion_cb()721 static void mainwin_spos_motion_cb ()
722 {
723     mainwin_spos_set_knob ();
724 
725     int pos = mainwin_sposition->get_pos ();
726     int length = aud_drct_get_length ();
727     int time = (pos - 1) * length / 12;
728 
729     StringBuf buf = format_time (time, length);
730 
731     mainwin_stime_min->set_text (buf);
732     mainwin_stime_sec->set_text (buf + 4);
733 }
734 
mainwin_spos_release_cb()735 static void mainwin_spos_release_cb ()
736 {
737     mainwin_spos_set_knob ();
738 
739     int pos = mainwin_sposition->get_pos ();
740     aud_drct_seek (aud_drct_get_length () * (pos - 1) / 12);
741 }
742 
mainwin_position_motion_cb()743 static void mainwin_position_motion_cb ()
744 {
745     int length = aud_drct_get_length () / 1000;
746     int pos = mainwin_position->get_pos ();
747     int time = pos * length / 219;
748 
749     mainwin_lock_info_text (str_printf (_("Seek to %d:%-2.2d / %d:%-2.2d"),
750      time / 60, time % 60, length / 60, length % 60));
751 }
752 
mainwin_position_release_cb()753 static void mainwin_position_release_cb ()
754 {
755     int length = aud_drct_get_length ();
756     int pos = mainwin_position->get_pos ();
757     int time = (int64_t) pos * length / 219;
758 
759     aud_drct_seek(time);
760     mainwin_release_info_text();
761 }
762 
mainwin_adjust_volume_motion(int v)763 void mainwin_adjust_volume_motion (int v)
764 {
765     aud_drct_set_volume_main (v);
766     mainwin_lock_info_text (str_printf (_("Volume: %d%%"), v));
767 }
768 
mainwin_adjust_volume_release()769 void mainwin_adjust_volume_release ()
770 {
771     mainwin_release_info_text ();
772 }
773 
mainwin_adjust_balance_motion(int b)774 void mainwin_adjust_balance_motion (int b)
775 {
776     aud_drct_set_volume_balance (b);
777 
778     if (b < 0)
779         mainwin_lock_info_text (str_printf (_("Balance: %d%% left"), -b));
780     else if (b == 0)
781         mainwin_lock_info_text (_("Balance: center"));
782     else
783         mainwin_lock_info_text (str_printf (_("Balance: %d%% right"), b));
784 }
785 
mainwin_adjust_balance_release()786 void mainwin_adjust_balance_release ()
787 {
788     mainwin_release_info_text ();
789 }
790 
mainwin_volume_set_frame()791 static void mainwin_volume_set_frame ()
792 {
793     int pos = mainwin_volume->get_pos ();
794     int frame = (pos * 27 + 25) / 51;
795     mainwin_volume->set_frame (0, 15 * frame);
796 }
797 
mainwin_set_volume_slider(int percent)798 void mainwin_set_volume_slider (int percent)
799 {
800     mainwin_volume->set_pos ((percent * 51 + 50) / 100);
801     mainwin_volume_set_frame ();
802 }
803 
mainwin_volume_motion_cb()804 static void mainwin_volume_motion_cb ()
805 {
806     mainwin_volume_set_frame ();
807     int pos = mainwin_volume->get_pos ();
808     int vol = (pos * 100 + 25) / 51;
809 
810     mainwin_adjust_volume_motion (vol);
811     equalizerwin_set_volume_slider (vol);
812 }
813 
mainwin_volume_release_cb()814 static void mainwin_volume_release_cb ()
815 {
816     mainwin_volume_set_frame ();
817     mainwin_adjust_volume_release ();
818 }
819 
mainwin_balance_set_frame()820 static void mainwin_balance_set_frame ()
821 {
822     int pos = mainwin_balance->get_pos ();
823     int frame = (abs (pos - 12) * 27 + 6) / 12;
824     mainwin_balance->set_frame (9, 15 * frame);
825 }
826 
mainwin_set_balance_slider(int percent)827 void mainwin_set_balance_slider (int percent)
828 {
829     if (percent > 0)
830         mainwin_balance->set_pos (12 + (percent * 12 + 50) / 100);
831     else
832         mainwin_balance->set_pos (12 + (percent * 12 - 50) / 100);
833 
834     mainwin_balance_set_frame ();
835 }
836 
mainwin_balance_motion_cb()837 static void mainwin_balance_motion_cb ()
838 {
839     mainwin_balance_set_frame ();
840     int pos = mainwin_balance->get_pos ();
841 
842     int bal;
843     if (pos > 12)
844         bal = ((pos - 12) * 100 + 6) / 12;
845     else
846         bal = ((pos - 12) * 100 - 6) / 12;
847 
848     mainwin_adjust_balance_motion (bal);
849     equalizerwin_set_balance_slider (bal);
850 }
851 
mainwin_balance_release_cb()852 static void mainwin_balance_release_cb ()
853 {
854     mainwin_balance_set_frame ();
855     mainwin_adjust_volume_release ();
856 }
857 
mainwin_set_volume_diff(int diff)858 void mainwin_set_volume_diff (int diff)
859 {
860     int vol = aud_drct_get_volume_main ();
861 
862     vol = aud::clamp (vol + diff, 0, 100);
863     mainwin_adjust_volume_motion (vol);
864     mainwin_set_volume_slider (vol);
865     equalizerwin_set_volume_slider (vol);
866 
867     mainwin_volume_release_timeout.queue (700, mainwin_volume_release_cb);
868 }
869 
mainwin_mr_change(MenuRowItem i)870 void mainwin_mr_change (MenuRowItem i)
871 {
872     switch (i)
873     {
874         case MENUROW_OPTIONS:
875             mainwin_lock_info_text (_("Options Menu"));
876             break;
877         case MENUROW_ALWAYS:
878             if (aud_get_bool ("skins", "always_on_top"))
879                 mainwin_lock_info_text (_("Disable 'Always On Top'"));
880             else
881                 mainwin_lock_info_text (_("Enable 'Always On Top'"));
882             break;
883         case MENUROW_FILEINFOBOX:
884             mainwin_lock_info_text (_("File Info Box"));
885             break;
886         case MENUROW_SCALE:
887             mainwin_lock_info_text (_("Double Size"));
888             break;
889         case MENUROW_VISUALIZATION:
890             mainwin_lock_info_text (_("Visualizations"));
891             break;
892         default:
893             break;
894     }
895 }
896 
mainwin_mr_release(MenuRowItem i,GdkEventButton * event)897 void mainwin_mr_release (MenuRowItem i, GdkEventButton * event)
898 {
899     switch (i)
900     {
901         case MENUROW_OPTIONS:
902             menu_popup (UI_MENU_VIEW, event->x_root, event->y_root, false, false, 1, event->time);
903             break;
904         case MENUROW_ALWAYS:
905             view_set_on_top (! aud_get_bool ("skins", "always_on_top"));
906             break;
907         case MENUROW_FILEINFOBOX:
908             audgui_infowin_show_current ();
909             break;
910         case MENUROW_SCALE:
911             view_set_double_size (! aud_get_bool ("skins", "double_size"));
912             break;
913         case MENUROW_VISUALIZATION:
914             audgui_show_prefs_for_plugin_type (PluginType::Vis);
915             break;
916         default:
917             break;
918     }
919 
920     mainwin_release_info_text();
921 }
922 
change_timer_mode_cb(GdkEventButton * event)923 bool change_timer_mode_cb (GdkEventButton * event)
924 {
925     if (event->type != GDK_BUTTON_PRESS || event->button != 1)
926         return false;
927 
928     view_set_show_remaining (! aud_get_bool ("skins", "show_remaining_time"));
929     return true;
930 }
931 
mainwin_info_button_press(GdkEventButton * event)932 static bool mainwin_info_button_press (GdkEventButton * event)
933 {
934     if (event->type == GDK_BUTTON_PRESS && event->button == 3)
935     {
936         menu_popup (UI_MENU_PLAYBACK, event->x_root, event->y_root, false,
937          false, event->button, event->time);
938         return true;
939     }
940 
941     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
942     {
943         audgui_infowin_show_current ();
944         return true;
945     }
946 
947     return false;
948 }
949 
mainwin_create_widgets()950 static void mainwin_create_widgets ()
951 {
952     mainwin_menubtn = new Button (9, 9, 0, 0, 0, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
953     mainwin->put_widget (false, mainwin_menubtn, 6, 3);
954     mainwin_menubtn->on_release ((ButtonCB) mainwin_menubtn_cb);
955 
956     mainwin_minimize = new Button (9, 9, 9, 0, 9, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
957     mainwin->put_widget (false, mainwin_minimize, 244, 3);
958     mainwin_minimize->on_release ((ButtonCB) mainwin_minimize_cb);
959 
960     mainwin_shade = new Button (9, 9, 0, 18, 9, 18, SKIN_TITLEBAR, SKIN_TITLEBAR);
961     mainwin->put_widget (false, mainwin_shade, 254, 3);
962     mainwin_shade->on_release ((ButtonCB) mainwin_shade_toggle);
963 
964     mainwin_close = new Button (9, 9, 18, 0, 18, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
965     mainwin->put_widget (false, mainwin_close, 264, 3);
966     mainwin_close->on_release ((ButtonCB) skins_close);
967 
968     mainwin_rew = new Button (23, 18, 0, 0, 0, 18, SKIN_CBUTTONS, SKIN_CBUTTONS);
969     mainwin->put_widget (false, mainwin_rew, 16, 88);
970     mainwin_rew->on_press (mainwin_rew_press);
971     mainwin_rew->on_release (mainwin_rew_release);
972     mainwin_rew->on_rpress (mainwin_playback_rpress);
973 
974     mainwin_fwd = new Button (22, 18, 92, 0, 92, 18, SKIN_CBUTTONS, SKIN_CBUTTONS);
975     mainwin->put_widget (false, mainwin_fwd, 108, 88);
976     mainwin_fwd->on_press (mainwin_fwd_press);
977     mainwin_fwd->on_release (mainwin_fwd_release);
978     mainwin_fwd->on_rpress (mainwin_playback_rpress);
979 
980     mainwin_play = new Button (23, 18, 23, 0, 23, 18, SKIN_CBUTTONS, SKIN_CBUTTONS);
981     mainwin->put_widget (false, mainwin_play, 39, 88);
982     mainwin_play->on_release ((ButtonCB) aud_drct_play);
983     mainwin_play->on_rpress (mainwin_playback_rpress);
984 
985     mainwin_pause = new Button (23, 18, 46, 0, 46, 18, SKIN_CBUTTONS, SKIN_CBUTTONS);
986     mainwin->put_widget (false, mainwin_pause, 62, 88);
987     mainwin_pause->on_release ((ButtonCB) aud_drct_pause);
988     mainwin_pause->on_rpress (mainwin_playback_rpress);
989 
990     mainwin_stop = new Button (23, 18, 69, 0, 69, 18, SKIN_CBUTTONS, SKIN_CBUTTONS);
991     mainwin->put_widget (false, mainwin_stop, 85, 88);
992     mainwin_stop->on_release ((ButtonCB) aud_drct_stop);
993     mainwin_stop->on_rpress (mainwin_playback_rpress);
994 
995     mainwin_eject = new Button (22, 16, 114, 0, 114, 16, SKIN_CBUTTONS, SKIN_CBUTTONS);
996     mainwin->put_widget (false, mainwin_eject, 136, 89);
997     mainwin_eject->on_release ((ButtonCB) action_play_file);
998 
999     mainwin_shuffle = new Button (46, 15, 28, 0, 28, 15, 28, 30, 28, 45, SKIN_SHUFREP, SKIN_SHUFREP);
1000     mainwin->put_widget (false, mainwin_shuffle, 164, 89);
1001     mainwin_shuffle->set_active (aud_get_bool ("shuffle"));
1002     mainwin_shuffle->on_release (mainwin_shuffle_cb);
1003 
1004     mainwin_repeat = new Button (28, 15, 0, 0, 0, 15, 0, 30, 0, 45, SKIN_SHUFREP, SKIN_SHUFREP);
1005     mainwin->put_widget (false, mainwin_repeat, 210, 89);
1006     mainwin_repeat->set_active (aud_get_bool ("repeat"));
1007     mainwin_repeat->on_release (mainwin_repeat_cb);
1008 
1009     mainwin_eq = new Button (23, 12, 0, 61, 46, 61, 0, 73, 46, 73, SKIN_SHUFREP, SKIN_SHUFREP);
1010     mainwin->put_widget (false, mainwin_eq, 219, 58);
1011     mainwin_eq->on_release (mainwin_eq_cb);
1012 
1013     mainwin_pl = new Button (23, 12, 23, 61, 69, 61, 23, 73, 69, 73, SKIN_SHUFREP, SKIN_SHUFREP);
1014     mainwin->put_widget (false, mainwin_pl, 242, 58);
1015     mainwin_pl->on_release (mainwin_pl_cb);
1016 
1017     String font;
1018     if (! config.mainwin_use_bitmapfont)
1019         font = aud_get_str ("skins", "mainwin_font");
1020 
1021     bool shaded = aud_get_bool ("skins", "mainwin_shaded");
1022     mainwin_info = new TextBox (153, font, ! shaded && config.autoscroll);
1023     mainwin->put_widget (false, mainwin_info, 112, 27);
1024     mainwin_info->on_press (mainwin_info_button_press);
1025 
1026     mainwin_othertext = new TextBox (153, nullptr, false);
1027     mainwin->put_widget (false, mainwin_othertext, 112, 43);
1028 
1029     mainwin_rate_text = new TextBox (15, nullptr, false);
1030     mainwin->put_widget (false, mainwin_rate_text, 111, 43);
1031 
1032     mainwin_freq_text = new TextBox (10, nullptr, false);
1033     mainwin->put_widget (false, mainwin_freq_text, 156, 43);
1034 
1035     mainwin_menurow = new MenuRow;
1036     mainwin->put_widget (false, mainwin_menurow, 10, 22);
1037 
1038     mainwin_volume = new HSlider (0, 51, SKIN_VOLUME, 68, 13, 0, 0, 14, 11, 15, 422, 0, 422);
1039     mainwin->put_widget (false, mainwin_volume, 107, 57);
1040     mainwin_volume->on_move (mainwin_volume_motion_cb);
1041     mainwin_volume->on_release (mainwin_volume_release_cb);
1042 
1043     mainwin_balance = new HSlider (0, 24, SKIN_BALANCE, 38, 13, 9, 0, 14, 11, 15, 422, 0, 422);
1044     mainwin->put_widget (false, mainwin_balance, 177, 57);
1045     mainwin_balance->on_move (mainwin_balance_motion_cb);
1046     mainwin_balance->on_release (mainwin_balance_release_cb);
1047 
1048     mainwin_monostereo = new MonoStereo;
1049     mainwin->put_widget (false, mainwin_monostereo, 212, 41);
1050 
1051     mainwin_playstatus = new PlayStatus;
1052     mainwin->put_widget (false, mainwin_playstatus, 24, 28);
1053 
1054     mainwin_minus_num = new SkinnedNumber;
1055     mainwin->put_widget (false, mainwin_minus_num, 36, 26);
1056 
1057     mainwin_10min_num = new SkinnedNumber;
1058     mainwin->put_widget (false, mainwin_10min_num, 48, 26);
1059 
1060     mainwin_min_num = new SkinnedNumber;
1061     mainwin->put_widget (false, mainwin_min_num, 60, 26);
1062 
1063     mainwin_10sec_num = new SkinnedNumber;
1064     mainwin->put_widget (false, mainwin_10sec_num, 78, 26);
1065 
1066     mainwin_sec_num = new SkinnedNumber;
1067     mainwin->put_widget (false, mainwin_sec_num, 90, 26);
1068 
1069     mainwin_about = new Button (20, 25);
1070     mainwin->put_widget (false, mainwin_about, 247, 83);
1071     mainwin_about->on_release ((ButtonCB) audgui_show_about_window);
1072 
1073     mainwin_vis = new SkinnedVis;
1074     mainwin->put_widget (false, mainwin_vis, 24, 43);
1075 
1076     mainwin_position = new HSlider (0, 219, SKIN_POSBAR, 248, 10, 0, 0, 29, 10, 248, 0, 278, 0);
1077     mainwin->put_widget (false, mainwin_position, 16, 72);
1078     mainwin_position->on_move (mainwin_position_motion_cb);
1079     mainwin_position->on_release (mainwin_position_release_cb);
1080 
1081     /* shaded */
1082 
1083     mainwin_shaded_menubtn = new Button (9, 9, 0, 0, 0, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
1084     mainwin->put_widget (true, mainwin_shaded_menubtn, 6, 3);
1085     mainwin_shaded_menubtn->on_release ((ButtonCB) mainwin_menubtn_cb);
1086 
1087     mainwin_shaded_minimize = new Button (9, 9, 9, 0, 9, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
1088     mainwin->put_widget (true, mainwin_shaded_minimize, 244, 3);
1089     mainwin_shaded_minimize->on_release ((ButtonCB) mainwin_minimize_cb);
1090 
1091     mainwin_shaded_shade = new Button (9, 9, 0, 27, 9, 27, SKIN_TITLEBAR, SKIN_TITLEBAR);
1092     mainwin->put_widget (true, mainwin_shaded_shade, 254, 3);
1093     mainwin_shaded_shade->on_release ((ButtonCB) mainwin_shade_toggle);
1094 
1095     mainwin_shaded_close = new Button (9, 9, 18, 0, 18, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
1096     mainwin->put_widget (true, mainwin_shaded_close, 264, 3);
1097     mainwin_shaded_close->on_release ((ButtonCB) skins_close);
1098 
1099     mainwin_srew = new Button (8, 7);
1100     mainwin->put_widget (true, mainwin_srew, 169, 4);
1101     mainwin_srew->on_release ((ButtonCB) aud_drct_pl_prev);
1102 
1103     mainwin_splay = new Button (10, 7);
1104     mainwin->put_widget (true, mainwin_splay, 177, 4);
1105     mainwin_splay->on_release ((ButtonCB) aud_drct_play);
1106 
1107     mainwin_spause = new Button (10, 7);
1108     mainwin->put_widget (true, mainwin_spause, 187, 4);
1109     mainwin_spause->on_release ((ButtonCB) aud_drct_pause);
1110 
1111     mainwin_sstop = new Button (9, 7);
1112     mainwin->put_widget (true, mainwin_sstop, 197, 4);
1113     mainwin_sstop->on_release ((ButtonCB) aud_drct_stop);
1114 
1115     mainwin_sfwd = new Button (8, 7);
1116     mainwin->put_widget (true, mainwin_sfwd, 206, 4);
1117     mainwin_sfwd->on_release ((ButtonCB) aud_drct_pl_next);
1118 
1119     mainwin_seject = new Button (9, 7);
1120     mainwin->put_widget (true, mainwin_seject, 216, 4);
1121     mainwin_seject->on_release ((ButtonCB) action_play_file);
1122 
1123     mainwin_svis = new SmallVis ();
1124     mainwin->put_widget (true, mainwin_svis, 79, 5);
1125 
1126     mainwin_sposition = new HSlider (1, 13, SKIN_TITLEBAR, 17, 7, 0, 36, 3, 7, 17, 36, 17, 36);
1127     mainwin->put_widget (true, mainwin_sposition, 226, 4);
1128     mainwin_sposition->on_move (mainwin_spos_motion_cb);
1129     mainwin_sposition->on_release (mainwin_spos_release_cb);
1130 
1131     mainwin_stime_min = new TextBox (15, nullptr, false);
1132     mainwin->put_widget (true, mainwin_stime_min, 130, 4);
1133     mainwin_stime_min->on_press (change_timer_mode_cb);
1134 
1135     mainwin_stime_sec = new TextBox (10, nullptr, false);
1136     mainwin->put_widget (true, mainwin_stime_sec, 147, 4);
1137     mainwin_stime_sec->on_press (change_timer_mode_cb);
1138 }
1139 
state_cb(GtkWidget * widget,GdkEventWindowState * event,void *)1140 static gboolean state_cb (GtkWidget * widget, GdkEventWindowState * event, void *)
1141 {
1142     if (event->changed_mask & GDK_WINDOW_STATE_STICKY)
1143         view_set_sticky (!! (event->new_window_state & GDK_WINDOW_STATE_STICKY));
1144 
1145     if (event->changed_mask & GDK_WINDOW_STATE_ABOVE)
1146         view_set_on_top (!! (event->new_window_state & GDK_WINDOW_STATE_ABOVE));
1147 
1148     return true;
1149 }
1150 
draw(cairo_t * cr)1151 void MainWindow::draw (cairo_t * cr)
1152 {
1153     int width = is_shaded () ? MAINWIN_SHADED_WIDTH : skin.hints.mainwin_width;
1154     int height = is_shaded () ? MAINWIN_SHADED_HEIGHT : skin.hints.mainwin_height;
1155 
1156     skin_draw_pixbuf (cr, SKIN_MAIN, 0, 0, 0, 0, width, height);
1157     skin_draw_mainwin_titlebar (cr, is_shaded (), true);
1158 }
1159 
mainwin_create_window()1160 static void mainwin_create_window ()
1161 {
1162     bool shaded = aud_get_bool ("skins", "player_shaded");
1163 
1164     mainwin = new MainWindow (shaded);
1165 
1166     GtkWidget * w = mainwin->gtk ();
1167     drag_dest_set (w);
1168 
1169     g_signal_connect (w, "drag-data-received", (GCallback) mainwin_drag_data_received, nullptr);
1170     g_signal_connect (w, "window-state-event", (GCallback) state_cb, nullptr);
1171 
1172     hook_associate ("playback begin", (HookFunction) mainwin_playback_begin, nullptr);
1173     hook_associate ("playback ready", (HookFunction) mainwin_playback_begin, nullptr);
1174     hook_associate ("playback seek", (HookFunction) mainwin_update_song_info, nullptr);
1175     hook_associate ("playback stop", (HookFunction) mainwin_playback_stop, nullptr);
1176     hook_associate ("playback pause", (HookFunction) playback_pause, nullptr);
1177     hook_associate ("playback unpause", (HookFunction) playback_unpause, nullptr);
1178     hook_associate ("title change", (HookFunction) title_change, nullptr);
1179     hook_associate ("info change", (HookFunction) info_change, nullptr);
1180     hook_associate ("set record", (HookFunction) record_toggled, nullptr);
1181     hook_associate ("set repeat", (HookFunction) repeat_toggled, nullptr);
1182     hook_associate ("set shuffle", (HookFunction) shuffle_toggled, nullptr);
1183     hook_associate ("set no_playlist_advance", (HookFunction) no_advance_toggled, nullptr);
1184     hook_associate ("set stop_after_current_song", (HookFunction) stop_after_song_toggled, nullptr);
1185 }
1186 
mainwin_unhook()1187 void mainwin_unhook ()
1188 {
1189     seeking = false;
1190     timer_remove (TimerRate::Hz10, seek_timeout);
1191 
1192     status_message_timeout.stop ();
1193     mainwin_volume_release_timeout.stop ();
1194 
1195     hook_dissociate ("playback begin", (HookFunction) mainwin_playback_begin);
1196     hook_dissociate ("playback ready", (HookFunction) mainwin_playback_begin);
1197     hook_dissociate ("playback seek", (HookFunction) mainwin_update_song_info);
1198     hook_dissociate ("playback stop", (HookFunction) mainwin_playback_stop);
1199     hook_dissociate ("playback pause", (HookFunction) playback_pause);
1200     hook_dissociate ("playback unpause", (HookFunction) playback_unpause);
1201     hook_dissociate ("title change", (HookFunction) title_change);
1202     hook_dissociate ("info change", (HookFunction) info_change);
1203     hook_dissociate ("set record", (HookFunction) record_toggled);
1204     hook_dissociate ("set repeat", (HookFunction) repeat_toggled);
1205     hook_dissociate ("set shuffle", (HookFunction) shuffle_toggled);
1206     hook_dissociate ("set no_playlist_advance", (HookFunction) no_advance_toggled);
1207     hook_dissociate ("set stop_after_current_song", (HookFunction) stop_after_song_toggled);
1208 
1209     start_stop_visual (true);
1210 
1211     locked_textbox = nullptr;
1212     locked_old_text = String ();
1213 }
1214 
mainwin_create()1215 void mainwin_create ()
1216 {
1217     mainwin_create_window ();
1218     mainwin_create_widgets ();
1219     mainwin_set_song_title (nullptr);
1220 }
1221 
mainwin_update_volume()1222 static void mainwin_update_volume ()
1223 {
1224     int volume = aud_drct_get_volume_main ();
1225     int balance = aud_drct_get_volume_balance ();
1226 
1227     mainwin_set_volume_slider (volume);
1228     mainwin_set_balance_slider (balance);
1229     equalizerwin_set_volume_slider (volume);
1230     equalizerwin_set_balance_slider (balance);
1231 }
1232 
mainwin_update_time_display(int time,int length)1233 static void mainwin_update_time_display (int time, int length)
1234 {
1235     StringBuf scratch = format_time (time, length);
1236 
1237     mainwin_minus_num->set (scratch[0]);
1238     mainwin_10min_num->set (scratch[1]);
1239     mainwin_min_num->set (scratch[2]);
1240     mainwin_10sec_num->set (scratch[4]);
1241     mainwin_sec_num->set (scratch[5]);
1242 
1243     if (! mainwin_sposition->get_pressed ())
1244     {
1245         mainwin_stime_min->set_text (scratch);
1246         mainwin_stime_sec->set_text (scratch + 4);
1247     }
1248 
1249     playlistwin_set_time (scratch, scratch + 4);
1250 }
1251 
mainwin_update_time_slider(int time,int length)1252 static void mainwin_update_time_slider (int time, int length)
1253 {
1254     mainwin_position->setVisible (length > 0);
1255     mainwin_sposition->setVisible (length > 0);
1256 
1257     if (length > 0 && ! seeking)
1258     {
1259         if (time < length)
1260         {
1261             mainwin_position->set_pos (time * (int64_t) 219 / length);
1262             mainwin_sposition->set_pos (1 + time * (int64_t) 12 / length);
1263         }
1264         else
1265         {
1266             mainwin_position->set_pos (219);
1267             mainwin_sposition->set_pos (13);
1268         }
1269 
1270         mainwin_spos_set_knob ();
1271     }
1272 }
1273 
mainwin_update_song_info()1274 void mainwin_update_song_info ()
1275 {
1276     mainwin_update_volume ();
1277 
1278     if (! aud_drct_get_playing ())
1279         return;
1280 
1281     int time = 0, length = 0;
1282     if (aud_drct_get_ready ())
1283     {
1284         time = aud_drct_get_time ();
1285         length = aud_drct_get_length ();
1286     }
1287 
1288     mainwin_update_time_display (time, length);
1289     mainwin_update_time_slider (time, length);
1290 }
1291