pnmixer
Volume mixer for the system tray
ui-popup-menu.c
Go to the documentation of this file.
1 /* ui-popup-menu.c
2  * PNmixer is written by Nick Lanham, a fork of OBmixer
3  * which was programmed by Lee Ferrett, derived
4  * from the program "AbsVolume" by Paul Sherman
5  * This program is free software; you can redistribute
6  * it and/or modify it under the terms of the GNU General
7  * Public License v3. source code is available at
8  * <http://github.com/nicklan/pnmixer>
9  */
10 
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 
21 #include <stdlib.h>
22 #include <glib.h>
23 #include <gtk/gtk.h>
24 
25 #include "audio.h"
26 #include "support-log.h"
27 #include "support-intl.h"
28 #include "support-ui.h"
29 #include "ui-popup-menu.h"
30 #include "ui-about-dialog.h"
31 
32 #include "main.h"
33 
34 #ifdef WITH_GTK3
35 #define POPUP_MENU_UI_FILE "popup-menu-gtk3.glade"
36 #else
37 #define POPUP_MENU_UI_FILE "popup-menu-gtk2.glade"
38 #endif
39 
40 /* Helpers */
41 
42 #ifdef WITH_GTK3
43 
44 /* Updates the mute checkbox according to the current audio state. */
45 static void
46 update_mute_check(GtkToggleButton *mute_check, gboolean has_mute, gboolean muted)
47 {
48  /* On Gtk3 version, we listen for the signal sent by the GtkMenuItem.
49  * So, when we change the value of the GtkToggleButton, we don't have
50  * to block the signal handlers, since there's nobody listening to the
51  * GtkToggleButton anyway.
52  */
53  if (has_mute == FALSE) {
54  gtk_toggle_button_set_active(mute_check, TRUE);
55  gtk_widget_set_sensitive(GTK_WIDGET(mute_check), FALSE);
56  gtk_widget_set_tooltip_text(GTK_WIDGET(mute_check),
57  _("Soundcard has no mute switch"));
58  } else {
59  gtk_toggle_button_set_active(mute_check, muted);
60  gtk_widget_set_tooltip_text(GTK_WIDGET(mute_check), NULL);
61  }
62 }
63 
64 #else
65 
66 /* Updates the mute item according to the current audio state. */
67 static void
68 update_mute_item(GtkCheckMenuItem *mute_item, GCallback handler_func,
69  gpointer handler_data, gboolean has_mute, gboolean muted)
70 {
71  /* On Gtk2 version, we must block the signals sent by the GtkCheckMenuItem
72  * before we update it manually.
73  */
74  gint n_blocked;
75 
76  n_blocked = g_signal_handlers_block_by_func
77  (G_OBJECT(mute_item), DATA_PTR(handler_func), handler_data);
78  g_assert(n_blocked == 1);
79 
80  if (has_mute == FALSE) {
81  gtk_check_menu_item_set_active(mute_item, TRUE);
82  gtk_widget_set_sensitive(GTK_WIDGET(mute_item), FALSE);
83  gtk_widget_set_tooltip_text(GTK_WIDGET(mute_item),
84  _("Soundcard has no mute switch"));
85  } else {
86  gtk_check_menu_item_set_active(mute_item, muted);
87  gtk_widget_set_tooltip_text(GTK_WIDGET(mute_item), NULL);
88  }
89 
90  g_signal_handlers_unblock_by_func
91  (G_OBJECT(mute_item), DATA_PTR(handler_func), handler_data);
92 }
93 
94 #endif
95 
96 /* Public functions & signal handlers */
97 
98 struct popup_menu {
99  /* Audio system */
101  /* Widgets */
102  GtkWidget *menu_window;
103  GtkWidget *menu;
104 #ifdef WITH_GTK3
105  GtkWidget *mute_check;
106 #else
107  GtkWidget *mute_item;
108 #endif
109 };
110 
117 void
118 #ifdef WITH_GTK3
119 on_mute_item_activate(G_GNUC_UNUSED GtkMenuItem *item,
120 #else
121 on_mute_item_activate(G_GNUC_UNUSED GtkCheckMenuItem *item,
122 #endif
123  PopupMenu *menu)
124 {
125  audio_toggle_mute(menu->audio, AUDIO_USER_POPUP);
126 }
127 
134 void
135 on_mixer_item_activate(G_GNUC_UNUSED GtkMenuItem *item,
136  G_GNUC_UNUSED PopupMenu *menu)
137 {
139 }
140 
147 void
148 on_prefs_item_activate(G_GNUC_UNUSED GtkMenuItem *item,
149  G_GNUC_UNUSED PopupMenu *menu)
150 {
152 }
153 
160 void
161 on_reload_item_activate(G_GNUC_UNUSED GtkMenuItem *item,
162  PopupMenu *menu)
163 {
164  audio_reload(menu->audio);
165 }
166 
173 void
174 on_about_item_activate(G_GNUC_UNUSED GtkMenuItem *item,
175  G_GNUC_UNUSED PopupMenu *menu)
176 {
178 }
179 
187 static void
188 on_audio_changed(G_GNUC_UNUSED Audio *audio, AudioEvent *event, gpointer data)
189 {
190  PopupMenu *menu = (PopupMenu *) data;
191 
192 #ifdef WITH_GTK3
193  update_mute_check(GTK_TOGGLE_BUTTON(menu->mute_check),
194  event->has_mute, event->muted);
195 #else
196  update_mute_item(GTK_CHECK_MENU_ITEM(menu->mute_item),
197  G_CALLBACK(on_mute_item_activate),
198  menu, event->has_mute, event->muted);
199 #endif
200 }
201 
207 GtkWindow *
209 {
210  return GTK_WINDOW(menu->menu_window);
211 }
212 
224 void
226  GTK_3_22_UNUSED GtkMenuPositionFunc func,
227  GTK_3_22_UNUSED gpointer data,
228  GTK_3_22_UNUSED guint button,
229  GTK_3_22_UNUSED guint activate_time)
230 {
231 #if GTK_CHECK_VERSION(3,22,0)
232  gtk_menu_popup_at_pointer(GTK_MENU(menu->menu), NULL);
233 #else
234  gtk_menu_popup(GTK_MENU(menu->menu), NULL, NULL,
235  func, data, button, activate_time);
236 #endif
237 }
238 
244 void
246 {
247  DEBUG("Destroying");
248 
250  gtk_widget_destroy(menu->menu_window);
251  g_free(menu);
252 }
253 
260 PopupMenu *
262 {
263  gchar *uifile;
264  GtkBuilder *builder;
265  PopupMenu *menu;
266 
267  menu = g_new0(PopupMenu, 1);
268 
269  /* Build UI file */
271  g_assert(uifile);
272 
273  DEBUG("Building from ui file '%s'", uifile);
274  builder = gtk_builder_new_from_file(uifile);
275 
276  /* Save some widgets for later use */
277  assign_gtk_widget(builder, menu, menu_window);
278  assign_gtk_widget(builder, menu, menu);
279 #ifdef WITH_GTK3
280  assign_gtk_widget(builder, menu, mute_check);
281 #else
282  assign_gtk_widget(builder, menu, mute_item);
283 #endif
284 
285 #ifdef WITH_GTK3
286  /* Gtk3 doesn't seem to scale images automatically like Gtk2 did.
287  * If we have a 'broken' icon set that only provides one size of icon
288  * (let's say 128x128), Gtk2 would scale it appropriately, but not Gtk3.
289  * This will result in huge icons in the menu.
290  * If we follow the Gtk3 logic, then we shouldn't do anything to handle that,
291  * and when users report such problem, we tell them to fix the icon theme.
292  * If we want PNMixer to work in as many cases as possible, then we must
293  * handle the broken icon theme and resize the icons by ourself.
294  * We choose the second option here.
295  */
296  GtkRequisition label_req;
297  GtkRequisition image_req;
298  GtkWidget *mute_accellabel;
299  GtkWidget *mixer_image;
300  GtkWidget *prefs_image;
301  GtkWidget *reload_image;
302  GtkWidget *about_image;
303  GtkWidget *quit_image;
304 
305  mute_accellabel = gtk_builder_get_widget(builder, "mute_accellabel");
306  mixer_image = gtk_builder_get_widget(builder, "mixer_image");
307  prefs_image = gtk_builder_get_widget(builder, "prefs_image");
308  reload_image = gtk_builder_get_widget(builder, "reload_image");
309  about_image = gtk_builder_get_widget(builder, "about_image");
310  quit_image = gtk_builder_get_widget(builder, "quit_image");
311 
312  gtk_widget_get_preferred_size(mute_accellabel, &label_req, NULL);
313  gtk_widget_get_preferred_size(mixer_image, &image_req, NULL);
314 
315  /* We only care about height. We want the image to stick to the text height. */
316  if (image_req.height > (label_req.height + 1)) {
317  gint new_height = label_req.height;
318 
319  if (new_height % 2)
320  new_height++; // make it even
321 
322  DEBUG("Gtk3 workaround: resizing images from %dpx to %dpx",
323  image_req.height, new_height);
324  gtk_image_set_pixel_size(GTK_IMAGE(mixer_image), new_height);
325  gtk_image_set_pixel_size(GTK_IMAGE(prefs_image), new_height);
326  gtk_image_set_pixel_size(GTK_IMAGE(reload_image), new_height);
327  gtk_image_set_pixel_size(GTK_IMAGE(about_image), new_height);
328  gtk_image_set_pixel_size(GTK_IMAGE(quit_image), new_height);
329  }
330 #endif
331 
332  /* Connect ui signal handlers */
333  gtk_builder_connect_signals(builder, menu);
334 
335  /* Connect audio signal handlers */
336  menu->audio = audio;
338 
339  /* Cleanup */
340  g_object_unref(builder);
341  g_free(uifile);
342 
343  return menu;
344 }
GtkBuilder * gtk_builder_new_from_file(const gchar *filename)
Definition: support-ui.c:25
#define _(String)
Definition: support-intl.h:44
Internationalization support.
Logging support.
Header for audio.c.
#define DATA_PTR(ptr)
Definition: support-ui.h:52
static void update_mute_item(GtkCheckMenuItem *mute_item, GCallback handler_func, gpointer handler_data, gboolean has_mute, gboolean muted)
Definition: ui-popup-menu.c:68
GtkWidget * menu
void on_mute_item_activate(G_GNUC_UNUSED GtkCheckMenuItem *item, PopupMenu *menu)
GtkWidget * mute_item
static void on_audio_changed(G_GNUC_UNUSED Audio *audio, AudioEvent *event, gpointer data)
void on_mixer_item_activate(G_GNUC_UNUSED GtkMenuItem *item, G_GNUC_UNUSED PopupMenu *menu)
Header for support-ui.c.
void audio_signals_disconnect(Audio *audio, AudioCallback callback, gpointer data)
Definition: audio.c:318
#define assign_gtk_widget(builder, container, name)
Definition: support-ui.h:75
PopupMenu * popup_menu_create(Audio *audio)
void audio_toggle_mute(Audio *audio, AudioUser user)
Definition: audio.c:412
void audio_signals_connect(Audio *audio, AudioCallback callback, gpointer data)
Definition: audio.c:337
gboolean muted
Definition: audio.h:79
header for ui-about-dialog.c.
#define POPUP_MENU_UI_FILE
Definition: ui-popup-menu.c:37
#define DEBUG(...)
Definition: support-log.h:38
Header for main.c.
gboolean has_mute
Definition: audio.h:78
gchar * get_ui_file(const char *filename)
Definition: support-ui.c:58
Header for ui-popup-menu.c.
void run_mixer_command(void)
Definition: main.c:79
Definition: audio.c:198
static void update_mute_check(GtkToggleButton *mute_check, GCallback handler_func, gpointer handler_data, gboolean has_mute, gboolean muted)
void on_about_item_activate(G_GNUC_UNUSED GtkMenuItem *item, G_GNUC_UNUSED PopupMenu *menu)
void popup_menu_show(PopupMenu *menu, GTK_3_22_UNUSED GtkMenuPositionFunc func, GTK_3_22_UNUSED gpointer data, GTK_3_22_UNUSED guint button, GTK_3_22_UNUSED guint activate_time)
void on_reload_item_activate(G_GNUC_UNUSED GtkMenuItem *item, PopupMenu *menu)
#define gtk_builder_get_widget(builder, name)
Definition: support-ui.h:58
void run_prefs_dialog(void)
Definition: main.c:151
GtkWindow * popup_menu_get_window(PopupMenu *menu)
GtkWidget * menu_window
#define GTK_3_22_UNUSED
Definition: support-ui.h:37
void audio_reload(Audio *audio)
Definition: audio.c:669
void popup_menu_destroy(PopupMenu *menu)
void run_about_dialog(void)
Definition: main.c:168
Audio * audio
void on_prefs_item_activate(G_GNUC_UNUSED GtkMenuItem *item, G_GNUC_UNUSED PopupMenu *menu)