You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

462 lines
17 KiB

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
  2. *
  3. * Copyright (C) 2009 Bastien Nocera
  4. * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  19. *
  20. */
  21. #include <glib.h>
  22. #include <glib/gi18n.h>
  23. #include <glib-object.h>
  24. #include <gtk/gtk.h>
  25. #include <libmatemixer/matemixer.h>
  26. #include "gvc-combo-box.h"
  27. #define GVC_COMBO_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_COMBO_BOX, GvcComboBoxPrivate))
  28. struct _GvcComboBoxPrivate
  29. {
  30. GtkWidget *drop_box;
  31. GtkWidget *start_box;
  32. GtkWidget *end_box;
  33. GtkWidget *label;
  34. GtkWidget *button;
  35. GtkTreeModel *model;
  36. GtkWidget *combobox;
  37. MateMixerSwitch *swtch;
  38. };
  39. enum {
  40. COL_NAME,
  41. COL_HUMAN_NAME,
  42. NUM_COLS
  43. };
  44. enum {
  45. CHANGING,
  46. BUTTON_CLICKED,
  47. LAST_SIGNAL
  48. };
  49. static guint signals[LAST_SIGNAL] = { 0, };
  50. enum {
  51. PROP_0,
  52. PROP_SWITCH,
  53. PROP_LABEL,
  54. PROP_SHOW_BUTTON,
  55. PROP_BUTTON_LABEL,
  56. N_PROPERTIES
  57. };
  58. static GParamSpec *properties[N_PROPERTIES] = { NULL, };
  59. static void gvc_combo_box_class_init (GvcComboBoxClass *klass);
  60. static void gvc_combo_box_init (GvcComboBox *combo);
  61. static void gvc_combo_box_dispose (GObject *object);
  62. G_DEFINE_TYPE (GvcComboBox, gvc_combo_box, GTK_TYPE_BOX)
  63. MateMixerSwitch *
  64. gvc_combo_box_get_switch (GvcComboBox *combobox)
  65. {
  66. g_return_val_if_fail (GVC_IS_COMBO_BOX (combobox), NULL);
  67. return combobox->priv->swtch;
  68. }
  69. void
  70. gvc_combo_box_set_size_group (GvcComboBox *combobox,
  71. GtkSizeGroup *group,
  72. gboolean symmetric)
  73. {
  74. g_return_if_fail (GVC_IS_COMBO_BOX (combobox));
  75. g_return_if_fail (GTK_IS_SIZE_GROUP (group));
  76. if (group != NULL) {
  77. gtk_size_group_add_widget (group, combobox->priv->start_box);
  78. if (symmetric == TRUE)
  79. gtk_size_group_add_widget (group, combobox->priv->end_box);
  80. }
  81. gtk_widget_queue_draw (GTK_WIDGET (combobox));
  82. }
  83. static void
  84. on_switch_active_option_notify (MateMixerSwitch *swtch,
  85. GParamSpec *pspec,
  86. GvcComboBox *combobox)
  87. {
  88. GtkTreeIter iter;
  89. MateMixerSwitchOption *active;
  90. gboolean cont;
  91. const gchar *name;
  92. active = mate_mixer_switch_get_active_option (swtch);
  93. if G_UNLIKELY (active == NULL) {
  94. g_warn_if_reached ();
  95. return;
  96. }
  97. /* Select the newly activated switch option in the combo box */
  98. name = mate_mixer_switch_option_get_name (active);
  99. cont = gtk_tree_model_get_iter_first (combobox->priv->model, &iter);
  100. while (cont == TRUE) {
  101. gchar *current;
  102. gtk_tree_model_get (combobox->priv->model, &iter,
  103. COL_NAME, &current,
  104. -1);
  105. if (g_strcmp0 (name, current) == 0) {
  106. gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox->priv->combobox), &iter);
  107. g_free (current);
  108. return;
  109. }
  110. g_free (current);
  111. cont = gtk_tree_model_iter_next (combobox->priv->model, &iter);
  112. }
  113. g_warning ("Could not find switch option '%s' in combo box", name);
  114. }
  115. static void
  116. gvc_combo_box_set_switch (GvcComboBox *combobox, MateMixerSwitch *swtch)
  117. {
  118. MateMixerSwitchOption *active;
  119. const GList *options;
  120. g_return_if_fail (GVC_IS_COMBO_BOX (combobox));
  121. g_return_if_fail (MATE_MIXER_IS_SWITCH (swtch));
  122. combobox->priv->swtch = g_object_ref (swtch);
  123. active = mate_mixer_switch_get_active_option (swtch);
  124. options = mate_mixer_switch_list_options (swtch);
  125. while (options != NULL) {
  126. GtkTreeIter iter;
  127. MateMixerSwitchOption *option = MATE_MIXER_SWITCH_OPTION (options->data);
  128. gtk_list_store_insert_with_values (GTK_LIST_STORE (combobox->priv->model),
  129. &iter,
  130. G_MAXINT,
  131. COL_NAME,
  132. mate_mixer_switch_option_get_name (option),
  133. COL_HUMAN_NAME,
  134. mate_mixer_switch_option_get_label (option),
  135. -1);
  136. /* Select the currently active option of the switch */
  137. if (option == active) {
  138. gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox->priv->combobox),
  139. &iter);
  140. }
  141. options = options->next;
  142. }
  143. g_signal_connect (G_OBJECT (swtch),
  144. "notify::active-option",
  145. G_CALLBACK (on_switch_active_option_notify),
  146. combobox);
  147. }
  148. static void
  149. gvc_combo_box_set_property (GObject *object,
  150. guint prop_id,
  151. const GValue *value,
  152. GParamSpec *pspec)
  153. {
  154. GvcComboBox *self = GVC_COMBO_BOX (object);
  155. switch (prop_id) {
  156. case PROP_SWITCH:
  157. gvc_combo_box_set_switch (self, g_value_get_object (value));
  158. break;
  159. case PROP_LABEL:
  160. gtk_label_set_text_with_mnemonic (GTK_LABEL (self->priv->label), g_value_get_string (value));
  161. break;
  162. case PROP_BUTTON_LABEL:
  163. gtk_button_set_label (GTK_BUTTON (self->priv->button), g_value_get_string (value));
  164. break;
  165. case PROP_SHOW_BUTTON:
  166. gtk_widget_set_visible (self->priv->button, g_value_get_boolean (value));
  167. break;
  168. default:
  169. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  170. break;
  171. }
  172. }
  173. static void
  174. gvc_combo_box_get_property (GObject *object,
  175. guint prop_id,
  176. GValue *value,
  177. GParamSpec *pspec)
  178. {
  179. GvcComboBox *self = GVC_COMBO_BOX (object);
  180. switch (prop_id) {
  181. case PROP_SWITCH:
  182. g_value_set_object (value, self->priv->swtch);
  183. break;
  184. case PROP_LABEL:
  185. g_value_set_string (value, gtk_label_get_text (GTK_LABEL (self->priv->label)));
  186. break;
  187. case PROP_BUTTON_LABEL:
  188. g_value_set_string (value, gtk_button_get_label (GTK_BUTTON (self->priv->button)));
  189. break;
  190. case PROP_SHOW_BUTTON:
  191. g_value_set_boolean (value, gtk_widget_get_visible (self->priv->button));
  192. break;
  193. default:
  194. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  195. break;
  196. }
  197. }
  198. static void
  199. gvc_combo_box_class_init (GvcComboBoxClass *klass)
  200. {
  201. GObjectClass *object_class = G_OBJECT_CLASS (klass);
  202. object_class->dispose = gvc_combo_box_dispose;
  203. object_class->set_property = gvc_combo_box_set_property;
  204. object_class->get_property = gvc_combo_box_get_property;
  205. properties[PROP_SWITCH] =
  206. g_param_spec_object ("switch",
  207. "switch",
  208. "The MateMixerSwitch",
  209. MATE_MIXER_TYPE_SWITCH,
  210. G_PARAM_READWRITE |
  211. G_PARAM_CONSTRUCT_ONLY |
  212. G_PARAM_STATIC_STRINGS);
  213. properties[PROP_LABEL] =
  214. g_param_spec_string ("label",
  215. "label",
  216. "The combo box label",
  217. _("_Profile:"),
  218. G_PARAM_READWRITE |
  219. G_PARAM_CONSTRUCT |
  220. G_PARAM_STATIC_STRINGS);
  221. properties[PROP_SHOW_BUTTON] =
  222. g_param_spec_boolean ("show-button",
  223. "show-button",
  224. "Whether to show the button",
  225. FALSE,
  226. G_PARAM_READWRITE |
  227. G_PARAM_CONSTRUCT |
  228. G_PARAM_STATIC_STRINGS);
  229. properties[PROP_BUTTON_LABEL] =
  230. g_param_spec_string ("button-label",
  231. "button-label",
  232. "The button's label",
  233. "",
  234. G_PARAM_READWRITE |
  235. G_PARAM_CONSTRUCT |
  236. G_PARAM_STATIC_STRINGS);
  237. g_object_class_install_properties (object_class, N_PROPERTIES, properties);
  238. signals[CHANGING] =
  239. g_signal_new ("changing",
  240. G_TYPE_FROM_CLASS (klass),
  241. G_SIGNAL_RUN_LAST,
  242. G_STRUCT_OFFSET (GvcComboBoxClass, changing),
  243. NULL, NULL,
  244. g_cclosure_marshal_VOID__VOID,
  245. G_TYPE_NONE,
  246. 1,
  247. MATE_MIXER_TYPE_SWITCH_OPTION);
  248. signals[BUTTON_CLICKED] =
  249. g_signal_new ("button-clicked",
  250. G_TYPE_FROM_CLASS (klass),
  251. G_SIGNAL_RUN_LAST,
  252. G_STRUCT_OFFSET (GvcComboBoxClass, button_clicked),
  253. NULL, NULL,
  254. g_cclosure_marshal_VOID__VOID,
  255. G_TYPE_NONE,
  256. 0,
  257. G_TYPE_NONE);
  258. g_type_class_add_private (klass, sizeof (GvcComboBoxPrivate));
  259. }
  260. static void
  261. on_combo_box_changed (GtkComboBox *widget, GvcComboBox *combobox)
  262. {
  263. GtkTreeIter iter;
  264. gchar *name;
  265. MateMixerSwitchOption *option;
  266. if G_UNLIKELY (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter) == FALSE)
  267. return;
  268. gtk_tree_model_get (combobox->priv->model, &iter,
  269. COL_NAME, &name,
  270. -1);
  271. option = mate_mixer_switch_get_option (combobox->priv->swtch, name);
  272. if G_UNLIKELY (option == NULL) {
  273. g_warn_if_reached ();
  274. g_free (name);
  275. return;
  276. }
  277. /* Inform that we are about to change the active option of the switch */
  278. g_signal_emit (combobox, signals[CHANGING], 0, option);
  279. mate_mixer_switch_set_active_option (combobox->priv->swtch, option);
  280. g_free (name);
  281. }
  282. static void
  283. on_combo_box_button_clicked (GtkButton *button, GvcComboBox *combobox)
  284. {
  285. /* The meaning of the button is defined by the owner, so only notify
  286. * when it is clicked on */
  287. g_signal_emit (combobox, signals[BUTTON_CLICKED], 0);
  288. }
  289. static void
  290. gvc_combo_box_init (GvcComboBox *combobox)
  291. {
  292. GtkWidget *frame;
  293. GtkCellRenderer *renderer;
  294. frame = gtk_frame_new (NULL);
  295. gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
  296. combobox->priv = GVC_COMBO_BOX_GET_PRIVATE (combobox);
  297. combobox->priv->model = GTK_TREE_MODEL (gtk_list_store_new (NUM_COLS,
  298. G_TYPE_STRING,
  299. G_TYPE_STRING));
  300. combobox->priv->combobox = gtk_combo_box_new_with_model (combobox->priv->model);
  301. combobox->priv->label = gtk_label_new (NULL);
  302. #if GTK_CHECK_VERSION (3, 16, 0)
  303. gtk_label_set_xalign (GTK_LABEL (combobox->priv->label), 0.0);
  304. gtk_label_set_yalign (GTK_LABEL (combobox->priv->label), 0.5);
  305. #else
  306. gtk_misc_set_alignment (GTK_MISC (combobox->priv->label), 0.0, 0.5);
  307. #endif
  308. gtk_label_set_mnemonic_widget (GTK_LABEL (combobox->priv->label),
  309. combobox->priv->combobox);
  310. renderer = gtk_cell_renderer_text_new ();
  311. gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox->priv->combobox),
  312. renderer,
  313. FALSE);
  314. gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combobox->priv->combobox),
  315. renderer,
  316. "text",
  317. COL_HUMAN_NAME);
  318. #if GTK_CHECK_VERSION (3, 0, 0)
  319. combobox->priv->drop_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  320. combobox->priv->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  321. combobox->priv->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  322. /* Make sure the combo box does not get too long on long profile names */
  323. g_object_set (G_OBJECT (renderer),
  324. "ellipsize",
  325. PANGO_ELLIPSIZE_END,
  326. NULL);
  327. gtk_combo_box_set_popup_fixed_width (GTK_COMBO_BOX (combobox->priv->combobox), FALSE);
  328. #else
  329. combobox->priv->drop_box = gtk_hbox_new (FALSE, 6);
  330. combobox->priv->start_box = gtk_hbox_new (FALSE, 6);
  331. combobox->priv->end_box = gtk_hbox_new (FALSE, 6);
  332. #endif
  333. gtk_box_pack_start (GTK_BOX (combobox),
  334. frame,
  335. TRUE, TRUE, 0);
  336. gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box),
  337. combobox->priv->start_box,
  338. FALSE, FALSE, 0);
  339. gtk_box_pack_start (GTK_BOX (combobox->priv->start_box),
  340. combobox->priv->label,
  341. FALSE, FALSE, 0);
  342. gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box),
  343. combobox->priv->combobox,
  344. TRUE, TRUE, 0);
  345. combobox->priv->button = gtk_button_new_with_label ("");
  346. gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box),
  347. combobox->priv->button,
  348. FALSE, FALSE, 0);
  349. gtk_box_pack_start (GTK_BOX (combobox->priv->drop_box),
  350. combobox->priv->end_box,
  351. FALSE, FALSE, 0);
  352. gtk_container_add (GTK_CONTAINER (frame), combobox->priv->drop_box);
  353. g_signal_connect (G_OBJECT (combobox->priv->combobox),
  354. "changed",
  355. G_CALLBACK (on_combo_box_changed),
  356. combobox);
  357. g_signal_connect (G_OBJECT (combobox->priv->button),
  358. "clicked",
  359. G_CALLBACK (on_combo_box_button_clicked),
  360. combobox);
  361. gtk_widget_set_no_show_all (combobox->priv->button, TRUE);
  362. gtk_widget_show_all (frame);
  363. }
  364. static void
  365. gvc_combo_box_dispose (GObject *object)
  366. {
  367. GvcComboBox *combobox;
  368. combobox = GVC_COMBO_BOX (object);
  369. if G_LIKELY (combobox->priv->swtch != NULL) {
  370. g_signal_handlers_disconnect_by_func (G_OBJECT (combobox->priv->swtch),
  371. G_CALLBACK (on_switch_active_option_notify),
  372. combobox);
  373. g_clear_object (&combobox->priv->swtch);
  374. }
  375. g_clear_object (&combobox->priv->model);
  376. G_OBJECT_CLASS (gvc_combo_box_parent_class)->dispose (object);
  377. }
  378. GtkWidget *
  379. gvc_combo_box_new (MateMixerSwitch *swtch, const gchar *label)
  380. {
  381. return g_object_new (GVC_TYPE_COMBO_BOX,
  382. "switch", swtch,
  383. "label", label,
  384. #if GTK_CHECK_VERSION (3, 0, 0)
  385. "orientation", GTK_ORIENTATION_HORIZONTAL,
  386. #endif
  387. NULL);
  388. }