Project

General

Profile

main.cc

Proposed skins/main.cc - Jim Turner, October 30, 2016 01:11

 
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
#include <libaudcore/playlist.h>
43

    
44
#include "actions-mainwin.h"
45
#include "actions-playlist.h"
46
#include "dnd.h"
47
#include "menus.h"
48
#include "plugin.h"
49
#include "skins_cfg.h"
50
#include "equalizer.h"
51
#include "main.h"
52
#include "vis-callbacks.h"
53
#include "playlist.h"
54
#include "button.h"
55
#include "hslider.h"
56
#include "menurow.h"
57
#include "monostereo.h"
58
#include "number.h"
59
#include "playlist-widget.h"
60
#include "playstatus.h"
61
#include "textbox.h"
62
#include "window.h"
63
#include "vis.h"
64
#include "util.h"
65
#include "view.h"
66

    
67
#include "../ui-common/menu-ops.h"
68

    
69
#define SEEK_THRESHOLD 200 /* milliseconds */
70
#define SEEK_SPEED 50 /* milliseconds per pixel */
71

    
72
class MainWindow : public Window
73
{
74
public:
75
    MainWindow (bool shaded) :
76
        Window (WINDOW_MAIN, & config.player_x, & config.player_y,
77
         shaded ? MAINWIN_SHADED_WIDTH : skin.hints.mainwin_width,
78
         shaded ? MAINWIN_SHADED_HEIGHT : skin.hints.mainwin_height, shaded) {}
79

    
80
private:
81
    void draw (cairo_t * cr);
82
    bool button_press (GdkEventButton * event);
83
    bool scroll (GdkEventScroll * event);
84
    bool motion (GdkEventMotion * event);  // JWT:ADDED NEXT 5 FOR POPUP SONG INFO:
85
    bool leave ();
86
    int m_playlist = -1;
87
    int m_popup_pos = -1;
88
    QueuedFunc m_popup_timer;
89
};
90

    
91
Window * mainwin;
92

    
93
Button * mainwin_eq, * mainwin_pl;
94
TextBox * mainwin_info;
95
MenuRow * mainwin_menurow;
96

    
97
SkinnedVis * mainwin_vis;
98
SmallVis * mainwin_svis;
99

    
100
static bool infopopup_on = false;
101
static bool seeking = false;
102
static int seek_start, seek_time;
103

    
104
static TextBox * locked_textbox = nullptr;
105
static String locked_old_text;
106

    
107
static QueuedFunc status_message_timeout;
108
static QueuedFunc mainwin_volume_release_timeout;
109

    
110
static Button * mainwin_menubtn, * mainwin_minimize, * mainwin_shade, * mainwin_close;
111
static Button * mainwin_shaded_menubtn, * mainwin_shaded_minimize, * mainwin_shaded_shade, * mainwin_shaded_close;
112

    
113
static Button * mainwin_rew, * mainwin_fwd;
114
static Button * mainwin_eject;
115
static Button * mainwin_play, * mainwin_pause, * mainwin_stop;
116
static Button * mainwin_shuffle, * mainwin_repeat;
117

    
118
static TextBox * mainwin_stime_min, * mainwin_stime_sec;
119
static TextBox * mainwin_rate_text, * mainwin_freq_text, * mainwin_othertext;
120

    
121
static PlayStatus * mainwin_playstatus;
122
static SkinnedNumber * mainwin_minus_num, * mainwin_10min_num, * mainwin_min_num;
123
static SkinnedNumber * mainwin_10sec_num, * mainwin_sec_num;
124
static HSlider * mainwin_position, * mainwin_sposition;
125

    
126
static HSlider * mainwin_volume, * mainwin_balance;
127
static MonoStereo * mainwin_monostereo;
128

    
129
static Button * mainwin_srew, * mainwin_splay, * mainwin_spause;
130
static Button * mainwin_sstop, * mainwin_sfwd, * mainwin_seject, * mainwin_about;
131

    
132
static void mainwin_position_motion_cb ();
133
static void mainwin_position_release_cb ();
134
static void seek_timeout (void * rewind);
135

    
136
static void format_time (char buf[7], int time, int length)
137
{
138
    bool zero = aud_get_bool (nullptr, "leading_zero");
139
    bool remaining = aud_get_bool ("skins", "show_remaining_time");
140

    
141
    if (remaining && length > 0)
142
    {
143
        time = (length - time) / 1000;
144

    
145
        if (time < 60)
146
            snprintf (buf, 7, zero ? "-00:%02d" : " -0:%02d", time);
147
        else if (time < 6000)
148
            snprintf (buf, 7, zero ? "%03d:%02d" : "%3d:%02d", -time / 60, time % 60);
149
        else
150
            snprintf (buf, 7, "%3d:%02d", -time / 3600, time / 60 % 60);
151
    }
152
    else
153
    {
154
        time /= 1000;
155

    
156
        if (time < 6000)
157
            snprintf (buf, 7, zero ? " %02d:%02d" : " %2d:%02d", time / 60, time % 60);
158
        else if (time < 60000)
159
            snprintf (buf, 7, "%3d:%02d", time / 60, time % 60);
160
        else
161
            snprintf (buf, 7, "%3d:%02d", time / 3600, time / 60 % 60);
162
    }
163
}
164

    
165
static void mainwin_menubtn_cb ()
166
{
167
    int x, y;
168
    mainwin->getPosition (& x, & y);
169
    menu_popup (UI_MENU_MAIN, x + 6 * config.scale,
170
     y + MAINWIN_SHADED_HEIGHT * config.scale, false, false, 1, GDK_CURRENT_TIME);
171
}
172

    
173
static void mainwin_minimize_cb ()
174
{
175
    gtk_window_iconify ((GtkWindow *) mainwin->gtk ());
176
}
177

    
178
static void mainwin_shade_toggle ()
179
{
180
    view_set_player_shaded (! aud_get_bool ("skins", "player_shaded"));
181
}
182

    
183
static void mainwin_lock_info_text (const char * text)
184
{
185
    if (! locked_textbox)
186
    {
187
        locked_textbox = skin.hints.mainwin_othertext_is_status ? mainwin_othertext : mainwin_info;
188
        locked_old_text = locked_textbox->get_text ();
189
    }
190

    
191
    locked_textbox->set_text (text);
192
}
193

    
194
static void mainwin_release_info_text (void * = nullptr)
195
{
196
    if (locked_textbox)
197
    {
198
        locked_textbox->set_text (locked_old_text);
199
        locked_textbox = nullptr;
200
        locked_old_text = String ();
201
    }
202
}
203

    
204
static void set_info_text (TextBox * textbox, const char * text)
205
{
206
    if (textbox == locked_textbox)
207
        locked_old_text = String (text);
208
    else
209
        textbox->set_text (text);
210
}
211

    
212
#define mainwin_set_info_text(t) set_info_text (mainwin_info, (t))
213
#define mainwin_set_othertext(t) set_info_text (mainwin_othertext, (t))
214

    
215
void mainwin_show_status_message (const char * message)
216
{
217
    mainwin_lock_info_text (message);
218
    status_message_timeout.queue (1000, mainwin_release_info_text, nullptr);
219
}
220

    
221
static void mainwin_set_song_title (const char * title)
222
{
223
    StringBuf buf;
224

    
225
    if (title)
226
        buf.steal (str_printf (_("%s - Audacious"), title));
227
    else
228
        buf.steal (str_copy (_("Audacious")));
229

    
230
    int instance = aud_get_instance ();
231
    if (instance != 1)
232
        buf.combine (str_printf (" (%d)", instance));
233

    
234
    mainwin->setWindowTitle ((const char *) buf);
235
    mainwin_set_info_text (title ? title : "");
236
}
237

    
238
static void title_change ()
239
{
240
    if (aud_drct_get_ready ())
241
        mainwin_set_song_title (aud_drct_get_title ());
242
    else
243
        mainwin_set_song_title ("Buffering ...");
244
}
245

    
246
static void setup_widget (Widget * widget, int x, int y, bool show)
247
{
248
    int width, height;
249

    
250
    /* use get_size_request(), not get_preferred_size() */
251
    /* get_preferred_size() will return 0x0 for hidden widgets */
252
    gtk_widget_get_size_request (widget->gtk (), & width, & height);
253

    
254
    width /= config.scale;
255
    height /= config.scale;
256

    
257
    /* hide widgets that are outside the window boundary */
258
    if (x < 0 || x + width > skin.hints.mainwin_width ||
259
     y < 0 || y + height > skin.hints.mainwin_height)
260
        show = false;
261

    
262
    widget->setVisible (show);
263
    mainwin->move_widget (false, widget, x, y);
264
}
265

    
266
void mainwin_refresh_hints ()
267
{
268
    const SkinHints * p = & skin.hints;
269

    
270
    mainwin_menurow->setVisible (p->mainwin_menurow_visible);
271
    mainwin_rate_text->setVisible (p->mainwin_streaminfo_visible);
272
    mainwin_freq_text->setVisible (p->mainwin_streaminfo_visible);
273
    mainwin_monostereo->setVisible (p->mainwin_streaminfo_visible);
274

    
275
    mainwin_info->set_width (p->mainwin_text_width);
276

    
277
    setup_widget (mainwin_vis, p->mainwin_vis_x, p->mainwin_vis_y, p->mainwin_vis_visible);
278
    setup_widget (mainwin_info, p->mainwin_text_x, p->mainwin_text_y, p->mainwin_text_visible);
279
    setup_widget (mainwin_othertext, p->mainwin_infobar_x, p->mainwin_infobar_y, p->mainwin_othertext_visible);
280

    
281
    bool playing = aud_drct_get_playing ();
282
    bool can_seek = aud_drct_get_length () > 0;
283

    
284
    setup_widget (mainwin_minus_num, p->mainwin_number_0_x, p->mainwin_number_0_y, playing);
285
    setup_widget (mainwin_10min_num, p->mainwin_number_1_x, p->mainwin_number_1_y, playing);
286
    setup_widget (mainwin_min_num, p->mainwin_number_2_x, p->mainwin_number_2_y, playing);
287
    setup_widget (mainwin_10sec_num, p->mainwin_number_3_x, p->mainwin_number_3_y, playing);
288
    setup_widget (mainwin_sec_num, p->mainwin_number_4_x, p->mainwin_number_4_y, playing);
289
    setup_widget (mainwin_position, p->mainwin_position_x, p->mainwin_position_y, can_seek);
290

    
291
    setup_widget (mainwin_playstatus, p->mainwin_playstatus_x, p->mainwin_playstatus_y, true);
292
    setup_widget (mainwin_volume, p->mainwin_volume_x, p->mainwin_volume_y, true);
293
    setup_widget (mainwin_balance, p->mainwin_balance_x, p->mainwin_balance_y, true);
294
    setup_widget (mainwin_rew, p->mainwin_previous_x, p->mainwin_previous_y, true);
295
    setup_widget (mainwin_play, p->mainwin_play_x, p->mainwin_play_y, true);
296
    setup_widget (mainwin_pause, p->mainwin_pause_x, p->mainwin_pause_y, true);
297
    setup_widget (mainwin_stop, p->mainwin_stop_x, p->mainwin_stop_y, true);
298
    setup_widget (mainwin_fwd, p->mainwin_next_x, p->mainwin_next_y, true);
299
    setup_widget (mainwin_eject, p->mainwin_eject_x, p->mainwin_eject_y, true);
300
    setup_widget (mainwin_eq, p->mainwin_eqbutton_x, p->mainwin_eqbutton_y, true);
301
    setup_widget (mainwin_pl, p->mainwin_plbutton_x, p->mainwin_plbutton_y, true);
302
    setup_widget (mainwin_shuffle, p->mainwin_shuffle_x, p->mainwin_shuffle_y, true);
303
    setup_widget (mainwin_repeat, p->mainwin_repeat_x, p->mainwin_repeat_y, true);
304
    setup_widget (mainwin_about, p->mainwin_about_x, p->mainwin_about_y, true);
305
    setup_widget (mainwin_minimize, p->mainwin_minimize_x, p->mainwin_minimize_y, true);
306
    setup_widget (mainwin_shade, p->mainwin_shade_x, p->mainwin_shade_y, true);
307
    setup_widget (mainwin_close, p->mainwin_close_x, p->mainwin_close_y, true);
308

    
309
    if (aud_get_bool ("skins", "player_shaded"))
310
        mainwin->resize (MAINWIN_SHADED_WIDTH, MAINWIN_SHADED_HEIGHT);
311
    else
312
        mainwin->resize (p->mainwin_width, p->mainwin_height);
313

    
314
    mainwin_vis->set_colors ();
315
}
316

    
317
/* note that the song info is not translated since it is displayed using
318
 * the skinned bitmap font, which supports only the English alphabet */
319
static void mainwin_set_song_info (int bitrate, int samplerate, int channels)
320
{
321
    char scratch[32];
322
    int length;
323

    
324
    if (bitrate > 0)
325
    {
326
        if (bitrate < 1000000)
327
            snprintf (scratch, sizeof scratch, "%3d", bitrate / 1000);
328
        else
329
            snprintf (scratch, sizeof scratch, "%2dH", bitrate / 100000);
330

    
331
        mainwin_rate_text->set_text (scratch);
332
    }
333
    else
334
        mainwin_rate_text->set_text (nullptr);
335

    
336
    if (samplerate > 0)
337
    {
338
        snprintf (scratch, sizeof scratch, "%2d", samplerate / 1000);
339
        mainwin_freq_text->set_text (scratch);
340
    }
341
    else
342
        mainwin_freq_text->set_text (nullptr);
343

    
344
    mainwin_monostereo->set_num_channels (channels);
345

    
346
    if (bitrate > 0)
347
        snprintf (scratch, sizeof scratch, "%d kbps", bitrate / 1000);
348
    else
349
        scratch[0] = 0;
350

    
351
    if (samplerate > 0)
352
    {
353
        length = strlen (scratch);
354
        snprintf (scratch + length, sizeof scratch - length, "%s%d kHz", length ?
355
         ", " : "", samplerate / 1000);
356
    }
357

    
358
    if (channels > 0)
359
    {
360
        length = strlen (scratch);
361
        snprintf (scratch + length, sizeof scratch - length, "%s%s", length ?
362
         ", " : "", channels > 2 ? "surround" : channels > 1 ? "stereo" : "mono");
363
    }
364

    
365
    mainwin_set_othertext (scratch);
366
}
367

    
368
static void info_change ()
369
{
370
    int bitrate, samplerate, channels;
371
    aud_drct_get_info (bitrate, samplerate, channels);
372
    mainwin_set_song_info (bitrate, samplerate, channels);
373
}
374

    
375
static void playback_pause ()
376
{
377
    mainwin_playstatus->set_status (STATUS_PAUSE);
378
}
379

    
380
static void playback_unpause ()
381
{
382
    mainwin_playstatus->set_status (STATUS_PLAY);
383
}
384

    
385
void mainwin_playback_begin ()
386
{
387
    mainwin_update_song_info ();
388

    
389
    mainwin_stime_min->show ();
390
    mainwin_stime_sec->show ();
391
    mainwin_minus_num->show ();
392
    mainwin_10min_num->show ();
393
    mainwin_min_num->show ();
394
    mainwin_10sec_num->show ();
395
    mainwin_sec_num->show ();
396

    
397
    if (aud_drct_get_length () > 0)
398
    {
399
        mainwin_position->show ();
400
        mainwin_sposition->show ();
401
    }
402

    
403
    if (aud_drct_get_paused ())
404
        playback_pause ();
405
    else
406
        playback_unpause ();
407

    
408
    title_change ();
409
    info_change ();
410
}
411

    
412
static void mainwin_playback_stop ()
413
{
414
    seeking = false;
415
    timer_remove (TimerRate::Hz10, seek_timeout);
416

    
417
    mainwin_set_song_title (nullptr);
418

    
419
    mainwin_vis->clear ();
420
    mainwin_svis->clear ();
421

    
422
    mainwin_minus_num->hide ();
423
    mainwin_10min_num->hide ();
424
    mainwin_min_num->hide ();
425
    mainwin_10sec_num->hide ();
426
    mainwin_sec_num->hide ();
427
    mainwin_stime_min->hide ();
428
    mainwin_stime_sec->hide ();
429
    mainwin_position->hide ();
430
    mainwin_sposition->hide ();
431

    
432
    mainwin_position->set_pressed (false);
433
    mainwin_sposition->set_pressed (false);
434

    
435
    /* clear sampling parameter displays */
436
    mainwin_rate_text->set_text (nullptr);
437
    mainwin_freq_text->set_text (nullptr);
438
    mainwin_monostereo->set_num_channels (0);
439
    mainwin_set_othertext ("");
440

    
441
    mainwin_playstatus->set_status (STATUS_STOP);
442

    
443
    playlistwin_hide_timer();
444
}
445

    
446
static void repeat_toggled ()
447
{
448
    mainwin_repeat->set_active (aud_get_bool (nullptr, "repeat"));
449
}
450

    
451
static void shuffle_toggled ()
452
{
453
    mainwin_shuffle->set_active (aud_get_bool (nullptr, "shuffle"));
454
}
455

    
456
static void no_advance_toggled ()
457
{
458
    if (aud_get_bool (nullptr, "no_playlist_advance"))
459
        mainwin_show_status_message (_("Single mode."));
460
    else
461
        mainwin_show_status_message (_("Playlist mode."));
462
}
463

    
464
static void stop_after_song_toggled ()
465
{
466
    if (aud_get_bool (nullptr, "stop_after_current_song"))
467
        mainwin_show_status_message (_("Stopping after song."));
468
}
469

    
470
bool MainWindow::scroll (GdkEventScroll * event)
471
{
472
    switch (event->direction)
473
    {
474
        case GDK_SCROLL_UP:
475
            mainwin_set_volume_diff (5);
476
            break;
477
        case GDK_SCROLL_DOWN:
478
            mainwin_set_volume_diff (-5);
479
            break;
480
        case GDK_SCROLL_LEFT:
481
            aud_drct_seek (aud_drct_get_time () - 5000);
482
            break;
483
        case GDK_SCROLL_RIGHT:
484
            aud_drct_seek (aud_drct_get_time () + 5000);
485
            break;
486
        default:
487
            break;
488
    }
489

    
490
    return true;
491
}
492

    
493
bool MainWindow::button_press (GdkEventButton * event)
494
{
495
    if (event->button == 1 && event->type == GDK_2BUTTON_PRESS &&
496
     event->window == gtk_widget_get_window (gtk ()) &&
497
     event->y < 14 * config.scale)
498
    {
499
        mainwin_shade_toggle ();
500
        return true;
501
    }
502

    
503
    if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
504
    {
505
        menu_popup (UI_MENU_MAIN, event->x_root, event->y_root, false, false,
506
         event->button, event->time);
507
        return true;
508
    }
509

    
510
    return Window::button_press (event);
511
}
512

    
513
/* JWT:ADDED NEXT TWO FOR POPUP SONG INFO: */
514
bool MainWindow::motion (GdkEventMotion * event)
515
{
516
    int mousex = event->x;
517
    if (is_shaded ())
518
    {
519
        if (mousex > 62 && mousex < 164)
520
        {
521
            if (aud_get_bool (nullptr, "show_filepopup_for_tuple"))
522
            {
523
                m_playlist = aud_playlist_get_active ();
524
                m_popup_pos = aud_playlist_get_position (m_playlist);
525
                audgui_infopopup_hide ();
526

    
527
                if (m_popup_pos >= 0)
528
                {
529
                    auto show_cb = [] (void * me_) {
530
                        auto me = (MainWindow *) me_;
531
                        audgui_infopopup_show (me->m_playlist, me->m_popup_pos);
532
                    };
533
                    m_popup_timer.queue (aud_get_int (nullptr, "filepopup_delay") * 100, show_cb, this);
534
                    infopopup_on = true;
535
                }
536
            }
537
        }
538
        else if (infopopup_on)
539
        {
540
            m_popup_pos = -1;
541
            m_popup_timer.stop ();
542
            audgui_infopopup_hide ();
543
            infopopup_on = false;
544
        }
545
    }
546
    return true;
547
}
548

    
549
bool MainWindow::leave ()
550
{
551
    if (infopopup_on)
552
    {
553
        m_popup_pos = -1;
554
        m_popup_timer.stop ();
555
        audgui_infopopup_hide ();
556
        infopopup_on = false;
557
    }
558

    
559
    return true;
560
}
561

    
562
static void mainwin_playback_rpress (Button * button, GdkEventButton * event)
563
{
564
    menu_popup (UI_MENU_PLAYBACK, event->x_root, event->y_root, false, false,
565
     event->button, event->time);
566
}
567

    
568
bool Window::keypress (GdkEventKey * event)
569
{
570
    if (playlistwin_list->handle_keypress (event))
571
        return true;
572

    
573
    switch (event->keyval)
574
    {
575
        case GDK_KEY_Left:
576
        case GDK_KEY_KP_Left:
577
        case GDK_KEY_KP_7:
578
            aud_drct_seek (aud_drct_get_time () - 5000);
579
            break;
580
        case GDK_KEY_Right:
581
        case GDK_KEY_KP_Right:
582
        case GDK_KEY_KP_9:
583
            aud_drct_seek (aud_drct_get_time () + 5000);
584
            break;
585
        case GDK_KEY_KP_4:
586
            aud_drct_pl_prev ();
587
            break;
588
        case GDK_KEY_KP_6:
589
            aud_drct_pl_next ();
590
            break;
591
        case GDK_KEY_KP_Insert:
592
            audgui_jump_to_track ();
593
            break;
594
        case GDK_KEY_space:
595
            aud_drct_pause ();
596
            break;
597
        case GDK_KEY_Tab: /* GtkUIManager does not handle tab, apparently. */
598
            if (event->state & GDK_SHIFT_MASK)
599
                pl_prev ();
600
            else
601
                pl_next ();
602

    
603
            break;
604
        case GDK_KEY_ISO_Left_Tab:
605
            pl_prev ();
606
            break;
607
        default:
608
            return false;
609
    }
610

    
611
    return true;
612
}
613

    
614
void mainwin_drag_data_received (GtkWidget * widget, GdkDragContext * context,
615
 int x, int y, GtkSelectionData * selection_data, unsigned info, unsigned time, void *)
616
{
617
    g_return_if_fail (selection_data != nullptr);
618

    
619
    const char * data = (const char *) gtk_selection_data_get_data (selection_data);
620
    g_return_if_fail (data);
621

    
622
    if (str_has_prefix_nocase (data, "file:///"))
623
    {
624
        if (str_has_suffix_nocase (data, ".wsz\r\n") || str_has_suffix_nocase
625
         (data, ".zip\r\n"))
626
        {
627
            on_skin_view_drag_data_received (0, context, x, y, selection_data, info, time, 0);
628
            return;
629
        }
630
    }
631

    
632
    audgui_urilist_open (data);
633
}
634

    
635
static int time_now ()
636
{
637
    struct timeval tv;
638
    gettimeofday (& tv, nullptr);
639
    return (tv.tv_sec % (24 * 3600) * 1000 + tv.tv_usec / 1000);
640
}
641

    
642
static int time_diff (int a, int b)
643
{
644
    if (a > 18 * 3600 * 1000 && b < 6 * 3600 * 1000) /* detect midnight */
645
        b += 24 * 3600 * 1000;
646
    return (b > a) ? b - a : 0;
647
}
648

    
649
static void seek_timeout (void * rewind)
650
{
651
    int held = time_diff (seek_time, time_now ());
652
    if (held < SEEK_THRESHOLD)
653
        return;
654

    
655
    int position;
656
    if (aud::from_ptr<bool> (rewind))
657
        position = seek_start - held / SEEK_SPEED;
658
    else
659
        position = seek_start + held / SEEK_SPEED;
660

    
661
    position = aud::clamp (position, 0, 219);
662
    mainwin_position->set_pos (position);
663
    mainwin_position_motion_cb ();
664
}
665

    
666
static void seek_press (GdkEventButton * event, bool rewind)
667
{
668
    if (event->button != 1 || seeking)
669
        return;
670

    
671
    seeking = true;
672
    seek_start = mainwin_position->get_pos ();
673
    seek_time = time_now ();
674
    timer_add (TimerRate::Hz10, seek_timeout, aud::to_ptr (rewind));
675
}
676

    
677
static void seek_release (GdkEventButton * event, bool rewind)
678
{
679
    if (event->button != 1 || ! seeking)
680
        return;
681

    
682
    if (! aud_drct_get_playing () || time_diff (seek_time, time_now ()) <
683
     SEEK_THRESHOLD)
684
    {
685
        if (rewind)
686
            aud_drct_pl_prev ();
687
        else
688
            aud_drct_pl_next ();
689
    }
690
    else
691
        mainwin_position_release_cb ();
692

    
693
    seeking = false;
694
    timer_remove (TimerRate::Hz10, seek_timeout);
695
}
696

    
697
static void mainwin_rew_press (Button * button, GdkEventButton * event)
698
    { seek_press (event, true); }
699
static void mainwin_rew_release (Button * button, GdkEventButton * event)
700
    { seek_release (event, true); }
701
static void mainwin_fwd_press (Button * button, GdkEventButton * event)
702
    { seek_press (event, false); }
703
static void mainwin_fwd_release (Button * button, GdkEventButton * event)
704
    { seek_release (event, false); }
705

    
706
static void mainwin_shuffle_cb (Button * button, GdkEventButton * event)
707
    { aud_set_bool (nullptr, "shuffle", button->get_active ()); }
708
static void mainwin_repeat_cb (Button * button, GdkEventButton * event)
709
    { aud_set_bool (nullptr, "repeat", button->get_active ()); }
710
static void mainwin_eq_cb (Button * button, GdkEventButton * event)
711
    { view_set_show_equalizer (button->get_active ()); }
712
static void mainwin_pl_cb (Button * button, GdkEventButton * event)
713
    { view_set_show_playlist (button->get_active ()); }
714

    
715
static void mainwin_spos_set_knob ()
716
{
717
    int pos = mainwin_sposition->get_pos ();
718
    int x = (pos < 6) ? 17 : (pos < 9) ? 20 : 23;
719
    mainwin_sposition->set_knob (x, 36, x, 36);
720
}
721

    
722
static void mainwin_spos_motion_cb ()
723
{
724
    mainwin_spos_set_knob ();
725

    
726
    int pos = mainwin_sposition->get_pos ();
727
    int length = aud_drct_get_length ();
728
    int time = (pos - 1) * length / 12;
729

    
730
    char buf[7];
731
    format_time (buf, time, length);
732

    
733
    mainwin_stime_min->set_text (buf);
734
    mainwin_stime_sec->set_text (buf + 4);
735
}
736

    
737
static void mainwin_spos_release_cb ()
738
{
739
    mainwin_spos_set_knob ();
740

    
741
    int pos = mainwin_sposition->get_pos ();
742
    aud_drct_seek (aud_drct_get_length () * (pos - 1) / 12);
743
}
744

    
745
static void mainwin_position_motion_cb ()
746
{
747
    int length = aud_drct_get_length () / 1000;
748
    int pos = mainwin_position->get_pos ();
749
    int time = pos * length / 219;
750

    
751
    mainwin_lock_info_text (str_printf (_("Seek to %d:%-2.2d / %d:%-2.2d"),
752
     time / 60, time % 60, length / 60, length % 60));
753
}
754

    
755
static void mainwin_position_release_cb ()
756
{
757
    int length = aud_drct_get_length ();
758
    int pos = mainwin_position->get_pos ();
759
    int time = (int64_t) pos * length / 219;
760

    
761
    aud_drct_seek(time);
762
    mainwin_release_info_text();
763
}
764

    
765
void mainwin_adjust_volume_motion (int v)
766
{
767
    aud_drct_set_volume_main (v);
768
    mainwin_lock_info_text (str_printf (_("Volume: %d%%"), v));
769
}
770

    
771
void mainwin_adjust_volume_release ()
772
{
773
    mainwin_release_info_text ();
774
}
775

    
776
void mainwin_adjust_balance_motion (int b)
777
{
778
    aud_drct_set_volume_balance (b);
779

    
780
    if (b < 0)
781
        mainwin_lock_info_text (str_printf (_("Balance: %d%% left"), -b));
782
    else if (b == 0)
783
        mainwin_lock_info_text (_("Balance: center"));
784
    else
785
        mainwin_lock_info_text (str_printf (_("Balance: %d%% right"), b));
786
}
787

    
788
void mainwin_adjust_balance_release ()
789
{
790
    mainwin_release_info_text ();
791
}
792

    
793
static void mainwin_volume_set_frame ()
794
{
795
    int pos = mainwin_volume->get_pos ();
796
    int frame = (pos * 27 + 25) / 51;
797
    mainwin_volume->set_frame (0, 15 * frame);
798
}
799

    
800
void mainwin_set_volume_slider (int percent)
801
{
802
    mainwin_volume->set_pos ((percent * 51 + 50) / 100);
803
    mainwin_volume_set_frame ();
804
}
805

    
806
static void mainwin_volume_motion_cb ()
807
{
808
    mainwin_volume_set_frame ();
809
    int pos = mainwin_volume->get_pos ();
810
    int vol = (pos * 100 + 25) / 51;
811

    
812
    mainwin_adjust_volume_motion (vol);
813
    equalizerwin_set_volume_slider (vol);
814
}
815

    
816
static void mainwin_volume_release_cb ()
817
{
818
    mainwin_volume_set_frame ();
819
    mainwin_adjust_volume_release ();
820
}
821

    
822
static void mainwin_balance_set_frame ()
823
{
824
    int pos = mainwin_balance->get_pos ();
825
    int frame = (abs (pos - 12) * 27 + 6) / 12;
826
    mainwin_balance->set_frame (9, 15 * frame);
827
}
828

    
829
void mainwin_set_balance_slider (int percent)
830
{
831
    if (percent > 0)
832
        mainwin_balance->set_pos (12 + (percent * 12 + 50) / 100);
833
    else
834
        mainwin_balance->set_pos (12 + (percent * 12 - 50) / 100);
835

    
836
    mainwin_balance_set_frame ();
837
}
838

    
839
static void mainwin_balance_motion_cb ()
840
{
841
    mainwin_balance_set_frame ();
842
    int pos = mainwin_balance->get_pos ();
843

    
844
    int bal;
845
    if (pos > 12)
846
        bal = ((pos - 12) * 100 + 6) / 12;
847
    else
848
        bal = ((pos - 12) * 100 - 6) / 12;
849

    
850
    mainwin_adjust_balance_motion (bal);
851
    equalizerwin_set_balance_slider (bal);
852
}
853

    
854
static void mainwin_balance_release_cb ()
855
{
856
    mainwin_balance_set_frame ();
857
    mainwin_adjust_volume_release ();
858
}
859

    
860
void mainwin_set_volume_diff (int diff)
861
{
862
    int vol = aud_drct_get_volume_main ();
863

    
864
    vol = aud::clamp (vol + diff, 0, 100);
865
    mainwin_adjust_volume_motion (vol);
866
    mainwin_set_volume_slider (vol);
867
    equalizerwin_set_volume_slider (vol);
868

    
869
    mainwin_volume_release_timeout.queue (700,
870
     [] (void *) { mainwin_volume_release_cb (); }, nullptr);
871
}
872

    
873
void mainwin_mr_change (MenuRowItem i)
874
{
875
    switch (i)
876
    {
877
        case MENUROW_OPTIONS:
878
            mainwin_lock_info_text (_("Options Menu"));
879
            break;
880
        case MENUROW_ALWAYS:
881
            if (aud_get_bool ("skins", "always_on_top"))
882
                mainwin_lock_info_text (_("Disable 'Always On Top'"));
883
            else
884
                mainwin_lock_info_text (_("Enable 'Always On Top'"));
885
            break;
886
        case MENUROW_FILEINFOBOX:
887
            mainwin_lock_info_text (_("File Info Box"));
888
            break;
889
        case MENUROW_SCALE:
890
            mainwin_lock_info_text (_("Double Size"));
891
            break;
892
        case MENUROW_VISUALIZATION:
893
            mainwin_lock_info_text (_("Visualizations"));
894
            break;
895
        default:
896
            break;
897
    }
898
}
899

    
900
void mainwin_mr_release (MenuRowItem i, GdkEventButton * event)
901
{
902
    switch (i)
903
    {
904
        case MENUROW_OPTIONS:
905
            menu_popup (UI_MENU_VIEW, event->x_root, event->y_root, false, false, 1, event->time);
906
            break;
907
        case MENUROW_ALWAYS:
908
            view_set_on_top (! aud_get_bool ("skins", "always_on_top"));
909
            break;
910
        case MENUROW_FILEINFOBOX:
911
            audgui_infowin_show_current ();
912
            break;
913
        case MENUROW_SCALE:
914
            view_set_double_size (! aud_get_bool ("skins", "double_size"));
915
            break;
916
        case MENUROW_VISUALIZATION:
917
            audgui_show_prefs_for_plugin_type (PluginType::Vis);
918
            break;
919
        default:
920
            break;
921
    }
922

    
923
    mainwin_release_info_text();
924
}
925

    
926
bool change_timer_mode_cb (GdkEventButton * event)
927
{
928
    if (event->type != GDK_BUTTON_PRESS || event->button != 1)
929
        return false;
930

    
931
    view_set_show_remaining (! aud_get_bool ("skins", "show_remaining_time"));
932
    return true;
933
}
934

    
935
static bool mainwin_info_button_press (GdkEventButton * event)
936
{
937
    if (event->type == GDK_BUTTON_PRESS && event->button == 3)
938
    {
939
        menu_popup (UI_MENU_PLAYBACK, event->x_root, event->y_root, false,
940
         false, event->button, event->time);
941
        return true;
942
    }
943

    
944
    if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
945
    {
946
        audgui_infowin_show_current ();
947
        return true;
948
    }
949

    
950
    return false;
951
}
952

    
953
static void mainwin_create_widgets ()
954
{
955
    mainwin_menubtn = new Button (9, 9, 0, 0, 0, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
956
    mainwin->put_widget (false, mainwin_menubtn, 6, 3);
957
    mainwin_menubtn->on_release ((ButtonCB) mainwin_menubtn_cb);
958

    
959
    mainwin_minimize = new Button (9, 9, 9, 0, 9, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
960
    mainwin->put_widget (false, mainwin_minimize, 244, 3);
961
    mainwin_minimize->on_release ((ButtonCB) mainwin_minimize_cb);
962

    
963
    mainwin_shade = new Button (9, 9, 0, 18, 9, 18, SKIN_TITLEBAR, SKIN_TITLEBAR);
964
    mainwin->put_widget (false, mainwin_shade, 254, 3);
965
    mainwin_shade->on_release ((ButtonCB) mainwin_shade_toggle);
966

    
967
    mainwin_close = new Button (9, 9, 18, 0, 18, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
968
    mainwin->put_widget (false, mainwin_close, 264, 3);
969
    mainwin_close->on_release ((ButtonCB) skins_close);
970

    
971
    mainwin_rew = new Button (23, 18, 0, 0, 0, 18, SKIN_CBUTTONS, SKIN_CBUTTONS);
972
    mainwin->put_widget (false, mainwin_rew, 16, 88);
973
    mainwin_rew->on_press (mainwin_rew_press);
974
    mainwin_rew->on_release (mainwin_rew_release);
975
    mainwin_rew->on_rpress (mainwin_playback_rpress);
976

    
977
    mainwin_fwd = new Button (22, 18, 92, 0, 92, 18, SKIN_CBUTTONS, SKIN_CBUTTONS);
978
    mainwin->put_widget (false, mainwin_fwd, 108, 88);
979
    mainwin_fwd->on_press (mainwin_fwd_press);
980
    mainwin_fwd->on_release (mainwin_fwd_release);
981
    mainwin_fwd->on_rpress (mainwin_playback_rpress);
982

    
983
    mainwin_play = new Button (23, 18, 23, 0, 23, 18, SKIN_CBUTTONS, SKIN_CBUTTONS);
984
    mainwin->put_widget (false, mainwin_play, 39, 88);
985
    mainwin_play->on_release ((ButtonCB) aud_drct_play);
986
    mainwin_play->on_rpress (mainwin_playback_rpress);
987

    
988
    mainwin_pause = new Button (23, 18, 46, 0, 46, 18, SKIN_CBUTTONS, SKIN_CBUTTONS);
989
    mainwin->put_widget (false, mainwin_pause, 62, 88);
990
    mainwin_pause->on_release ((ButtonCB) aud_drct_pause);
991
    mainwin_pause->on_rpress (mainwin_playback_rpress);
992

    
993
    mainwin_stop = new Button (23, 18, 69, 0, 69, 18, SKIN_CBUTTONS, SKIN_CBUTTONS);
994
    mainwin->put_widget (false, mainwin_stop, 85, 88);
995
    mainwin_stop->on_release ((ButtonCB) aud_drct_stop);
996
    mainwin_stop->on_rpress (mainwin_playback_rpress);
997

    
998
    mainwin_eject = new Button (22, 16, 114, 0, 114, 16, SKIN_CBUTTONS, SKIN_CBUTTONS);
999
    mainwin->put_widget (false, mainwin_eject, 136, 89);
1000
    mainwin_eject->on_release ((ButtonCB) action_play_file);
1001

    
1002
    mainwin_shuffle = new Button (46, 15, 28, 0, 28, 15, 28, 30, 28, 45, SKIN_SHUFREP, SKIN_SHUFREP);
1003
    mainwin->put_widget (false, mainwin_shuffle, 164, 89);
1004
    mainwin_shuffle->set_active (aud_get_bool (nullptr, "shuffle"));
1005
    mainwin_shuffle->on_release (mainwin_shuffle_cb);
1006

    
1007
    mainwin_repeat = new Button (28, 15, 0, 0, 0, 15, 0, 30, 0, 45, SKIN_SHUFREP, SKIN_SHUFREP);
1008
    mainwin->put_widget (false, mainwin_repeat, 210, 89);
1009
    mainwin_repeat->set_active (aud_get_bool (nullptr, "repeat"));
1010
    mainwin_repeat->on_release (mainwin_repeat_cb);
1011

    
1012
    mainwin_eq = new Button (23, 12, 0, 61, 46, 61, 0, 73, 46, 73, SKIN_SHUFREP, SKIN_SHUFREP);
1013
    mainwin->put_widget (false, mainwin_eq, 219, 58);
1014
    mainwin_eq->on_release (mainwin_eq_cb);
1015

    
1016
    mainwin_pl = new Button (23, 12, 23, 61, 69, 61, 23, 73, 69, 73, SKIN_SHUFREP, SKIN_SHUFREP);
1017
    mainwin->put_widget (false, mainwin_pl, 242, 58);
1018
    mainwin_pl->on_release (mainwin_pl_cb);
1019

    
1020
    String font;
1021
    if (! config.mainwin_use_bitmapfont)
1022
        font = aud_get_str ("skins", "mainwin_font");
1023

    
1024
    bool shaded = aud_get_bool ("skins", "mainwin_shaded");
1025
    mainwin_info = new TextBox (153, font, ! shaded && config.autoscroll);
1026
    mainwin->put_widget (false, mainwin_info, 112, 27);
1027
    mainwin_info->on_press (mainwin_info_button_press);
1028

    
1029
    mainwin_othertext = new TextBox (153, nullptr, false);
1030
    mainwin->put_widget (false, mainwin_othertext, 112, 43);
1031

    
1032
    mainwin_rate_text = new TextBox (15, nullptr, false);
1033
    mainwin->put_widget (false, mainwin_rate_text, 111, 43);
1034

    
1035
    mainwin_freq_text = new TextBox (10, nullptr, false);
1036
    mainwin->put_widget (false, mainwin_freq_text, 156, 43);
1037

    
1038
    mainwin_menurow = new MenuRow;
1039
    mainwin->put_widget (false, mainwin_menurow, 10, 22);
1040

    
1041
    mainwin_volume = new HSlider (0, 51, SKIN_VOLUME, 68, 13, 0, 0, 14, 11, 15, 422, 0, 422);
1042
    mainwin->put_widget (false, mainwin_volume, 107, 57);
1043
    mainwin_volume->on_move (mainwin_volume_motion_cb);
1044
    mainwin_volume->on_release (mainwin_volume_release_cb);
1045

    
1046
    mainwin_balance = new HSlider (0, 24, SKIN_BALANCE, 38, 13, 9, 0, 14, 11, 15, 422, 0, 422);
1047
    mainwin->put_widget (false, mainwin_balance, 177, 57);
1048
    mainwin_balance->on_move (mainwin_balance_motion_cb);
1049
    mainwin_balance->on_release (mainwin_balance_release_cb);
1050

    
1051
    mainwin_monostereo = new MonoStereo;
1052
    mainwin->put_widget (false, mainwin_monostereo, 212, 41);
1053

    
1054
    mainwin_playstatus = new PlayStatus;
1055
    mainwin->put_widget (false, mainwin_playstatus, 24, 28);
1056

    
1057
    mainwin_minus_num = new SkinnedNumber;
1058
    mainwin->put_widget (false, mainwin_minus_num, 36, 26);
1059

    
1060
    mainwin_10min_num = new SkinnedNumber;
1061
    mainwin->put_widget (false, mainwin_10min_num, 48, 26);
1062

    
1063
    mainwin_min_num = new SkinnedNumber;
1064
    mainwin->put_widget (false, mainwin_min_num, 60, 26);
1065

    
1066
    mainwin_10sec_num = new SkinnedNumber;
1067
    mainwin->put_widget (false, mainwin_10sec_num, 78, 26);
1068

    
1069
    mainwin_sec_num = new SkinnedNumber;
1070
    mainwin->put_widget (false, mainwin_sec_num, 90, 26);
1071

    
1072
    mainwin_about = new Button (20, 25);
1073
    mainwin->put_widget (false, mainwin_about, 247, 83);
1074
    mainwin_about->on_release ((ButtonCB) audgui_show_about_window);
1075

    
1076
    mainwin_vis = new SkinnedVis;
1077
    mainwin->put_widget (false, mainwin_vis, 24, 43);
1078

    
1079
    mainwin_position = new HSlider (0, 219, SKIN_POSBAR, 248, 10, 0, 0, 29, 10, 248, 0, 278, 0);
1080
    mainwin->put_widget (false, mainwin_position, 16, 72);
1081
    mainwin_position->on_move (mainwin_position_motion_cb);
1082
    mainwin_position->on_release (mainwin_position_release_cb);
1083

    
1084
    /* shaded */
1085

    
1086
    mainwin_shaded_menubtn = new Button (9, 9, 0, 0, 0, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
1087
    mainwin->put_widget (true, mainwin_shaded_menubtn, 6, 3);
1088
    mainwin_shaded_menubtn->on_release ((ButtonCB) mainwin_menubtn_cb);
1089

    
1090
    mainwin_shaded_minimize = new Button (9, 9, 9, 0, 9, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
1091
    mainwin->put_widget (true, mainwin_shaded_minimize, 244, 3);
1092
    mainwin_shaded_minimize->on_release ((ButtonCB) mainwin_minimize_cb);
1093

    
1094
    mainwin_shaded_shade = new Button (9, 9, 0, 27, 9, 27, SKIN_TITLEBAR, SKIN_TITLEBAR);
1095
    mainwin->put_widget (true, mainwin_shaded_shade, 254, 3);
1096
    mainwin_shaded_shade->on_release ((ButtonCB) mainwin_shade_toggle);
1097

    
1098
    mainwin_shaded_close = new Button (9, 9, 18, 0, 18, 9, SKIN_TITLEBAR, SKIN_TITLEBAR);
1099
    mainwin->put_widget (true, mainwin_shaded_close, 264, 3);
1100
    mainwin_shaded_close->on_release ((ButtonCB) skins_close);
1101

    
1102
    mainwin_srew = new Button (8, 7);
1103
    mainwin->put_widget (true, mainwin_srew, 169, 4);
1104
    mainwin_srew->on_release ((ButtonCB) aud_drct_pl_prev);
1105

    
1106
    mainwin_splay = new Button (10, 7);
1107
    mainwin->put_widget (true, mainwin_splay, 177, 4);
1108
    mainwin_splay->on_release ((ButtonCB) aud_drct_play);
1109

    
1110
    mainwin_spause = new Button (10, 7);
1111
    mainwin->put_widget (true, mainwin_spause, 187, 4);
1112
    mainwin_spause->on_release ((ButtonCB) aud_drct_pause);
1113

    
1114
    mainwin_sstop = new Button (9, 7);
1115
    mainwin->put_widget (true, mainwin_sstop, 197, 4);
1116
    mainwin_sstop->on_release ((ButtonCB) aud_drct_stop);
1117

    
1118
    mainwin_sfwd = new Button (8, 7);
1119
    mainwin->put_widget (true, mainwin_sfwd, 206, 4);
1120
    mainwin_sfwd->on_release ((ButtonCB) aud_drct_pl_next);
1121

    
1122
    mainwin_seject = new Button (9, 7);
1123
    mainwin->put_widget (true, mainwin_seject, 216, 4);
1124
    mainwin_seject->on_release ((ButtonCB) action_play_file);
1125

    
1126
    mainwin_svis = new SmallVis ();
1127
    mainwin->put_widget (true, mainwin_svis, 79, 5);
1128

    
1129
    mainwin_sposition = new HSlider (1, 13, SKIN_TITLEBAR, 17, 7, 0, 36, 3, 7, 17, 36, 17, 36);
1130
    mainwin->put_widget (true, mainwin_sposition, 226, 4);
1131
    mainwin_sposition->on_move (mainwin_spos_motion_cb);
1132
    mainwin_sposition->on_release (mainwin_spos_release_cb);
1133

    
1134
    mainwin_stime_min = new TextBox (15, nullptr, false);
1135
    mainwin->put_widget (true, mainwin_stime_min, 130, 4);
1136
    mainwin_stime_min->on_press (change_timer_mode_cb);
1137

    
1138
    mainwin_stime_sec = new TextBox (10, nullptr, false);
1139
    mainwin->put_widget (true, mainwin_stime_sec, 147, 4);
1140
    mainwin_stime_sec->on_press (change_timer_mode_cb);
1141
}
1142

    
1143
static gboolean state_cb (GtkWidget * widget, GdkEventWindowState * event, void *)
1144
{
1145
    if (event->changed_mask & GDK_WINDOW_STATE_STICKY)
1146
        view_set_sticky (!! (event->new_window_state & GDK_WINDOW_STATE_STICKY));
1147

    
1148
    if (event->changed_mask & GDK_WINDOW_STATE_ABOVE)
1149
        view_set_on_top (!! (event->new_window_state & GDK_WINDOW_STATE_ABOVE));
1150

    
1151
    return true;
1152
}
1153

    
1154
void MainWindow::draw (cairo_t * cr)
1155
{
1156
    int width = is_shaded () ? MAINWIN_SHADED_WIDTH : skin.hints.mainwin_width;
1157
    int height = is_shaded () ? MAINWIN_SHADED_HEIGHT : skin.hints.mainwin_height;
1158

    
1159
    skin_draw_pixbuf (cr, SKIN_MAIN, 0, 0, 0, 0, width, height);
1160
    skin_draw_mainwin_titlebar (cr, is_shaded (), true);
1161
}
1162

    
1163
static void mainwin_create_window ()
1164
{
1165
    bool shaded = aud_get_bool ("skins", "player_shaded");
1166

    
1167
    mainwin = new MainWindow (shaded);
1168

    
1169
    GtkWidget * w = mainwin->gtk ();
1170
    drag_dest_set (w);
1171

    
1172
    g_signal_connect (w, "drag-data-received", (GCallback) mainwin_drag_data_received, nullptr);
1173
    g_signal_connect (w, "window-state-event", (GCallback) state_cb, nullptr);
1174

    
1175
    hook_associate ("playback begin", (HookFunction) mainwin_playback_begin, nullptr);
1176
    hook_associate ("playback ready", (HookFunction) mainwin_playback_begin, nullptr);
1177
    hook_associate ("playback seek", (HookFunction) mainwin_update_song_info, nullptr);
1178
    hook_associate ("playback stop", (HookFunction) mainwin_playback_stop, nullptr);
1179
    hook_associate ("playback pause", (HookFunction) playback_pause, nullptr);
1180
    hook_associate ("playback unpause", (HookFunction) playback_unpause, nullptr);
1181
    hook_associate ("title change", (HookFunction) title_change, nullptr);
1182
    hook_associate ("info change", (HookFunction) info_change, nullptr);
1183
    hook_associate ("set repeat", (HookFunction) repeat_toggled, nullptr);
1184
    hook_associate ("set shuffle", (HookFunction) shuffle_toggled, nullptr);
1185
    hook_associate ("set no_playlist_advance", (HookFunction) no_advance_toggled, nullptr);
1186
    hook_associate ("set stop_after_current_song", (HookFunction) stop_after_song_toggled, nullptr);
1187
}
1188

    
1189
void mainwin_unhook ()
1190
{
1191
    seeking = false;
1192
    timer_remove (TimerRate::Hz10, seek_timeout);
1193

    
1194
    status_message_timeout.stop ();
1195
    mainwin_volume_release_timeout.stop ();
1196

    
1197
    hook_dissociate ("playback begin", (HookFunction) mainwin_playback_begin);
1198
    hook_dissociate ("playback ready", (HookFunction) mainwin_playback_begin);
1199
    hook_dissociate ("playback seek", (HookFunction) mainwin_update_song_info);
1200
    hook_dissociate ("playback stop", (HookFunction) mainwin_playback_stop);
1201
    hook_dissociate ("playback pause", (HookFunction) playback_pause);
1202
    hook_dissociate ("playback unpause", (HookFunction) playback_unpause);
1203
    hook_dissociate ("title change", (HookFunction) title_change);
1204
    hook_dissociate ("info change", (HookFunction) info_change);
1205
    hook_dissociate ("set repeat", (HookFunction) repeat_toggled);
1206
    hook_dissociate ("set shuffle", (HookFunction) shuffle_toggled);
1207
    hook_dissociate ("set no_playlist_advance", (HookFunction) no_advance_toggled);
1208
    hook_dissociate ("set stop_after_current_song", (HookFunction) stop_after_song_toggled);
1209

    
1210
    start_stop_visual (true);
1211

    
1212
    locked_textbox = nullptr;
1213
    locked_old_text = String ();
1214
}
1215

    
1216
void mainwin_create ()
1217
{
1218
    mainwin_create_window ();
1219
    mainwin_create_widgets ();
1220
    mainwin_set_song_title (nullptr);
1221
}
1222

    
1223
static void mainwin_update_volume ()
1224
{
1225
    int volume = aud_drct_get_volume_main ();
1226
    int balance = aud_drct_get_volume_balance ();
1227

    
1228
    mainwin_set_volume_slider (volume);
1229
    mainwin_set_balance_slider (balance);
1230
    equalizerwin_set_volume_slider (volume);
1231
    equalizerwin_set_balance_slider (balance);
1232
}
1233

    
1234
static void mainwin_update_time_display (int time, int length)
1235
{
1236
    char scratch[7];
1237
    format_time (scratch, time, length);
1238

    
1239
    mainwin_minus_num->set (scratch[0]);
1240
    mainwin_10min_num->set (scratch[1]);
1241
    mainwin_min_num->set (scratch[2]);
1242
    mainwin_10sec_num->set (scratch[4]);
1243
    mainwin_sec_num->set (scratch[5]);
1244

    
1245
    if (! mainwin_sposition->get_pressed ())
1246
    {
1247
        mainwin_stime_min->set_text (scratch);
1248
        mainwin_stime_sec->set_text (scratch + 4);
1249
    }
1250

    
1251
    playlistwin_set_time (scratch, scratch + 4);
1252
}
1253

    
1254
static void mainwin_update_time_slider (int time, int length)
1255
{
1256
    mainwin_position->setVisible (length > 0);
1257
    mainwin_sposition->setVisible (length > 0);
1258

    
1259
    if (length > 0 && ! seeking)
1260
    {
1261
        if (time < length)
1262
        {
1263
            mainwin_position->set_pos (time * (int64_t) 219 / length);
1264
            mainwin_sposition->set_pos (1 + time * (int64_t) 12 / length);
1265
        }
1266
        else
1267
        {
1268
            mainwin_position->set_pos (219);
1269
            mainwin_sposition->set_pos (13);
1270
        }
1271

    
1272
        mainwin_spos_set_knob ();
1273
    }
1274
}
1275

    
1276
void mainwin_update_song_info ()
1277
{
1278
    mainwin_update_volume ();
1279

    
1280
    if (! aud_drct_get_playing ())
1281
        return;
1282

    
1283
    int time = 0, length = 0;
1284
    if (aud_drct_get_ready ())
1285
    {
1286
        time = aud_drct_get_time ();
1287
        length = aud_drct_get_length ();
1288
    }
1289

    
1290
    mainwin_update_time_display (time, length);
1291
    mainwin_update_time_slider (time, length);
1292
}