1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright 2009-2010 Red Hat, Inc,
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 2 of the License, or
8
* (at your option) any later version.
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.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
* Written by: Matthias Clasen <mclasen@redhat.com>
22
#include <gdk/gdkkeysyms.h>
23
#include "um-editable-combo.h"
25
#define EMPTY_TEXT "\xe2\x80\x94"
27
struct _UmEditableComboPrivate {
28
GtkNotebook *notebook;
39
#define UM_EDITABLE_COMBO_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), UM_TYPE_EDITABLE_COMBO, UmEditableComboPrivate))
54
static guint signals [LAST_SIGNAL] = { 0, };
56
G_DEFINE_TYPE (UmEditableCombo, um_editable_combo, GTK_TYPE_ALIGNMENT);
59
um_editable_combo_set_editable (UmEditableCombo *combo,
62
UmEditableComboPrivate *priv;
66
if (priv->editable != editable) {
67
priv->editable = editable;
69
gtk_notebook_set_current_page (priv->notebook, editable ? 1 : 0);
71
g_object_notify (G_OBJECT (combo), "editable");
76
um_editable_combo_get_editable (UmEditableCombo *combo)
78
return combo->priv->editable;
82
um_editable_combo_set_model (UmEditableCombo *combo,
85
gtk_combo_box_set_model (combo->priv->combo, model);
87
g_object_notify (G_OBJECT (combo), "model");
91
um_editable_combo_get_model (UmEditableCombo *combo)
93
return gtk_combo_box_get_model (combo->priv->combo);
97
um_editable_combo_set_text_column (UmEditableCombo *combo,
100
UmEditableComboPrivate *priv = combo->priv;
103
if (priv->text_column == text_column)
106
priv->text_column = text_column;
108
cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->combo));
109
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->combo),
115
g_object_notify (G_OBJECT (combo), "text-column");
119
um_editable_combo_get_text_column (UmEditableCombo *combo)
121
return combo->priv->text_column;
125
um_editable_combo_set_active (UmEditableCombo *combo,
133
um_editable_combo_set_active_iter (combo, NULL);
135
model = gtk_combo_box_get_model (combo->priv->combo);
136
path = gtk_tree_path_new_from_indices (active, -1);
137
gtk_tree_model_get_iter (model, &iter, path);
138
gtk_tree_path_free (path);
139
um_editable_combo_set_active_iter (combo, &iter);
144
um_editable_combo_set_active_iter (UmEditableCombo *combo,
147
UmEditableComboPrivate *priv = combo->priv;
152
gtk_combo_box_set_active_iter (priv->combo, iter);
153
priv->active = gtk_combo_box_get_active (priv->combo);
155
if (priv->text_column == -1)
159
model = gtk_combo_box_get_model (priv->combo);
160
gtk_tree_model_get (model, iter, priv->text_column, &text, -1);
163
text = g_strdup (EMPTY_TEXT);
166
gtk_label_set_text (priv->label, text);
167
label = gtk_bin_get_child ((GtkBin*)priv->button);
168
gtk_label_set_text (GTK_LABEL (label), text);
174
um_editable_combo_get_active_iter (UmEditableCombo *combo,
177
return gtk_combo_box_get_active_iter (combo->priv->combo, iter);
181
um_editable_combo_get_active (UmEditableCombo *combo)
183
return combo->priv->active;
187
um_editable_combo_set_property (GObject *object,
192
UmEditableCombo *combo = UM_EDITABLE_COMBO (object);
196
um_editable_combo_set_editable (combo, g_value_get_boolean (value));
199
um_editable_combo_set_model (combo, g_value_get_object (value));
201
case PROP_TEXT_COLUMN:
202
um_editable_combo_set_text_column (combo, g_value_get_int (value));
205
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
211
um_editable_combo_get_property (GObject *object,
216
UmEditableCombo *combo = UM_EDITABLE_COMBO (object);
220
g_value_set_boolean (value,
221
um_editable_combo_get_editable (combo));
224
g_value_set_object (value,
225
um_editable_combo_get_model (combo));
227
case PROP_TEXT_COLUMN:
228
g_value_set_int (value,
229
um_editable_combo_get_text_column (combo));
232
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
237
static void um_editable_combo_activate (UmEditableCombo *combo);
240
um_editable_combo_class_init (UmEditableComboClass *class)
242
GObjectClass *object_class;
243
GtkWidgetClass *widget_class;
245
object_class = G_OBJECT_CLASS (class);
246
widget_class = GTK_WIDGET_CLASS (class);
248
object_class->set_property = um_editable_combo_set_property;
249
object_class->get_property = um_editable_combo_get_property;
251
signals[EDITING_DONE] =
252
g_signal_new ("editing-done",
253
G_TYPE_FROM_CLASS (class),
255
G_STRUCT_OFFSET (UmEditableComboClass, editing_done),
257
g_cclosure_marshal_VOID__VOID,
260
g_signal_new ("activate",
261
G_TYPE_FROM_CLASS (class),
262
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
263
G_STRUCT_OFFSET (UmEditableComboClass, activate),
265
g_cclosure_marshal_VOID__VOID,
267
widget_class->activate_signal = signals[ACTIVATE];
268
class->activate = um_editable_combo_activate;
270
g_object_class_install_property (object_class, PROP_MODEL,
271
g_param_spec_object ("model",
272
"Model", "The options to present in the combobox",
276
g_object_class_install_property (object_class, PROP_TEXT_COLUMN,
277
g_param_spec_int ("text-column",
278
"Text Column", "The model column that contains the displayable text",
283
g_object_class_install_property (object_class, PROP_EDITABLE,
284
g_param_spec_boolean ("editable",
285
"Editable", "Whether the text can be edited",
289
g_type_class_add_private (class, sizeof (UmEditableComboPrivate));
293
start_editing (UmEditableCombo *combo)
295
gtk_notebook_set_current_page (combo->priv->notebook, 2);
296
gtk_combo_box_popup (combo->priv->combo);
300
stop_editing (UmEditableCombo *combo)
302
um_editable_combo_set_active (combo,
303
gtk_combo_box_get_active (combo->priv->combo));
304
gtk_notebook_set_current_page (combo->priv->notebook, 1);
306
g_signal_emit (combo, signals[EDITING_DONE], 0);
310
cancel_editing (UmEditableCombo *combo)
312
gtk_combo_box_set_active (combo->priv->combo,
313
um_editable_combo_get_active (combo));
314
gtk_notebook_set_current_page (combo->priv->notebook, 1);
318
um_editable_combo_activate (UmEditableCombo *combo)
320
if (combo->priv->editable) {
321
gtk_notebook_set_current_page (combo->priv->notebook, 2);
322
gtk_widget_grab_focus (GTK_WIDGET (combo->priv->combo));
327
button_clicked (GtkWidget *widget,
328
UmEditableCombo *combo)
330
if (combo->priv->editable)
331
start_editing (combo);
335
combo_changed (GtkWidget *widget,
336
UmEditableCombo *combo)
338
if (combo->priv->editable)
339
stop_editing (combo);
343
combo_key_press (GtkWidget *widget,
345
UmEditableCombo *combo)
347
if (event->keyval == GDK_KEY_Escape) {
348
cancel_editing (combo);
355
focus_moved (GtkWindow *window,
357
UmEditableCombo *combo)
359
if (gtk_notebook_get_current_page (combo->priv->notebook) == 2 &&
360
(!widget || !gtk_widget_is_ancestor (widget, (GtkWidget *)combo)))
361
stop_editing (combo);
365
combo_hierarchy_changed (GtkWidget *widget,
366
GtkWidget *previous_toplevel,
367
UmEditableCombo *combo)
369
UmEditableComboPrivate *priv;
374
toplevel = gtk_widget_get_toplevel (widget);
375
if (priv->toplevel != toplevel) {
377
g_signal_handlers_disconnect_by_func (priv->toplevel,
380
if (GTK_IS_WINDOW (toplevel))
381
priv->toplevel = toplevel;
383
priv->toplevel = NULL;
386
g_signal_connect (priv->toplevel, "set-focus",
387
G_CALLBACK (focus_moved), combo);
392
update_button_padding (GtkWidget *widget,
393
GtkAllocation *allocation,
394
UmEditableCombo *combo)
396
UmEditableComboPrivate *priv = combo->priv;
397
GtkAllocation parent_allocation;
401
gtk_widget_get_allocation (gtk_widget_get_parent (widget), &parent_allocation);
403
offset = allocation->x - parent_allocation.x;
405
gtk_misc_get_padding (GTK_MISC (priv->label), &pad, NULL);
407
gtk_misc_set_padding (GTK_MISC (priv->label), offset, 0);
411
um_editable_combo_init (UmEditableCombo *combo)
413
UmEditableComboPrivate *priv;
414
GtkCellRenderer *cell;
416
priv = combo->priv = UM_EDITABLE_COMBO_GET_PRIVATE (combo);
419
priv->text_column = -1;
421
priv->notebook = (GtkNotebook*)gtk_notebook_new ();
422
gtk_notebook_set_show_tabs (priv->notebook, FALSE);
423
gtk_notebook_set_show_border (priv->notebook, FALSE);
425
priv->label = (GtkLabel*)gtk_label_new ("");
426
gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
427
gtk_notebook_append_page (priv->notebook, (GtkWidget*)priv->label, NULL);
429
priv->button = (GtkButton*)gtk_button_new_with_label ("");
430
gtk_widget_set_receives_default ((GtkWidget*)priv->button, TRUE);
431
gtk_button_set_relief (priv->button, GTK_RELIEF_NONE);
432
gtk_button_set_alignment (priv->button, 0.0, 0.5);
433
gtk_notebook_append_page (priv->notebook, (GtkWidget*)priv->button, NULL);
434
g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked), combo);
436
priv->combo = (GtkComboBox*)gtk_combo_box_new ();
437
cell = gtk_cell_renderer_text_new ();
438
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo), cell, TRUE);
439
gtk_notebook_append_page (priv->notebook, (GtkWidget*)priv->combo, NULL);
441
g_signal_connect (priv->combo, "changed", G_CALLBACK (combo_changed), combo);
442
g_signal_connect (priv->combo, "key-press-event", G_CALLBACK (combo_key_press), combo);
443
g_signal_connect (gtk_bin_get_child (GTK_BIN (priv->button)), "size-allocate", G_CALLBACK (update_button_padding), combo);
446
gtk_container_add (GTK_CONTAINER (combo), (GtkWidget*)priv->notebook);
448
gtk_widget_show ((GtkWidget*)priv->notebook);
449
gtk_widget_show ((GtkWidget*)priv->label);
450
gtk_widget_show ((GtkWidget*)priv->button);
451
gtk_widget_show ((GtkWidget*)priv->combo);
453
gtk_notebook_set_current_page (priv->notebook, 0);
455
/* ugly hack to catch the combo box losing focus */
456
g_signal_connect (combo, "hierarchy-changed",
457
G_CALLBACK (combo_hierarchy_changed), combo);
461
um_editable_combo_new (void)
463
return (GtkWidget *) g_object_new (UM_TYPE_EDITABLE_COMBO, NULL);