1 /*
2  *  Copyright (C) 2005 Marc Pavot <marc.pavot@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2, or (at your option)
7  *  any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  */
19 
20 #include "shell/ario-shell.h"
21 
22 #include <gtk/gtk.h>
23 #include <gdk/gdk.h>
24 #include <config.h>
25 #include <string.h>
26 #include <glib/gi18n.h>
27 
28 #include "ario-debug.h"
29 #include "ario-util.h"
30 #include "covers/ario-cover-handler.h"
31 #include "covers/ario-cover-manager.h"
32 #include "lib/ario-conf.h"
33 #include "lyrics/ario-lyrics-manager.h"
34 #include "notification/ario-notification-manager.h"
35 #include "playlist/ario-playlist-manager.h"
36 #include "plugins/ario-plugin-manager.h"
37 #include "preferences/ario-preferences.h"
38 #include "servers/ario-server.h"
39 #include "shell/ario-shell-coverdownloader.h"
40 #include "shell/ario-shell-coverselect.h"
41 #include "shell/ario-shell-lyrics.h"
42 #include "shell/ario-shell-preferences.h"
43 #include "shell/ario-shell-similarartists.h"
44 #include "sources/ario-source-manager.h"
45 #include "widgets/ario-firstlaunch.h"
46 #include "widgets/ario-header.h"
47 #include "widgets/ario-playlist.h"
48 #include "widgets/ario-status-bar.h"
49 
50 static void ario_shell_finalize (GObject *object);
51 static void ario_shell_show (ArioShell *shell,
52                              gboolean minimized);
53 static void ario_shell_cmd_quit (GSimpleAction *action,
54                                  GVariant *parameter,
55                                  gpointer data);
56 static void ario_shell_cmd_connect (GSimpleAction *action,
57                                     GVariant *parameter,
58                                     gpointer data);
59 static void ario_shell_cmd_disconnect (GSimpleAction *action,
60                                        GVariant *parameter,
61                                        gpointer data);
62 static void ario_shell_cmd_update (GSimpleAction *action,
63                                    GVariant *parameter,
64                                    gpointer data);
65 static void ario_shell_cmd_plugins (GSimpleAction *action,
66                                     GVariant *parameter,
67                                     gpointer data);
68 static void ario_shell_cmd_preferences (GSimpleAction *action,
69                                         GVariant *parameter,
70                                         gpointer data);
71 static void ario_shell_cmd_lyrics (GSimpleAction *action,
72                                    GVariant *parameter,
73                                    gpointer data);
74 static void ario_shell_cmd_cover_select (GSimpleAction *action,
75                                          GVariant *parameter,
76                                          gpointer data);
77 static void ario_shell_cmd_covers (GSimpleAction *action,
78                                    GVariant *parameter,
79                                    gpointer data);
80 static void ario_shell_cmd_similar_artists (GSimpleAction *action,
81                                             GVariant *parameter,
82                                             gpointer data);
83 static void ario_shell_cmd_add_similar (GSimpleAction *action,
84                                         GVariant *parameter,
85                                         gpointer data);
86 static void ario_shell_cmd_about (GSimpleAction *action,
87                                   GVariant *parameter,
88                                   gpointer data);
89 static void ario_shell_server_state_changed_cb (ArioServer *server,
90                                                 ArioShell *shell);
91 static void ario_shell_server_song_changed_cb (ArioServer *server,
92                                                ArioShell *shell);
93 static gboolean ario_shell_window_state_cb (GtkWidget *widget,
94                                             GdkEvent *event,
95                                             ArioShell *shell);
96 static void ario_shell_sync_window_state (ArioShell *shell);
97 static void ario_shell_sync_paned (ArioShell *shell);
98 static void ario_shell_sync_server (ArioShell *shell);
99 static void ario_shell_sync_playlist_position (ArioShell *shell);
100 static void ario_shell_firstlaunch_delete_cb (ArioFirstlaunch *firstlaunch,
101                                               ArioShell *shell);
102 static void ario_shell_view_statusbar_changed_cb (GSimpleAction *action,
103                                                   GVariant *parameter,
104                                                   gpointer data);
105 static void ario_shell_view_upperpart_changed_cb (GSimpleAction *action,
106                                                   GVariant *parameter,
107                                                   gpointer data);
108 static void ario_shell_view_playlist_changed_cb (GSimpleAction *action,
109                                                  GVariant *parameter,
110                                                  gpointer data);
111 static void ario_shell_sync_statusbar_visibility (ArioShell *shell);
112 static void ario_shell_sync_upperpart_visibility (ArioShell *shell);
113 static void ario_shell_sync_playlist_visibility (ArioShell *shell);
114 static void ario_shell_playlist_position_changed_cb (guint notification_id,
115                                                      ArioShell *shell);
116 
117 struct ArioShellPrivate
118 {
119         GtkApplication * app;
120 
121         ArioCoverHandler *cover_handler;
122         ArioPlaylistManager *playlist_manager;
123         ArioNotificationManager *notification_manager;
124         GtkWidget *header;
125         GtkWidget *paned;
126         GtkWidget *sourcemanager;
127         GtkWidget *playlist;
128         GtkWidget *status_bar;
129         GtkWidget *vbox;
130         GtkWidget *hbox;
131 
132         gboolean statusbar_hidden;
133         gboolean upperpart_hidden;
134         gboolean playlist_hidden;
135         gboolean connected;
136         gboolean shown;
137 
138         gboolean maximized;
139         gboolean visible;
140 
141         int window_x;
142         int window_y;
143         int window_w;
144         int window_h;
145 };
146 
147 enum
148 {
149         PROP_0,
150 };
151 
152 static const GActionEntry shell_actions[] = {
153         { "connect", ario_shell_cmd_connect},
154         { "disconnect", ario_shell_cmd_disconnect},
155         { "update", ario_shell_cmd_update},
156         { "view-upperpart", ario_shell_view_upperpart_changed_cb, NULL, "false" },
157         { "view-playlist", ario_shell_view_playlist_changed_cb, NULL, "false" },
158         { "view-statusbar", ario_shell_view_statusbar_changed_cb, NULL, "false" },
159         { "view-lyrics", ario_shell_cmd_lyrics},
160         { "cover-select", ario_shell_cmd_cover_select},
161         { "dlcovers", ario_shell_cmd_covers},
162         { "similar-artists", ario_shell_cmd_similar_artists},
163         { "add-similar", ario_shell_cmd_add_similar},
164         { "preferences", ario_shell_cmd_preferences},
165         { "plugins", ario_shell_cmd_plugins},
166         { "about", ario_shell_cmd_about},
167         { "quit", ario_shell_cmd_quit},
168 };
169 
170 #define ARIO_SHELL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ARIO_TYPE_SHELL, ArioShellPrivate))
G_DEFINE_TYPE(ArioShell,ario_shell,GTK_TYPE_APPLICATION_WINDOW)171 G_DEFINE_TYPE (ArioShell, ario_shell, GTK_TYPE_APPLICATION_WINDOW)
172 
173 static void
174 ario_shell_class_init (ArioShellClass *klass)
175 {
176         ARIO_LOG_FUNCTION_START;
177         GObjectClass *object_class = (GObjectClass *) klass;
178 
179         /* Virtual methods */
180         object_class->finalize = ario_shell_finalize;
181 
182         /* Private attributes */
183         g_type_class_add_private (klass, sizeof (ArioShellPrivate));
184 }
185 
186 static void
ario_shell_init(ArioShell * shell)187 ario_shell_init (ArioShell *shell)
188 {
189         ARIO_LOG_FUNCTION_START;
190         shell->priv = ARIO_SHELL_GET_PRIVATE (shell);
191         shell->priv->connected = FALSE;
192         shell->priv->shown = FALSE;
193 
194         shell->priv->visible = TRUE;
195 
196         shell->priv->window_x = -1;
197         shell->priv->window_y = -1;
198         shell->priv->window_w = -1;
199         shell->priv->window_h = -1;
200 }
201 
202 static void
ario_shell_finalize(GObject * object)203 ario_shell_finalize (GObject *object)
204 {
205         ARIO_LOG_FUNCTION_START;
206         ArioShell *shell = ARIO_SHELL (object);
207 
208         gtk_widget_hide (GTK_WIDGET(shell));
209 
210         g_object_unref (shell->priv->playlist);
211         g_object_unref (shell->priv->sourcemanager);
212         g_object_unref (shell->priv->cover_handler);
213         g_object_unref (shell->priv->playlist_manager);
214         g_object_unref (shell->priv->notification_manager);
215 
216         g_object_unref (ario_server_get_instance ());
217 
218         G_OBJECT_CLASS (ario_shell_parent_class)->finalize (object);
219 }
220 
221 ArioShell *
ario_shell_new(GtkApplication * app)222 ario_shell_new (GtkApplication * app)
223 {
224         ARIO_LOG_FUNCTION_START;
225         ArioShell *shell;
226 
227         shell = g_object_new (ARIO_TYPE_SHELL, NULL);
228         shell->priv->app = app;
229 
230         return shell;
231 }
232 
233 static void
ario_shell_quit(ArioShell * shell)234 ario_shell_quit (ArioShell *shell)
235 {
236         ARIO_LOG_FUNCTION_START;
237 
238         /* Stop music on exit if needed */
239         if (ario_conf_get_boolean (PREF_STOP_EXIT, PREF_STOP_EXIT_DEFAULT))
240                 ario_server_do_stop ();
241 
242         /* Stop main loop */
243         g_application_quit (G_APPLICATION (shell->priv->app));
244 }
245 
246 static gboolean
ario_shell_window_delete_cb(GtkWidget * win,GdkEventAny * event,ArioShell * shell)247 ario_shell_window_delete_cb (GtkWidget *win,
248                              GdkEventAny *event,
249                              ArioShell *shell)
250 {
251         ARIO_LOG_FUNCTION_START;
252         if (ario_conf_get_boolean (PREF_HIDE_ON_CLOSE, PREF_HIDE_ON_CLOSE_DEFAULT)) {
253                 ario_shell_set_visibility (shell, VISIBILITY_TOGGLE);
254         } else {
255                 ario_shell_quit (shell);
256         }
257         return TRUE;
258 };
259 
260 void
ario_shell_construct(ArioShell * shell,gboolean minimized)261 ario_shell_construct (ArioShell *shell,
262                       gboolean minimized)
263 {
264         ARIO_LOG_FUNCTION_START;
265         GtkWidget *separator;
266         GAction *action;
267         ArioFirstlaunch *firstlaunch;
268         GtkBuilder *builder;
269         GMenuModel *menu;
270 
271         g_return_if_fail (IS_ARIO_SHELL (shell));
272 
273         /* Set main window properties */
274         gtk_window_set_title (GTK_WINDOW (shell), "Ario");
275         gtk_window_set_position (GTK_WINDOW (shell), GTK_WIN_POS_CENTER);
276 
277         /* Create program icon */
278         gtk_window_set_default_icon_name ("ario");
279 
280         /* Connect window destruction signal to exit program */
281         g_signal_connect (shell,
282                           "delete_event",
283                           G_CALLBACK (ario_shell_window_delete_cb),
284                           shell);
285 
286         /* Initialize UI */
287         builder = gtk_builder_new_from_file (UI_PATH "ario-shell-menu.ui");
288         menu = G_MENU_MODEL (gtk_builder_get_object (builder, "menu"));
289         if (!g_application_get_is_remote (G_APPLICATION (shell->priv->app))) {
290                 gtk_application_set_app_menu (shell->priv->app,
291                                               menu);
292         }
293         g_object_unref (builder);
294 
295         /* Main window actions */
296         g_action_map_add_action_entries (G_ACTION_MAP (g_application_get_default ()),
297                                          shell_actions, G_N_ELEMENTS (shell_actions),
298                                          shell);
299 
300         /* Initialize server object (MPD, XMMS, ....) */
301         ario_server_get_instance ();
302 
303         /* Initialize cover art handler */
304         shell->priv->cover_handler = ario_cover_handler_new ();
305 
306         /* Initialize playlist manager */
307         shell->priv->playlist_manager = ario_playlist_manager_get_instance ();
308 
309         /* Initialize notification manager */
310         shell->priv->notification_manager = ario_notification_manager_get_instance ();
311 
312         /* Add widgets to main window.
313          * Structure is:
314          * vbox
315          * ---header
316          * ---separator
317          * ---hbox
318          * -----hpaned/vpaned
319          * -------sourcemanager
320          * -------playlist
321          * ---status_bar
322          */
323 
324         /* Create main vbox */
325         shell->priv->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
326 
327         /* Create header */
328         shell->priv->header = ario_header_new ();
329 
330         /* Create separator */
331         separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
332 
333         /* Create playlist */
334         shell->priv->playlist = ario_playlist_new ();
335         g_object_ref (shell->priv->playlist);
336 
337         /* Create source manager */
338         shell->priv->sourcemanager = ario_source_manager_get_instance ();
339         g_object_ref (shell->priv->sourcemanager);
340 
341         /* Create the hbox(for tabs and playlist) */
342         shell->priv->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
343 
344         /* Create status bar */
345         shell->priv->status_bar = ario_status_bar_new ();
346 
347         /* Synchronize status bar checkbox in menu with preferences */
348         shell->priv->statusbar_hidden = ario_conf_get_boolean (PREF_STATUSBAR_HIDDEN, PREF_STATUSBAR_HIDDEN_DEFAULT);
349         action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()),
350                                              "view-statusbar");
351         g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (!shell->priv->statusbar_hidden));
352 
353         /* Synchronize upper part checkbox in menu with preferences */
354         shell->priv->upperpart_hidden = ario_conf_get_boolean (PREF_UPPERPART_HIDDEN, PREF_UPPERPART_HIDDEN_DEFAULT);
355         action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()),
356                                              "view-upperpart");
357         g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (!shell->priv->upperpart_hidden));
358 
359         /* Synchronize playlist checkbox in menu with preferences */
360         shell->priv->playlist_hidden = ario_conf_get_boolean (PREF_PLAYLIST_HIDDEN, PREF_PLAYLIST_HIDDEN_DEFAULT);
361         action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()),
362                                              "view-playlist");
363         g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (!shell->priv->playlist_hidden));
364 
365         /* Add widgets to vbox */
366         gtk_box_pack_start (GTK_BOX (shell->priv->vbox),
367                             shell->priv->header,
368                             FALSE, FALSE, 0);
369 
370         gtk_box_pack_start (GTK_BOX (shell->priv->vbox),
371                             separator,
372                             FALSE, FALSE, 0);
373 
374         gtk_box_pack_start (GTK_BOX (shell->priv->vbox),
375                             shell->priv->hbox,
376                             TRUE, TRUE, 0);
377 
378         gtk_box_pack_start (GTK_BOX (shell->priv->vbox),
379                             shell->priv->status_bar,
380                             FALSE, FALSE, 0);
381 
382         /* Add vbox to main window */
383         gtk_container_add (GTK_CONTAINER (shell), shell->priv->vbox);
384 
385         /* First launch assistant */
386         if (!ario_conf_get_boolean (PREF_FIRST_TIME, PREF_FIRST_TIME_DEFAULT)) {
387                 firstlaunch = ario_firstlaunch_new (shell->priv->app);
388                 g_signal_connect (firstlaunch,
389                                   "destroy",
390                                   G_CALLBACK (ario_shell_firstlaunch_delete_cb),
391                                   shell);
392                 gtk_widget_show_all (GTK_WIDGET (firstlaunch));
393         } else {
394                 ario_shell_show (shell, minimized);
395         }
396 
397         /* Synchronize main window state with preferences */
398         ario_shell_sync_window_state (shell);
399 
400         /* Synchronize status bar visibility with preferences */
401         ario_shell_sync_statusbar_visibility (shell);
402 
403         /* Synchronize upper part visibility with preferences */
404         ario_shell_sync_upperpart_visibility (shell);
405 
406         /* Synchronize playlist visibility with preferences */
407         ario_shell_sync_playlist_visibility (shell);
408 }
409 
410 void
ario_shell_shutdown(ArioShell * shell)411 ario_shell_shutdown (ArioShell *shell)
412 {
413         ARIO_LOG_FUNCTION_START;
414         int width, height;
415 
416         /* If the main window is visible, we save a few preferences */
417         if (shell->priv->shown) {
418                 if (shell->priv->paned) {
419                         /* Save paned position */
420                         ario_conf_set_integer (PREF_VPANED_POSITION,
421                                                gtk_paned_get_position (GTK_PANED (shell->priv->paned)));
422                 }
423 
424                 /* Save window size */
425                 if (!ario_conf_get_boolean (PREF_WINDOW_MAXIMIZED, PREF_WINDOW_MAXIMIZED_DEFAULT)) {
426                         gtk_window_get_size (GTK_WINDOW (shell),
427                                              &width,
428                                              &height);
429 
430                         ario_conf_set_integer (PREF_WINDOW_WIDTH, width);
431                         ario_conf_set_integer (PREF_WINDOW_HEIGHT, height);
432                 }
433         }
434 
435         /* Shutdown the playlist */
436         ario_playlist_shutdown ();
437 
438         /* Shutdown the source manager */
439         ario_source_manager_shutdown ();
440 
441         /* Shutdown the cover_manager */
442         ario_cover_manager_shutdown (ario_cover_manager_get_instance ());
443 
444         /* Shutdown the lyrics_manager */
445         ario_lyrics_manager_shutdown (ario_lyrics_manager_get_instance ());
446 
447         /* Shutdown the server object */
448         ario_server_shutdown ();
449 }
450 
451 static void
ario_shell_show(ArioShell * shell,gboolean minimized)452 ario_shell_show (ArioShell *shell,
453                  gboolean minimized)
454 {
455         ARIO_LOG_FUNCTION_START;
456         ArioServer *server = ario_server_get_instance ();
457 
458         /* Connect signals for server state changes */
459         g_signal_connect (server,
460                           "state_changed",
461                           G_CALLBACK (ario_shell_server_state_changed_cb),
462                           shell);
463 
464         g_signal_connect (server,
465                           "song_changed",
466                           G_CALLBACK (ario_shell_server_song_changed_cb),
467                           shell);
468 
469         /* Autoconnect on startup if needed */
470         if (ario_conf_get_boolean (PREF_AUTOCONNECT, PREF_AUTOCONNECT_DEFAULT))
471                 ario_server_connect ();
472 
473         /* Show main window */
474         gtk_widget_show_all (GTK_WIDGET(shell));
475         shell->priv->shown = TRUE;
476 
477         /* Synchonize the main window with server state */
478         ario_shell_sync_server (shell);
479 
480         /* Synchronize playlist position with preferences */
481         ario_shell_sync_playlist_position (shell);
482 
483         /* Synchronize paned with preferences */
484         ario_shell_sync_paned (shell);
485 
486         /* Minimize window if needed */
487         if (minimized)
488                 ario_shell_set_visibility (shell, VISIBILITY_HIDDEN);
489 
490         /* Connect signal for window state changes */
491         g_signal_connect_object (shell,
492                                  "window-state-event",
493                                  G_CALLBACK (ario_shell_window_state_cb),
494                                  shell,
495                                  G_CONNECT_AFTER);
496 
497         /* Update server db on startup if needed */
498         if (ario_conf_get_boolean (PREF_UPDATE_STARTUP, PREF_UPDATE_STARTUP_DEFAULT))
499                 ario_server_update_db (NULL);
500 
501         /* Notification for trees configuration changes */
502         ario_conf_notification_add (PREF_PLAYLIST_POSITION,
503                                     (ArioNotifyFunc) ario_shell_playlist_position_changed_cb,
504                                     shell);
505 }
506 
507 void
ario_shell_present(ArioShell * shell)508 ario_shell_present (ArioShell *shell)
509 {
510         ARIO_LOG_FUNCTION_START;
511         if (ario_conf_get_boolean (PREF_FIRST_TIME, PREF_FIRST_TIME_DEFAULT)) {
512                 ario_shell_set_visibility(shell, VISIBILITY_VISIBLE);
513                 gtk_window_present (GTK_WINDOW (shell));
514         }
515 }
516 
517 void
ario_shell_set_visibility(ArioShell * shell,ArioVisibility state)518 ario_shell_set_visibility (ArioShell *shell,
519                            ArioVisibility state)
520 {
521         ARIO_LOG_FUNCTION_START;
522         switch (state)
523         {
524         case VISIBILITY_HIDDEN:
525                 if (shell->priv->visible)
526                         ario_shell_set_visibility (shell, VISIBILITY_TOGGLE);
527                 break;
528         case VISIBILITY_VISIBLE:
529                 if (!shell->priv->visible)
530                         ario_shell_set_visibility (shell, VISIBILITY_TOGGLE);
531                 break;
532         case VISIBILITY_TOGGLE:
533                 shell->priv->visible = !shell->priv->visible;
534 
535                 if (shell->priv->visible == TRUE) {
536                         /* Restore window state, size and position */
537                         if (shell->priv->window_x >= 0 && shell->priv->window_y >= 0) {
538                                 gtk_window_move (GTK_WINDOW (shell),
539                                                  shell->priv->window_x,
540                                                  shell->priv->window_y);
541                         }
542                         if (!shell->priv->maximized
543                             && (shell->priv->window_w >= 0 && shell->priv->window_y >=0)) {
544                                 gtk_window_resize (GTK_WINDOW (shell),
545                                                    shell->priv->window_w,
546                                                    shell->priv->window_h);
547                         }
548 
549                         if (shell->priv->maximized)
550                                 gtk_window_maximize (GTK_WINDOW (shell));
551                         gtk_widget_show (GTK_WIDGET(shell));
552 
553                         /* Restore paned position
554                          * This is in a g_timeout_add because there seems to have a bug (#2798748) if I call
555                          * ario_shell_sync_paned directly. I really don't understand why but this seems to work...*/
556                         g_timeout_add (100, (GSourceFunc) ario_shell_sync_paned, shell);
557                 } else {
558                         /* Save window state, size and position */
559                         shell->priv->maximized = ario_conf_get_boolean (PREF_WINDOW_MAXIMIZED, PREF_WINDOW_MAXIMIZED_DEFAULT);
560                         gtk_window_get_position (GTK_WINDOW (shell),
561                                                  &shell->priv->window_x,
562                                                  &shell->priv->window_y);
563                         gtk_window_get_size (GTK_WINDOW (shell),
564                                              &shell->priv->window_w,
565                                              &shell->priv->window_h);
566 
567                         /* Save paned position */
568                         ario_conf_set_integer (PREF_VPANED_POSITION,
569                                                gtk_paned_get_position (GTK_PANED (shell->priv->paned)));
570 
571                         gtk_widget_hide (GTK_WIDGET(shell));
572                 }
573         }
574 }
575 
576 static void
ario_shell_cmd_quit(GSimpleAction * action,GVariant * parameter,gpointer data)577 ario_shell_cmd_quit (GSimpleAction *action,
578                      GVariant *parameter,
579                      gpointer data)
580 {
581         ARIO_LOG_FUNCTION_START;
582         ArioShell *shell = ARIO_SHELL (data);
583         ario_shell_quit (shell);
584 }
585 
586 static void
ario_shell_cmd_connect(GSimpleAction * action,GVariant * parameter,gpointer data)587 ario_shell_cmd_connect (GSimpleAction *action,
588                         GVariant *parameter,
589                         gpointer data)
590 {
591         ARIO_LOG_FUNCTION_START;
592         ario_server_connect ();
593 }
594 
595 static void
ario_shell_cmd_disconnect(GSimpleAction * action,GVariant * parameter,gpointer data)596 ario_shell_cmd_disconnect (GSimpleAction *action,
597                            GVariant *parameter,
598                            gpointer data)
599 {
600         ARIO_LOG_FUNCTION_START;
601         ario_server_disconnect ();
602 }
603 
604 static void
ario_shell_cmd_update(GSimpleAction * action,GVariant * parameter,gpointer data)605 ario_shell_cmd_update (GSimpleAction *action,
606                        GVariant *parameter,
607                        gpointer data)
608 {
609         ARIO_LOG_FUNCTION_START;
610         ario_server_update_db (NULL);
611 }
612 
613 static void
ario_shell_cmd_preferences(GSimpleAction * action,GVariant * parameter,gpointer data)614 ario_shell_cmd_preferences (GSimpleAction *action,
615                             GVariant *parameter,
616                             gpointer data)
617 {
618         ARIO_LOG_FUNCTION_START;
619         GtkWidget *prefs;
620         ArioShell *shell = ARIO_SHELL (data);
621 
622         /* Create preferences dialog window */
623         prefs = ario_shell_preferences_new ();
624 
625         gtk_window_set_transient_for (GTK_WINDOW (prefs),
626                                       GTK_WINDOW (shell));
627 
628         gtk_widget_show_all (prefs);
629 }
630 
631 static void
ario_shell_cmd_lyrics(GSimpleAction * action,GVariant * parameter,gpointer data)632 ario_shell_cmd_lyrics (GSimpleAction *action,
633                        GVariant *parameter,
634                        gpointer data)
635 {
636         ARIO_LOG_FUNCTION_START;
637         GtkWidget *lyrics;
638 
639         /* Create lyrics dialog window */
640         lyrics = ario_shell_lyrics_new ();
641         if (lyrics)
642                 gtk_widget_show_all (lyrics);
643 }
644 
645 static void
ario_shell_cmd_about(GSimpleAction * action,GVariant * parameter,gpointer data)646 ario_shell_cmd_about (GSimpleAction *action,
647                       GVariant *parameter,
648                       gpointer data)
649 {
650         ARIO_LOG_FUNCTION_START;
651         ArioShell *shell = ARIO_SHELL (data);
652 
653         /* Create about dialog window */
654         const char *authors[] = {
655 #include "AUTHORS.tab"
656                 "",
657                 NULL
658         };
659         const char *artists[] = {
660                 "Luc Pavot",
661                 NULL
662         };
663         GdkPixbuf *logo_pixbuf = gdk_pixbuf_new_from_file (PIXMAP_PATH "logo.png", NULL);
664         gtk_show_about_dialog (GTK_WINDOW (shell),
665                                "name", "Ario",
666                                "program-name", "Ario",
667                                "version", PACKAGE_VERSION,
668                                "copyright", "Copyright \xc2\xa9 2005-" CURRENT_DATE " Marc Pavot",
669                                "comments", _("GTK client for MPD"),
670                                "translator-credits", _("translator-credits"),
671                                "authors", (const char **) authors,
672                                "artists", (const char **) artists,
673                                "logo", logo_pixbuf,
674                                NULL);
675         if (logo_pixbuf)
676                 g_object_unref (logo_pixbuf);
677 }
678 
679 static void
ario_shell_server_song_set_title(ArioShell * shell)680 ario_shell_server_song_set_title (ArioShell *shell)
681 {
682         ARIO_LOG_FUNCTION_START;
683         gchar *window_title;
684         gchar *tmp;
685 
686         switch (ario_server_get_current_state ()) {
687         case ARIO_STATE_PLAY:
688         case ARIO_STATE_PAUSE:
689                 /* Window title containing song name */
690                 tmp = ario_util_format_title (ario_server_get_current_song ());
691                 window_title = g_strdup_printf ("Ario - %s", tmp);
692                 gtk_window_set_title (GTK_WINDOW (shell), window_title);
693                 g_free (window_title);
694                 break;
695         default:
696                 /* Default window title */
697                 gtk_window_set_title (GTK_WINDOW (shell), "Ario");
698                 break;
699         }
700 }
701 
702 static void
ario_shell_server_song_changed_cb(ArioServer * server,ArioShell * shell)703 ario_shell_server_song_changed_cb (ArioServer *server,
704                                    ArioShell *shell)
705 {
706         ARIO_LOG_FUNCTION_START;
707         /* Change window title on song change */
708         ario_shell_server_song_set_title (shell);
709 }
710 
711 static void
ario_shell_server_state_changed_cb(ArioServer * server,ArioShell * shell)712 ario_shell_server_state_changed_cb (ArioServer *server,
713                                     ArioShell *shell)
714 {
715         ARIO_LOG_FUNCTION_START;
716         shell->priv->connected = ario_server_is_connected ();
717 
718         /* Synchronize main window with server state */
719         ario_shell_sync_server (shell);
720 
721         /* Change window title on song change */
722         ario_shell_server_song_set_title (shell);
723 }
724 
725 static void
ario_shell_cmd_cover_select(GSimpleAction * action,GVariant * parameter,gpointer data)726 ario_shell_cmd_cover_select (GSimpleAction *action,
727                              GVariant *parameter,
728                              gpointer data)
729 {
730         ARIO_LOG_FUNCTION_START;
731         GtkWidget *coverselect;
732         ArioServerAlbum server_album;
733 
734         /* Launch cover selection dialog for current album */
735         server_album.artist = ario_server_get_current_artist ();
736         server_album.album = ario_server_get_current_album ();
737         server_album.path = g_path_get_dirname ((ario_server_get_current_song ())->file);
738 
739         if (!server_album.album)
740                 server_album.album = ARIO_SERVER_UNKNOWN;
741 
742         if (!server_album.artist)
743                 server_album.artist = ARIO_SERVER_UNKNOWN;
744 
745         coverselect = ario_shell_coverselect_new (&server_album);
746         gtk_dialog_run (GTK_DIALOG (coverselect));
747         gtk_widget_destroy (coverselect);
748         g_free (server_album.path);
749 }
750 
751 static void
ario_shell_cmd_covers(GSimpleAction * action,GVariant * parameter,gpointer data)752 ario_shell_cmd_covers (GSimpleAction *action,
753                        GVariant *parameter,
754                        gpointer data)
755 {
756         ARIO_LOG_FUNCTION_START;
757         GtkWidget *coverdownloader;
758 
759         /* Launch cover art download dialog */
760         coverdownloader = ario_shell_coverdownloader_new ();
761 
762         if (coverdownloader) {
763                 ario_shell_coverdownloader_get_covers (ARIO_SHELL_COVERDOWNLOADER (coverdownloader),
764                                                        GET_COVERS);
765         }
766 }
767 
768 static void
ario_shell_cmd_similar_artists(GSimpleAction * action,GVariant * parameter,gpointer data)769 ario_shell_cmd_similar_artists (GSimpleAction *action,
770                                 GVariant *parameter,
771                                 gpointer data)
772 {
773         ARIO_LOG_FUNCTION_START;
774         GtkWidget *similarartists;
775 
776         /* Launch similar artist dialog */
777         similarartists = ario_shell_similarartists_new ();
778         if (similarartists)
779                 gtk_widget_show_all (similarartists);
780 }
781 
782 static void
ario_shell_cmd_add_similar(GSimpleAction * action,GVariant * parameter,gpointer data)783 ario_shell_cmd_add_similar (GSimpleAction *action,
784                             GVariant *parameter,
785                             gpointer data)
786 {
787         ARIO_LOG_FUNCTION_START;
788 
789         /* Add similar artists (from last.fm) to current playlist */
790         ario_shell_similarartists_add_similar_to_playlist (ario_server_get_current_artist (), -1);
791 }
792 
793 static void
ario_shell_sync_paned(ArioShell * shell)794 ario_shell_sync_paned (ArioShell *shell)
795 {
796         ARIO_LOG_FUNCTION_START;
797         int pos;
798 
799         /* Set paned position */
800         pos = ario_conf_get_integer (PREF_VPANED_POSITION, PREF_VPANED_POSITION_DEFAULT);
801         if (pos > 0 && shell->priv->paned)
802                 gtk_paned_set_position (GTK_PANED (shell->priv->paned),
803                                         pos);
804 }
805 
806 static gboolean
ario_shell_window_state_cb(GtkWidget * widget,GdkEvent * event,ArioShell * shell)807 ario_shell_window_state_cb (GtkWidget *widget,
808                             GdkEvent *event,
809                             ArioShell *shell)
810 {
811         ARIO_LOG_FUNCTION_START;
812         int width, height;
813         g_return_val_if_fail (widget != NULL, FALSE);
814 
815         if ((event->type == GDK_WINDOW_STATE)
816             && !(event->window_state.new_window_state & GDK_WINDOW_STATE_WITHDRAWN)) {
817                 /* Save window maximization state */
818                 ario_conf_set_boolean (PREF_WINDOW_MAXIMIZED,
819                                        event->window_state.new_window_state &
820                                        GDK_WINDOW_STATE_MAXIMIZED);
821 
822                 if (event->window_state.changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
823                         /* Save previous window size on maximization */
824                         gtk_window_get_size (GTK_WINDOW (shell),
825                                              &width,
826                                              &height);
827 
828                         ario_conf_set_integer (PREF_WINDOW_WIDTH, width);
829                         ario_conf_set_integer (PREF_WINDOW_HEIGHT, height);
830                 }
831         }
832 
833         return FALSE;
834 }
835 
836 static void
ario_shell_sync_window_state(ArioShell * shell)837 ario_shell_sync_window_state (ArioShell *shell)
838 {
839         ARIO_LOG_FUNCTION_START;
840         int width = ario_conf_get_integer (PREF_WINDOW_WIDTH, PREF_WINDOW_WIDTH_DEFAULT);
841         int height = ario_conf_get_integer (PREF_WINDOW_HEIGHT, PREF_WINDOW_HEIGHT_DEFAULT);
842         gboolean maximized = ario_conf_get_boolean (PREF_WINDOW_MAXIMIZED, PREF_WINDOW_MAXIMIZED_DEFAULT);
843 
844         /* Set main window size */
845         gtk_window_set_default_size (GTK_WINDOW (shell),
846                                      width, height);
847         gtk_window_resize (GTK_WINDOW (shell),
848                            width, height);
849 
850         /* Maximize main window if needed */
851         if (maximized)
852                 gtk_window_maximize (GTK_WINDOW (shell));
853         else
854                 gtk_window_unmaximize (GTK_WINDOW (shell));
855 }
856 
857 static void
ario_shell_sync_server(ArioShell * shell)858 ario_shell_sync_server (ArioShell *shell)
859 {
860         ARIO_LOG_FUNCTION_START;
861         GAction *connect_action;
862         GAction *disconnect_action;
863         gboolean is_playing;
864         GAction *action;
865 
866         /* Set connect entry visibility */
867         connect_action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()),
868                                                       "connect");
869         g_simple_action_set_enabled (G_SIMPLE_ACTION (connect_action), !shell->priv->connected);
870 
871         /* Set disconnect entry visibility */
872         disconnect_action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()),
873                                                          "disconnect");
874         g_simple_action_set_enabled (G_SIMPLE_ACTION (disconnect_action), shell->priv->connected);
875 
876         is_playing = ((shell->priv->connected)
877                       && ((ario_server_get_current_state () == ARIO_STATE_PLAY)
878                           || (ario_server_get_current_state () == ARIO_STATE_PAUSE)));
879 
880         /* Set lyrics entry sensitivty */
881         action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()),
882                                               "view-lyrics");
883         g_simple_action_set_enabled (G_SIMPLE_ACTION (action), is_playing);
884 
885         /* Set cover selection entry sensitivty */
886         action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()),
887                                               "cover-select");
888         g_simple_action_set_enabled (G_SIMPLE_ACTION (action), is_playing);
889 
890         /* Set similar artists entry sensitivty */
891         action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()),
892                                               "similar-artists");
893         g_simple_action_set_enabled (G_SIMPLE_ACTION (action), is_playing);
894 
895         /* Set similar artists addition entry sensitivty */
896         action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()),
897                                               "add-similar");
898         g_simple_action_set_enabled (G_SIMPLE_ACTION (action), is_playing);
899 }
900 
901 static void
ario_shell_sync_playlist_position(ArioShell * shell)902 ario_shell_sync_playlist_position (ArioShell *shell)
903 {
904         if (!shell->priv->playlist)
905                 return;
906 
907         /* Remove all widgets if playlist is in tabs */
908         int page_num = gtk_notebook_page_num (GTK_NOTEBOOK (shell->priv->sourcemanager), shell->priv->playlist);
909         if (page_num >= 0) {
910                 ario_source_manager_remove (ARIO_SOURCE (shell->priv->playlist));
911                 gtk_container_remove (GTK_CONTAINER (shell->priv->hbox), shell->priv->sourcemanager);
912         }
913 
914         /* Remove all widgets if playlist is in a paned */
915         if (shell->priv->paned) {
916                 /* Save paned position */
917                 ario_conf_set_integer (PREF_VPANED_POSITION,
918                                        gtk_paned_get_position (GTK_PANED (shell->priv->paned)));
919 
920                 gtk_container_remove (GTK_CONTAINER (shell->priv->paned), shell->priv->playlist);
921                 gtk_container_remove (GTK_CONTAINER (shell->priv->paned), shell->priv->sourcemanager);
922                 gtk_container_remove (GTK_CONTAINER (shell->priv->hbox), shell->priv->paned);
923                 shell->priv->paned = NULL;
924         }
925 
926         switch (ario_conf_get_integer (PREF_PLAYLIST_POSITION, PREF_PLAYLIST_POSITION_DEFAULT))
927         {
928         case PLAYLIST_POSITION_BELOW:
929                 /* Create paned (separation between upper part and playlist) */
930                 shell->priv->paned = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
931 
932                 /* Add widgets to paned */
933                 gtk_paned_pack1 (GTK_PANED (shell->priv->paned),
934                                  shell->priv->sourcemanager,
935                                  FALSE, FALSE);
936 
937                 gtk_paned_pack2 (GTK_PANED (shell->priv->paned),
938                                  shell->priv->playlist,
939                                  TRUE, FALSE);
940 
941                 gtk_box_pack_start (GTK_BOX (shell->priv->hbox),
942                                     shell->priv->paned,
943                                     TRUE, TRUE, 0);
944                 gtk_widget_show_all (shell->priv->paned);
945 
946                 /* Synchronize paned with preferences */
947                 ario_shell_sync_paned (shell);
948                 break;
949         case PLAYLIST_POSITION_RIGHT:
950                 /* Create paned (separation between left part and playlist) */
951                 shell->priv->paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
952 
953                 /* Add widgets to paned */
954                 gtk_paned_pack1 (GTK_PANED (shell->priv->paned),
955                                  shell->priv->sourcemanager,
956                                  FALSE, FALSE);
957 
958                 gtk_paned_pack2 (GTK_PANED (shell->priv->paned),
959                                  shell->priv->playlist,
960                                  TRUE, FALSE);
961 
962                 gtk_box_pack_start (GTK_BOX (shell->priv->hbox),
963                                     shell->priv->paned,
964                                     TRUE, TRUE, 0);
965                 gtk_widget_show_all (shell->priv->paned);
966 
967                 /* Synchronize paned with preferences */
968                 ario_shell_sync_paned (shell);
969                 break;
970         case PLAYLIST_POSITION_INSIDE:
971         default:
972                 /* Add playlist to tabs */
973                 ario_source_manager_append (ARIO_SOURCE (shell->priv->playlist));
974                 gtk_box_pack_start (GTK_BOX (shell->priv->hbox),
975                                     shell->priv->sourcemanager,
976                                     TRUE, TRUE, 0);
977                 gtk_widget_show_all (shell->priv->sourcemanager);
978                 break;
979         }
980 }
981 
982 static void
ario_shell_firstlaunch_delete_cb(ArioFirstlaunch * firstlaunch,ArioShell * shell)983 ario_shell_firstlaunch_delete_cb (ArioFirstlaunch *firstlaunch,
984                                   ArioShell *shell)
985 {
986         ARIO_LOG_FUNCTION_START;
987         /* Show main window when first launch assistant is deleted */
988         ario_shell_show (shell, FALSE);
989 }
990 
991 static void
ario_shell_view_statusbar_changed_cb(GSimpleAction * action,GVariant * parameter,gpointer data)992 ario_shell_view_statusbar_changed_cb (GSimpleAction *action,
993                                       GVariant *parameter,
994                                       gpointer data)
995 {
996         ARIO_LOG_FUNCTION_START;
997         GVariant *old_state, *new_state;
998         ArioShell *shell = ARIO_SHELL (data);
999 
1000         old_state = g_action_get_state (G_ACTION (action));
1001         new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
1002         g_simple_action_set_state (action, new_state);
1003         g_variant_unref (old_state);
1004 
1005         shell->priv->statusbar_hidden = !g_variant_get_boolean (new_state);
1006         ario_conf_set_boolean (PREF_STATUSBAR_HIDDEN, shell->priv->statusbar_hidden);
1007 
1008         /* Synchronize status bar visibility */
1009         ario_shell_sync_statusbar_visibility (shell);
1010 }
1011 
1012 static void
ario_shell_view_upperpart_changed_cb(GSimpleAction * action,GVariant * parameter,gpointer data)1013 ario_shell_view_upperpart_changed_cb (GSimpleAction *action,
1014                                       GVariant *parameter,
1015                                       gpointer data)
1016 {
1017         ARIO_LOG_FUNCTION_START;
1018         GVariant *old_state, *new_state;
1019         ArioShell *shell = ARIO_SHELL (data);
1020 
1021         old_state = g_action_get_state (G_ACTION (action));
1022         new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
1023         g_simple_action_set_state (action, new_state);
1024         g_variant_unref (old_state);
1025 
1026         shell->priv->upperpart_hidden = !g_variant_get_boolean (new_state);
1027         ario_conf_set_boolean (PREF_UPPERPART_HIDDEN, shell->priv->upperpart_hidden);
1028 
1029         /* Synchronize upper part visibility */
1030         ario_shell_sync_upperpart_visibility (shell);
1031 }
1032 
1033 static void
ario_shell_view_playlist_changed_cb(GSimpleAction * action,GVariant * parameter,gpointer data)1034 ario_shell_view_playlist_changed_cb (GSimpleAction *action,
1035                                      GVariant *parameter,
1036                                      gpointer data)
1037 {
1038         ARIO_LOG_FUNCTION_START;
1039         GVariant *old_state, *new_state;
1040         ArioShell *shell = ARIO_SHELL (data);
1041 
1042         old_state = g_action_get_state (G_ACTION (action));
1043         new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
1044         g_simple_action_set_state (action, new_state);
1045         g_variant_unref (old_state);
1046 
1047         shell->priv->playlist_hidden = !g_variant_get_boolean (new_state);
1048         ario_conf_set_boolean (PREF_PLAYLIST_HIDDEN, shell->priv->playlist_hidden);
1049 
1050         /* Synchronize playlist visibility */
1051         ario_shell_sync_playlist_visibility (shell);
1052 }
1053 
1054 static void
ario_shell_sync_statusbar_visibility(ArioShell * shell)1055 ario_shell_sync_statusbar_visibility (ArioShell *shell)
1056 {
1057         ARIO_LOG_FUNCTION_START;
1058         if (shell->priv->statusbar_hidden)
1059                 gtk_widget_hide (GTK_WIDGET (shell->priv->status_bar));
1060         else
1061                 gtk_widget_show (GTK_WIDGET (shell->priv->status_bar));
1062 }
1063 
1064 static void
ario_shell_sync_upperpart_visibility(ArioShell * shell)1065 ario_shell_sync_upperpart_visibility (ArioShell *shell)
1066 {
1067         ARIO_LOG_FUNCTION_START;
1068         if (shell->priv->upperpart_hidden)
1069                 gtk_widget_hide (GTK_WIDGET (shell->priv->sourcemanager));
1070         else
1071                 gtk_widget_show (GTK_WIDGET (shell->priv->sourcemanager));
1072 }
1073 
1074 static void
ario_shell_sync_playlist_visibility(ArioShell * shell)1075 ario_shell_sync_playlist_visibility (ArioShell *shell)
1076 {
1077         ARIO_LOG_FUNCTION_START;
1078         if (shell->priv->playlist_hidden)
1079                 gtk_widget_hide (GTK_WIDGET (shell->priv->playlist));
1080         else
1081                 gtk_widget_show (GTK_WIDGET (shell->priv->playlist));
1082 }
1083 
1084 static gboolean
ario_shell_plugins_window_delete_cb(GtkWidget * window,GdkEventAny * event,gpointer data)1085 ario_shell_plugins_window_delete_cb (GtkWidget *window,
1086                                      GdkEventAny *event,
1087                                      gpointer data)
1088 {
1089         ARIO_LOG_FUNCTION_START;
1090         gtk_widget_destroy (GTK_WIDGET (window));
1091         return TRUE;
1092 }
1093 
1094 static void
ario_shell_plugins_response_cb(GtkDialog * dialog,int response_id,gpointer data)1095 ario_shell_plugins_response_cb (GtkDialog *dialog,
1096                                 int response_id,
1097                                 gpointer data)
1098 {
1099         ARIO_LOG_FUNCTION_START;
1100         gtk_widget_destroy (GTK_WIDGET (dialog));
1101 }
1102 
1103 static void
ario_shell_cmd_plugins(GSimpleAction * action,GVariant * parameter,gpointer data)1104 ario_shell_cmd_plugins (GSimpleAction *action,
1105                         GVariant *parameter,
1106                         gpointer data)
1107 {
1108         ARIO_LOG_FUNCTION_START;
1109         GtkWidget *window;
1110         GtkWidget *manager;
1111         ArioShell *shell = ARIO_SHELL (data);
1112 
1113         /* Create plugins configuration dialog window */
1114         window = gtk_dialog_new_with_buttons (_("Configure Plugins"),
1115                                               GTK_WINDOW (shell),
1116                                               GTK_DIALOG_DESTROY_WITH_PARENT,
1117                                               _("_Close"),
1118                                               GTK_RESPONSE_CLOSE,
1119                                               NULL);
1120         gtk_container_set_border_width (GTK_CONTAINER (window), 5);
1121         gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))), 2);
1122 
1123         /* Connect signals for window destruction */
1124         g_signal_connect (window,
1125                           "delete_event",
1126                           G_CALLBACK (ario_shell_plugins_window_delete_cb),
1127                           NULL);
1128         g_signal_connect (window,
1129                           "response",
1130                           G_CALLBACK (ario_shell_plugins_response_cb),
1131                           NULL);
1132 
1133         manager = ario_plugin_manager_new ();
1134         gtk_widget_show_all (GTK_WIDGET (manager));
1135         gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (window))),
1136                            manager);
1137 
1138         gtk_window_set_default_size (GTK_WINDOW (window), 300, 350);
1139 
1140         gtk_window_present (GTK_WINDOW (window));
1141 }
1142 
1143 static void
ario_shell_playlist_position_changed_cb(guint notification_id,ArioShell * shell)1144 ario_shell_playlist_position_changed_cb (guint notification_id,
1145                                          ArioShell *shell)
1146 {
1147         ario_shell_sync_playlist_position(shell);
1148 }
1149