1 /*************************************************************************/
2 /* Copyright (C) 2007-2009 sujith <m.sujith@gmail.com> */
3 /* Copyright (C) 2009-2013 matias <mati86dl@gmail.com> */
4 /* */
5 /* This program is free software: you can redistribute it and/or modify */
6 /* it under the terms of the GNU General Public License as published by */
7 /* the Free Software Foundation, either version 3 of the License, or */
8 /* (at your option) any later version. */
9 /* */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
13 /* GNU General Public License for more details. */
14 /* */
15 /* You should have received a copy of the GNU General Public License */
16 /* along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /*************************************************************************/
18
19 #if HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include "pragha-statusicon.h"
24
25 #if defined(GETTEXT_PACKAGE)
26 #include <glib/gi18n-lib.h>
27 #else
28 #include <glib/gi18n.h>
29 #endif
30
31 #include "pragha-playback.h"
32 #include "pragha-menubar.h"
33 #include "pragha-window.h"
34 #include "pragha.h"
35
36 struct _PraghaStatusIcon {
37 GtkStatusIcon __parent__;
38
39 PraghaApplication *pragha;
40
41 GtkUIManager *ui_manager;
42 };
43
44 G_DEFINE_TYPE(PraghaStatusIcon, pragha_status_icon, GTK_TYPE_STATUS_ICON)
45
46 static void systray_about_action (GtkAction *action, PraghaStatusIcon *status_icon);
47 static void systray_open_file_action (GtkAction *action, PraghaStatusIcon *status_icon);
48 static void systray_add_location_action (GtkAction *action, PraghaStatusIcon *status_icon);
49 static void systray_play_pause_action (GtkAction *action, PraghaStatusIcon *status_icon);
50 static void systray_stop_action (GtkAction *action, PraghaStatusIcon *status_icon);
51 static void systray_prev_action (GtkAction *action, PraghaStatusIcon *status_icon);
52 static void systray_next_action (GtkAction *action, PraghaStatusIcon *status_icon);
53 static void systray_edit_action (GtkAction *action, PraghaStatusIcon *status_icon);
54 static void systray_quit (GtkAction *action, PraghaStatusIcon *status_icon);
55
56 static const gchar *systray_menu_xml =
57 "<ui> \
58 <popup> \
59 <menuitem action=\"About\"/> \
60 <separator/> \
61 <menuitem action=\"Add files\"/> \
62 <menuitem action=\"Add location\"/> \
63 <placeholder name=\"pragha-append-music-placeholder\"/> \
64 <separator/> \
65 <menuitem action=\"Prev\"/> \
66 <menuitem action=\"Play_Pause\"/> \
67 <menuitem action=\"Stop\"/> \
68 <menuitem action=\"Next\"/> \
69 <separator/> \
70 <menuitem action=\"Edit tags\"/> \
71 <separator/> \
72 <menuitem action=\"Quit\"/> \
73 </popup> \
74 </ui>";
75
76 static const GtkActionEntry systray_menu_aentries[] = {
77 {"About", "help-about", N_("About"),
78 "", NULL, G_CALLBACK(systray_about_action)},
79 {"Add files", "document-open", N_("_Add files"),
80 "", NULL, G_CALLBACK(systray_open_file_action)},
81 {"Add location", "network-workgroup", N_("Add _location"),
82 "", "Add a no local stream", G_CALLBACK(systray_add_location_action)},
83 {"Prev", "media-skip-backward", N_("Previous track"),
84 "", "Previous track", G_CALLBACK(systray_prev_action)},
85 {"Play_Pause", "media-playback-start", N_("Play / Pause"),
86 "", "Play / Pause", G_CALLBACK(systray_play_pause_action)},
87 {"Stop", "media-playback-stop", N_("Stop"),
88 "", "Stop", G_CALLBACK(systray_stop_action)},
89 {"Next", "media-skip-forward", N_("Next track"),
90 "", "Next track", G_CALLBACK(systray_next_action)},
91 {"Edit tags", NULL, N_("Edit track information"),
92 "", "Edit information of current track", G_CALLBACK(systray_edit_action)},
93 {"Quit", "application-exit", N_("_Quit"),
94 "", "Quit", G_CALLBACK(systray_quit)}
95 };
96
97 gint
pragha_systray_append_plugin_action(PraghaStatusIcon * status_icon,GtkActionGroup * action_group,const gchar * menu_xml)98 pragha_systray_append_plugin_action (PraghaStatusIcon *status_icon,
99 GtkActionGroup *action_group,
100 const gchar *menu_xml)
101 {
102 GError *error = NULL;
103 gint merge_id;
104
105 gtk_ui_manager_insert_action_group (status_icon->ui_manager, action_group, -1);
106
107 merge_id = gtk_ui_manager_add_ui_from_string (status_icon->ui_manager,
108 menu_xml,
109 -1,
110 &error);
111
112 if (error) {
113 g_warning ("Adding plugin to menubar: %s", error->message);
114 g_error_free (error);
115 }
116
117 return merge_id;
118 }
119
120 void
pragha_systray_remove_plugin_action(PraghaStatusIcon * status_icon,GtkActionGroup * action_group,gint merge_id)121 pragha_systray_remove_plugin_action (PraghaStatusIcon *status_icon,
122 GtkActionGroup *action_group,
123 gint merge_id)
124 {
125 gtk_ui_manager_remove_ui (status_icon->ui_manager, merge_id);
126 gtk_ui_manager_remove_action_group (status_icon->ui_manager, action_group);
127 g_object_unref (action_group);
128 }
129
130 /*
131 * Status Icon
132 */
133
134 static gboolean
status_icon_clicked(GtkWidget * widget,GdkEventButton * event,PraghaStatusIcon * status_icon)135 status_icon_clicked (GtkWidget *widget, GdkEventButton *event, PraghaStatusIcon *status_icon)
136 {
137 GtkWidget *popup_menu;
138
139 switch (event->button)
140 {
141 case 1:
142 pragha_window_toggle_state (status_icon->pragha, FALSE);
143 break;
144 case 2:
145 pragha_playback_play_pause_resume (status_icon->pragha);
146 break;
147 case 3:
148 popup_menu = gtk_ui_manager_get_widget(status_icon->ui_manager, "/popup");
149 gtk_menu_popup (GTK_MENU(popup_menu), NULL, NULL, NULL, NULL,
150 event->button, gtk_get_current_event_time ());
151 default: break;
152 }
153
154 return TRUE;
155 }
156
157 static gboolean
status_get_tooltip_cb(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,PraghaStatusIcon * status_icon)158 status_get_tooltip_cb (GtkWidget *widget,
159 gint x,
160 gint y,
161 gboolean keyboard_mode,
162 GtkTooltip *tooltip,
163 PraghaStatusIcon *status_icon)
164 {
165 PraghaBackend *backend;
166 PraghaToolbar *toolbar;
167 PraghaMusicobject *mobj;
168 gchar *markup_text;
169
170 toolbar = pragha_application_get_toolbar (status_icon->pragha);
171
172 backend = pragha_application_get_backend (status_icon->pragha);
173 if (pragha_backend_get_state (backend) == ST_STOPPED)
174 markup_text = g_strdup_printf("%s", _("<b>Not playing</b>"));
175 else {
176 mobj = pragha_backend_get_musicobject (backend);
177
178 markup_text = g_markup_printf_escaped ("<b>%s</b>: %s\n<b>%s</b>: %s\n<b>%s</b>: %s\n<b>%s</b>: %s / %s",
179 _("Title"), pragha_musicobject_get_title (mobj),
180 _("Artist"), pragha_musicobject_get_artist (mobj),
181 _("Album"), pragha_musicobject_get_album (mobj),
182 _("Length"), pragha_toolbar_get_progress_text (toolbar),
183 pragha_toolbar_get_length_text (toolbar));
184 }
185
186 gtk_tooltip_set_markup (tooltip, markup_text);
187 gtk_tooltip_set_icon (tooltip,
188 pragha_album_art_get_pixbuf(pragha_toolbar_get_album_art(toolbar)));
189
190 g_free(markup_text);
191
192 return TRUE;
193 }
194
195 static void
systray_volume_scroll(GtkWidget * widget,GdkEventScroll * event,PraghaStatusIcon * status_icon)196 systray_volume_scroll (GtkWidget *widget, GdkEventScroll *event, PraghaStatusIcon *status_icon)
197 {
198 PraghaBackend *backend;
199
200 if (event->type != GDK_SCROLL)
201 return;
202
203 backend = pragha_application_get_backend (status_icon->pragha);
204
205 switch (event->direction){
206 case GDK_SCROLL_UP:
207 pragha_backend_set_delta_volume (backend, +0.02);
208 break;
209 case GDK_SCROLL_DOWN:
210 pragha_backend_set_delta_volume (backend, -0.02);
211 break;
212 default:
213 break;
214 }
215 }
216
217 static void
systray_about_action(GtkAction * action,PraghaStatusIcon * status_icon)218 systray_about_action (GtkAction *action, PraghaStatusIcon *status_icon)
219 {
220 pragha_application_about_dialog (status_icon->pragha);
221 }
222
223 static void
systray_open_file_action(GtkAction * action,PraghaStatusIcon * status_icon)224 systray_open_file_action (GtkAction *action, PraghaStatusIcon *status_icon)
225 {
226 pragha_application_open_files (status_icon->pragha);
227 }
228
229 static void
systray_add_location_action(GtkAction * action,PraghaStatusIcon * status_icon)230 systray_add_location_action (GtkAction *action, PraghaStatusIcon *status_icon)
231 {
232 pragha_application_add_location (status_icon->pragha);
233 }
234
235 static void
systray_play_pause_action(GtkAction * action,PraghaStatusIcon * status_icon)236 systray_play_pause_action (GtkAction *action, PraghaStatusIcon *status_icon)
237 {
238 PraghaBackend *backend = pragha_application_get_backend (status_icon->pragha);
239 if (pragha_backend_emitted_error (backend) == FALSE)
240 pragha_playback_play_pause_resume(status_icon->pragha);
241 }
242
243 static void
systray_stop_action(GtkAction * action,PraghaStatusIcon * status_icon)244 systray_stop_action (GtkAction *action, PraghaStatusIcon *status_icon)
245 {
246 PraghaBackend *backend = pragha_application_get_backend (status_icon->pragha);
247 if (pragha_backend_emitted_error (backend) == FALSE)
248 pragha_playback_stop(status_icon->pragha);
249 }
250
251 static void
systray_prev_action(GtkAction * action,PraghaStatusIcon * status_icon)252 systray_prev_action (GtkAction *action, PraghaStatusIcon *status_icon)
253 {
254 PraghaBackend *backend = pragha_application_get_backend (status_icon->pragha);
255 if (pragha_backend_emitted_error (backend) == FALSE)
256 pragha_playback_prev_track(status_icon->pragha);
257 }
258
259 static void
systray_next_action(GtkAction * action,PraghaStatusIcon * status_icon)260 systray_next_action (GtkAction *action, PraghaStatusIcon *status_icon)
261 {
262 PraghaBackend *backend = pragha_application_get_backend (status_icon->pragha);
263 if (pragha_backend_emitted_error (backend) == FALSE)
264 pragha_playback_next_track(status_icon->pragha);
265 }
266
267 static void
systray_edit_action(GtkAction * action,PraghaStatusIcon * status_icon)268 systray_edit_action (GtkAction *action, PraghaStatusIcon *status_icon)
269 {
270 PraghaBackend *backend = pragha_application_get_backend (status_icon->pragha);
271 if (pragha_backend_emitted_error (backend) == FALSE)
272 pragha_playback_edit_current_track (status_icon->pragha);
273 }
274
275 static void
systray_quit(GtkAction * action,PraghaStatusIcon * status_icon)276 systray_quit (GtkAction *action, PraghaStatusIcon *status_icon)
277 {
278 pragha_application_quit (status_icon->pragha);
279 }
280
281 static void
pragha_status_icon_update_state(PraghaBackend * backend,GParamSpec * pspec,PraghaStatusIcon * status_icon)282 pragha_status_icon_update_state (PraghaBackend *backend, GParamSpec *pspec, PraghaStatusIcon *status_icon)
283 {
284 GtkAction *action;
285 PraghaBackendState state = 0;
286
287 state = pragha_backend_get_state (backend);
288
289 gboolean playing = (state != ST_STOPPED);
290
291 action = gtk_ui_manager_get_action (status_icon->ui_manager, "/popup/Prev");
292 gtk_action_set_sensitive (GTK_ACTION (action), playing);
293
294 action = gtk_ui_manager_get_action (status_icon->ui_manager, "/popup/Stop");
295 gtk_action_set_sensitive (GTK_ACTION (action), playing);
296
297 action = gtk_ui_manager_get_action (status_icon->ui_manager, "/popup/Next");
298 gtk_action_set_sensitive (GTK_ACTION (action), playing);
299
300 action = gtk_ui_manager_get_action (status_icon->ui_manager, "/popup/Edit tags");
301 gtk_action_set_sensitive (GTK_ACTION (action), playing);
302 }
303
304 static void
pragha_status_icon_set_application(PraghaStatusIcon * status_icon,PraghaApplication * pragha)305 pragha_status_icon_set_application (PraghaStatusIcon *status_icon, PraghaApplication *pragha)
306 {
307 PraghaPreferences *preferences;
308 GtkActionGroup *actions;
309
310 const GBindingFlags binding_flags =
311 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL;
312
313 status_icon->pragha = pragha;
314
315 gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON(status_icon), "pragha");
316
317 actions = gtk_action_group_new ("Systray Actions");
318 gtk_action_group_set_translation_domain (actions, GETTEXT_PACKAGE);
319
320 gtk_action_group_add_actions (actions,
321 systray_menu_aentries,
322 G_N_ELEMENTS(systray_menu_aentries),
323 (gpointer)status_icon);
324 gtk_ui_manager_insert_action_group (status_icon->ui_manager, actions, 0);
325
326 preferences = pragha_application_get_preferences (pragha);
327 g_object_bind_property (preferences, "show-status-icon",
328 status_icon, "visible", binding_flags);
329
330 g_signal_connect (pragha_application_get_backend (pragha), "notify::state",
331 G_CALLBACK (pragha_status_icon_update_state), status_icon);
332 pragha_status_icon_update_state (pragha_application_get_backend (pragha), NULL, status_icon);
333
334 g_object_unref (actions);
335 }
336
337 static void
pragha_status_icon_dispose(GObject * object)338 pragha_status_icon_dispose (GObject *object)
339 {
340 PraghaStatusIcon *status_icon = PRAGHA_STATUS_ICON(object);
341
342 if (status_icon->ui_manager) {
343 g_object_unref (status_icon->ui_manager);
344 status_icon->ui_manager = NULL;
345 }
346
347 (*G_OBJECT_CLASS (pragha_status_icon_parent_class)->dispose) (object);
348 }
349
350 static void
pragha_status_icon_class_init(PraghaStatusIconClass * klass)351 pragha_status_icon_class_init (PraghaStatusIconClass *klass)
352 {
353 GObjectClass *gobject_class;
354
355 gobject_class = G_OBJECT_CLASS (klass);
356 gobject_class->dispose = pragha_status_icon_dispose;
357 }
358
359 static void
pragha_status_icon_init(PraghaStatusIcon * status_icon)360 pragha_status_icon_init (PraghaStatusIcon *status_icon)
361 {
362 GError *error = NULL;
363
364 status_icon->ui_manager = gtk_ui_manager_new();
365 if (!gtk_ui_manager_add_ui_from_string (status_icon->ui_manager, systray_menu_xml, -1, &error))
366 g_critical("Unable to create systray menu, err : %s", error->message);
367
368 g_signal_connect (status_icon, "button-press-event",
369 G_CALLBACK (status_icon_clicked), status_icon);
370 g_signal_connect (status_icon, "scroll_event",
371 G_CALLBACK(systray_volume_scroll), status_icon);
372
373 g_object_set (G_OBJECT(status_icon), "has-tooltip", TRUE, NULL);
374 g_signal_connect (G_OBJECT(status_icon), "query-tooltip",
375 G_CALLBACK(status_get_tooltip_cb), status_icon);
376 }
377
378 PraghaStatusIcon *
pragha_status_icon_new(PraghaApplication * pragha)379 pragha_status_icon_new (PraghaApplication *pragha)
380 {
381 PraghaStatusIcon *status_icon;
382
383 status_icon = g_object_new (PRAGHA_TYPE_STATUS_ICON, NULL);
384
385 pragha_status_icon_set_application (status_icon, pragha);
386
387 return status_icon;
388 }
389