1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* eggcomboselect widget
5 * Copyright (C) Naba Kumar <naba@gnome.org>
8 * gtkcombo_select - combo_select widget for gtk+
9 * Copyright 1999-2001 Adrian E. Feiguin <feiguin@ifir.edu.ar>
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public
22 * License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
28 #include <gtk/gtkhbox.h>
29 #include <gtk/gtktogglebutton.h>
30 #include <gtk/gtkarrow.h>
31 #include <gtk/gtkeventbox.h>
32 #include <gtk/gtkmain.h>
33 #include <gtk/gtksignal.h>
34 #include <gtk/gtkwindow.h>
35 #include <gdk/gdkkeysyms.h>
36 #include <gtk/gtkframe.h>
37 #include <gtk/gtkscrolledwindow.h>
38 #include <gtk/gtktreeview.h>
39 #include <gtk/gtkcelllayout.h>
40 #include "gtkcellview.h"
42 #include <libegg/menu/eggcomboselect.h>
44 static void egg_combo_select_class_init (EggComboSelectClass
*klass
);
45 static void egg_combo_select_init (EggComboSelect
*combo_select
);
46 static void egg_combo_select_dispose (GObject
*combo_select
);
47 static void egg_combo_select_finalize (GObject
*combo_select
);
48 static void egg_combo_select_get_pos (EggComboSelect
*combo_select
,
53 static gint
egg_combo_select_button_toggled (GtkWidget
* widget
,
54 EggComboSelect
* combo_select
);
55 static void egg_combo_select_arrow_clicked (GtkWidget
* widget
,
56 EggComboSelect
* combo_select
);
57 static gint
egg_combo_select_button_press (GtkWidget
*widget
,
60 static gboolean
egg_combo_select_key_press (GtkWidget
* widget
, GdkEventKey
* event
, gpointer data
);
61 static void egg_combo_select_size_allocate (GtkWidget
*widget
,
62 GtkAllocation
*allocation
);
63 static void egg_combo_select_size_request (GtkWidget
*widget
,
64 GtkRequisition
*requisition
);
65 static void egg_combo_select_sync_cells (EggComboSelect
*combo_select
,
66 GtkCellLayout
*cell_layout
);
68 struct _EggComboSelectPriv
{
76 GtkTreeViewColumn
*column
;
79 GtkWidget
*cell_frame
;
80 GtkTreeRowReference
*active_row
;
84 typedef struct _ComboSelectCellInfo ComboSelectCellInfo
;
85 struct _ComboSelectCellInfo
87 GtkCellRenderer
*cell
;
90 GtkCellLayoutDataFunc func
;
92 GDestroyNotify destroy
;
103 static GtkHBoxClass
*parent_class
= NULL
;
104 static guint action_signals
[LAST_SIGNAL
] = { 0 };
106 /* Celllayout interface implementation */
108 static ComboSelectCellInfo
*
109 cell_get_info (EggComboSelect
*combo_select
,
110 GtkCellRenderer
*cell
)
114 for (i
= combo_select
->priv
->cells
; i
; i
= i
->next
)
116 ComboSelectCellInfo
*info
= (ComboSelectCellInfo
*)i
->data
;
118 if (info
&& info
->cell
== cell
)
126 cell_layout_pack_start (GtkCellLayout
*layout
,
127 GtkCellRenderer
*cell
,
130 ComboSelectCellInfo
*info
;
131 EggComboSelect
*combo_select
;
133 g_return_if_fail (EGG_IS_COMBO_SELECT (layout
));
134 g_return_if_fail (GTK_IS_CELL_RENDERER (cell
));
136 combo_select
= EGG_COMBO_SELECT (layout
);
139 gtk_object_sink (GTK_OBJECT (cell
));
141 info
= g_new0 (ComboSelectCellInfo
, 1);
143 info
->expand
= expand
;
144 info
->pack
= GTK_PACK_START
;
146 combo_select
->priv
->cells
= g_slist_append (combo_select
->priv
->cells
, info
);
148 if (combo_select
->priv
->cell_view
)
149 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_select
->priv
->cell_view
),
152 if (combo_select
->priv
->column
)
153 gtk_tree_view_column_pack_start (combo_select
->priv
->column
, cell
, expand
);
157 cell_layout_pack_end (GtkCellLayout
*layout
,
158 GtkCellRenderer
*cell
,
161 ComboSelectCellInfo
*info
;
162 EggComboSelect
*combo_select
;
164 g_return_if_fail (EGG_IS_COMBO_SELECT (layout
));
165 g_return_if_fail (GTK_IS_CELL_RENDERER (cell
));
167 combo_select
= EGG_COMBO_SELECT (layout
);
170 gtk_object_sink (GTK_OBJECT (cell
));
172 info
= g_new0 (ComboSelectCellInfo
, 1);
174 info
->expand
= expand
;
175 info
->pack
= GTK_PACK_END
;
177 combo_select
->priv
->cells
= g_slist_append (combo_select
->priv
->cells
, info
);
179 if (combo_select
->priv
->cell_view
)
180 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_select
->priv
->cell_view
),
183 if (combo_select
->priv
->column
)
184 gtk_tree_view_column_pack_end (combo_select
->priv
->column
, cell
, expand
);
188 cell_layout_clear_attributes (GtkCellLayout
*layout
,
189 GtkCellRenderer
*cell
)
191 ComboSelectCellInfo
*info
;
192 EggComboSelect
*combo_select
;
195 g_return_if_fail (EGG_IS_COMBO_SELECT (layout
));
196 g_return_if_fail (GTK_IS_CELL_RENDERER (cell
));
198 combo_select
= EGG_COMBO_SELECT (layout
);
200 info
= cell_get_info (combo_select
, cell
);
201 g_return_if_fail (info
!= NULL
);
203 list
= info
->attributes
;
204 while (list
&& list
->next
)
207 list
= list
->next
->next
;
209 g_slist_free (info
->attributes
);
210 info
->attributes
= NULL
;
212 if (combo_select
->priv
->cell_view
)
213 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_select
->priv
->cell_view
), cell
);
215 if (combo_select
->priv
->column
)
216 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_select
->priv
->column
), cell
);
218 gtk_widget_queue_resize (GTK_WIDGET (combo_select
));
222 cell_layout_clear (GtkCellLayout
*layout
)
224 EggComboSelect
*combo_select
;
227 g_return_if_fail (EGG_IS_COMBO_SELECT (layout
));
229 combo_select
= EGG_COMBO_SELECT (layout
);
231 if (combo_select
->priv
->cell_view
)
232 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_select
->priv
->cell_view
));
234 if (combo_select
->priv
->column
)
235 gtk_tree_view_column_clear (combo_select
->priv
->column
);
237 for (i
= combo_select
->priv
->cells
; i
; i
= i
->next
)
239 ComboSelectCellInfo
*info
= (ComboSelectCellInfo
*)i
->data
;
241 cell_layout_clear_attributes (layout
, info
->cell
);
242 g_object_unref (info
->cell
);
246 g_slist_free (combo_select
->priv
->cells
);
247 combo_select
->priv
->cells
= NULL
;
251 cell_layout_add_attribute (GtkCellLayout
*layout
,
252 GtkCellRenderer
*cell
,
253 const gchar
*attribute
,
256 ComboSelectCellInfo
*info
;
257 EggComboSelect
*combo_select
;
259 g_return_if_fail (EGG_IS_COMBO_SELECT (layout
));
260 g_return_if_fail (GTK_IS_CELL_RENDERER (cell
));
262 combo_select
= EGG_COMBO_SELECT (layout
);
264 info
= cell_get_info (combo_select
, cell
);
266 info
->attributes
= g_slist_prepend (info
->attributes
,
267 GINT_TO_POINTER (column
));
268 info
->attributes
= g_slist_prepend (info
->attributes
,
269 g_strdup (attribute
));
271 if (combo_select
->priv
->cell_view
)
272 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_select
->priv
->cell_view
),
273 cell
, attribute
, column
);
275 if (combo_select
->priv
->column
)
276 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_select
->priv
->column
),
277 cell
, attribute
, column
);
278 gtk_widget_queue_resize (GTK_WIDGET (combo_select
));
282 combo_cell_data_func (GtkCellLayout
*cell_layout
,
283 GtkCellRenderer
*cell
,
284 GtkTreeModel
*tree_model
,
288 ComboSelectCellInfo
*info
= (ComboSelectCellInfo
*)data
;
289 GtkWidget
*parent
= NULL
;
294 (*info
->func
) (cell_layout
, cell
, tree_model
, iter
, info
->func_data
);
298 cell_layout_set_cell_data_func (GtkCellLayout
*layout
,
299 GtkCellRenderer
*cell
,
300 GtkCellLayoutDataFunc func
,
302 GDestroyNotify destroy
)
304 ComboSelectCellInfo
*info
;
305 EggComboSelect
*combo_select
;
307 g_return_if_fail (EGG_IS_COMBO_SELECT (layout
));
309 combo_select
= EGG_COMBO_SELECT (layout
);
311 info
= cell_get_info (combo_select
, cell
);
312 g_return_if_fail (info
!= NULL
);
316 GDestroyNotify d
= info
->destroy
;
318 info
->destroy
= NULL
;
323 info
->func_data
= func_data
;
324 info
->destroy
= destroy
;
326 if (combo_select
->priv
->cell_view
)
327 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_select
->priv
->cell_view
), cell
, func
, func_data
, NULL
);
329 if (combo_select
->priv
->column
)
330 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_select
->priv
->column
), cell
, func
, func_data
, NULL
);
332 gtk_widget_queue_resize (GTK_WIDGET (combo_select
));
336 cell_layout_reorder (GtkCellLayout
*layout
,
337 GtkCellRenderer
*cell
,
340 ComboSelectCellInfo
*info
;
341 EggComboSelect
*combo_select
;
344 g_return_if_fail (EGG_IS_COMBO_SELECT (layout
));
345 g_return_if_fail (GTK_IS_CELL_RENDERER (cell
));
347 combo_select
= EGG_COMBO_SELECT (layout
);
349 info
= cell_get_info (combo_select
, cell
);
351 g_return_if_fail (info
!= NULL
);
352 g_return_if_fail (position
>= 0);
354 link
= g_slist_find (combo_select
->priv
->cells
, info
);
356 g_return_if_fail (link
!= NULL
);
358 combo_select
->priv
->cells
= g_slist_remove_link (combo_select
->priv
->cells
, link
);
359 combo_select
->priv
->cells
= g_slist_insert (combo_select
->priv
->cells
, info
,
362 if (combo_select
->priv
->cell_view
)
363 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_select
->priv
->cell_view
),
366 if (combo_select
->priv
->column
)
367 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_select
->priv
->column
),
370 gtk_widget_queue_draw (GTK_WIDGET (combo_select
));
374 cell_layout_init (GtkCellLayoutIface
*iface
)
376 iface
->pack_start
= cell_layout_pack_start
;
377 iface
->pack_end
= cell_layout_pack_end
;
378 iface
->reorder
= cell_layout_reorder
;
379 iface
->clear
= cell_layout_clear
;
380 iface
->add_attribute
= cell_layout_add_attribute
;
381 iface
->set_cell_data_func
= cell_layout_set_cell_data_func
;
382 iface
->clear_attributes
= cell_layout_clear_attributes
;
385 /* EggComboSelect class */
387 egg_combo_select_class_init (EggComboSelectClass
* klass
)
389 GObjectClass
*object_class
;
390 GtkWidgetClass
*widget_class
;
392 parent_class
= g_type_class_peek_parent (klass
);
393 object_class
= (GObjectClass
*) klass
;
394 widget_class
= (GtkWidgetClass
*) klass
;
396 object_class
->dispose
= egg_combo_select_dispose
;
397 object_class
->finalize
= egg_combo_select_finalize
;
399 widget_class
->size_allocate
= egg_combo_select_size_allocate
;
400 widget_class
->size_request
= egg_combo_select_size_request
;
402 action_signals
[CHANGED
] =
403 g_signal_new ("changed",
404 G_OBJECT_CLASS_TYPE (klass
),
406 G_STRUCT_OFFSET (EggComboSelectClass
, changed
),
408 g_cclosure_marshal_VOID__VOID
,
413 egg_combo_select_dispose (GObject
* combo_select
)
415 if (EGG_COMBO_SELECT (combo_select
)->priv
->popwin
)
417 egg_combo_select_popdown (EGG_COMBO_SELECT (combo_select
));
419 if (EGG_COMBO_SELECT (combo_select
)->priv
->model
)
421 g_object_unref (EGG_COMBO_SELECT (combo_select
)->priv
->model
);
422 EGG_COMBO_SELECT (combo_select
)->priv
->model
= NULL
;
424 if (G_OBJECT_CLASS (parent_class
)->dispose
)
425 (*G_OBJECT_CLASS (parent_class
)->dispose
) (combo_select
);
429 egg_combo_select_finalize (GObject
* combo_select
)
431 g_free (EGG_COMBO_SELECT(combo_select
)->priv
->title
);
432 g_free (EGG_COMBO_SELECT(combo_select
)->priv
);
433 if (G_OBJECT_CLASS (parent_class
)->finalize
)
434 (*G_OBJECT_CLASS (parent_class
)->finalize
) (combo_select
);
437 /***************************************************************/
440 egg_combo_select_get_position (EggComboSelect
*combo_select
,
449 GdkRectangle monitor
;
450 GtkRequisition popup_req
;
451 GtkPolicyType hpolicy
, vpolicy
;
453 // sample = GTK_BIN (combo_select->priv->button)->child;
454 sample
= combo_select
->priv
->button
;
456 gdk_window_get_origin (sample
->window
, x
, y
);
458 if (GTK_WIDGET_NO_WINDOW (sample
))
460 *x
+= sample
->allocation
.x
;
461 *y
+= sample
->allocation
.y
;
464 *width
= sample
->allocation
.width
;
466 /* *x -= GTK_CONTAINER (combo_select->priv->button)->border_width +
467 GTK_WIDGET (combo_select->priv->button)->style->xthickness;
468 *width += 2 * (GTK_CONTAINER (combo_select->priv->button)->border_width +
469 GTK_WIDGET (combo_select->priv->button)->style->xthickness);
471 hpolicy
= vpolicy
= GTK_POLICY_NEVER
;
472 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_select
->priv
->frame
),
474 gtk_widget_size_request (combo_select
->priv
->frame
, &popup_req
);
476 if (popup_req.width > *width)
478 hpolicy = GTK_POLICY_ALWAYS;
479 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_select->priv->frame),
481 gtk_widget_size_request (combo_select->priv->frame, &popup_req);
483 *width
= popup_req
.width
;
484 *height
= popup_req
.height
;
486 screen
= gtk_widget_get_screen (GTK_WIDGET (combo_select
));
487 monitor_num
= gdk_screen_get_monitor_at_window (screen
,
488 GTK_WIDGET (combo_select
)->window
);
489 gdk_screen_get_monitor_geometry (screen
, monitor_num
, &monitor
);
493 else if (*x
+ *width
> monitor
.x
+ monitor
.width
)
494 *x
= monitor
.x
+ monitor
.width
- *width
;
496 if (*y
+ sample
->allocation
.height
+ *height
<= monitor
.y
+ monitor
.height
)
497 *y
+= sample
->allocation
.height
;
498 else if (*y
- *height
>= monitor
.y
)
500 else if (monitor
.y
+ monitor
.height
- (*y
+ sample
->allocation
.height
) > *y
- monitor
.y
)
502 *y
+= sample
->allocation
.height
;
503 *height
= monitor
.y
+ monitor
.height
- *y
;
507 *height
= *y
- monitor
.y
;
511 if (popup_req
.height
> *height
)
513 vpolicy
= GTK_POLICY_ALWAYS
;
515 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_select
->priv
->frame
),
521 list_popup_resize_idle (gpointer user_data
)
523 EggComboSelect
*combo_select
;
524 gint x
, y
, width
, height
;
526 GDK_THREADS_ENTER ();
528 combo_select
= EGG_COMBO_SELECT (user_data
);
530 if (combo_select
->priv
->treeview
&&
531 GTK_WIDGET_MAPPED (combo_select
->priv
->popwin
))
533 egg_combo_select_get_position (combo_select
, &x
, &y
, &width
, &height
);
535 gtk_widget_set_size_request (combo_select
->priv
->popwin
, width
, height
);
536 gtk_window_move (GTK_WINDOW (combo_select
->priv
->popwin
), x
, y
);
539 GDK_THREADS_LEAVE ();
545 egg_combo_select_popup_resize (EggComboSelect
*combo_select
)
547 g_idle_add (list_popup_resize_idle
, combo_select
);
551 on_treeview_selection_changed (GtkTreeSelection
*selection
,
552 EggComboSelect
*combo_select
)
557 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
))
559 egg_combo_select_set_active_iter (combo_select
, &iter
);
561 egg_combo_select_popdown (combo_select
);
565 egg_combo_select_popup (EggComboSelect
* combo_select
)
567 gint height
, width
, x
, y
;
568 gint old_width
, old_height
;
569 GtkWidget
*event_box
;
571 GtkTreeSelection
*selection
;
574 combo_select
->priv
->popwin
= gtk_window_new (GTK_WINDOW_POPUP
);
576 gtk_widget_ref (combo_select
->priv
->popwin
);
577 gtk_window_set_policy (GTK_WINDOW (combo_select
->priv
->popwin
), 1, 1, 0);
578 gtk_widget_set_events (combo_select
->priv
->popwin
, GDK_KEY_PRESS_MASK
);
579 g_signal_connect (G_OBJECT (combo_select
->priv
->popwin
), "button_press_event",
580 G_CALLBACK (egg_combo_select_button_press
), combo_select
);
581 g_signal_connect (G_OBJECT (combo_select
->priv
->popwin
), "key_press_event",
582 G_CALLBACK (egg_combo_select_key_press
), combo_select
);
584 event_box
= gtk_event_box_new ();
585 gtk_container_add (GTK_CONTAINER (combo_select
->priv
->popwin
), event_box
);
586 gtk_widget_show (event_box
);
588 gtk_widget_realize (event_box
);
589 cursor
= gdk_cursor_new (GDK_TOP_LEFT_ARROW
);
590 gdk_window_set_cursor (event_box
->window
, cursor
);
591 gdk_cursor_unref (cursor
);
593 combo_select
->priv
->frame
= gtk_scrolled_window_new (NULL
, NULL
);
594 gtk_container_add (GTK_CONTAINER (event_box
), combo_select
->priv
->frame
);
595 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (combo_select
->priv
->frame
),
597 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_select
->priv
->frame
),
598 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
599 gtk_widget_show (combo_select
->priv
->frame
);
601 combo_select
->priv
->treeview
= gtk_tree_view_new ();
602 if (combo_select
->priv
->model
)
603 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_select
->priv
->treeview
),
604 combo_select
->priv
->model
);
606 gtk_widget_show (combo_select
->priv
->treeview
);
607 gtk_container_add (GTK_CONTAINER (combo_select
->priv
->frame
), combo_select
->priv
->treeview
);
609 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_select
->priv
->treeview
));
611 combo_select
->priv
->column
= gtk_tree_view_column_new ();
612 if (combo_select
->priv
->title
)
613 gtk_tree_view_column_set_title (combo_select
->priv
->column
,
614 combo_select
->priv
->title
);
615 g_object_ref (combo_select
->priv
->column
);
616 egg_combo_select_sync_cells (combo_select
,
617 GTK_CELL_LAYOUT (combo_select
->priv
->column
));
619 gtk_tree_view_append_column (GTK_TREE_VIEW (combo_select
->priv
->treeview
),
620 combo_select
->priv
->column
);
622 old_width
= combo_select
->priv
->popwin
->allocation
.width
;
623 old_height
= combo_select
->priv
->popwin
->allocation
.height
;
625 egg_combo_select_get_position (combo_select
, &x
, &y
, &width
, &height
);
626 gtk_widget_set_size_request (combo_select
->priv
->popwin
, width
, height
);
627 gtk_window_move (GTK_WINDOW (combo_select
->priv
->popwin
), x
, y
);
628 gtk_widget_show (combo_select
->priv
->popwin
);
630 gtk_grab_add (combo_select
->priv
->popwin
);
631 gdk_pointer_grab (combo_select
->priv
->popwin
->window
, TRUE
,
632 GDK_BUTTON_PRESS_MASK
|
633 GDK_BUTTON_RELEASE_MASK
|
634 GDK_POINTER_MOTION_MASK
,
635 NULL
, NULL
, GDK_CURRENT_TIME
);
636 if (egg_combo_select_get_active_iter (combo_select
, &iter
))
637 gtk_tree_selection_select_iter (selection
, &iter
);
638 g_signal_connect (G_OBJECT (selection
), "changed",
639 G_CALLBACK (on_treeview_selection_changed
), combo_select
);
643 destroy_widget_on_idle (gpointer data
)
645 gtk_widget_destroy (GTK_WIDGET (data
));
650 egg_combo_select_popdown (EggComboSelect
*combo_select
)
652 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(combo_select
->priv
->button
), FALSE
);
654 gtk_grab_remove(combo_select
->priv
->popwin
);
655 gdk_pointer_ungrab(GDK_CURRENT_TIME
);
656 gtk_widget_hide(combo_select
->priv
->popwin
);
658 g_object_unref (combo_select
->priv
->column
);
659 g_idle_add (destroy_widget_on_idle
, combo_select
->priv
->popwin
);
661 combo_select
->priv
->popwin
= NULL
;
662 combo_select
->priv
->treeview
= NULL
;
663 combo_select
->priv
->column
= NULL
;
664 combo_select
->priv
->frame
= NULL
;
668 egg_combo_select_button_toggled (GtkWidget
* widget
, EggComboSelect
* combo_select
)
670 GtkToggleButton
*button
;
672 button
= GTK_TOGGLE_BUTTON(widget
);
675 gtk_widget_hide (combo_select
->priv
->popwin
);
676 gtk_grab_remove (combo_select
->priv
->popwin
);
677 gdk_pointer_ungrab (GDK_CURRENT_TIME
);
681 egg_combo_select_popup (combo_select
);
686 egg_combo_select_arrow_clicked (GtkWidget
*widget
, EggComboSelect
* combo_select
)
688 g_signal_emit_by_name (G_OBJECT (combo_select
), "changed");
692 egg_combo_select_sync_cells (EggComboSelect
*combo_select
,
693 GtkCellLayout
*cell_layout
)
697 for (k
= combo_select
->priv
->cells
; k
; k
= k
->next
)
700 ComboSelectCellInfo
*info
= (ComboSelectCellInfo
*)k
->data
;
702 if (info
->pack
== GTK_PACK_START
)
703 gtk_cell_layout_pack_start (cell_layout
,
704 info
->cell
, info
->expand
);
705 else if (info
->pack
== GTK_PACK_END
)
706 gtk_cell_layout_pack_end (cell_layout
,
707 info
->cell
, info
->expand
);
709 gtk_cell_layout_set_cell_data_func (cell_layout
,
711 combo_cell_data_func
, info
, NULL
);
713 for (j
= info
->attributes
; j
; j
= j
->next
->next
)
715 gtk_cell_layout_add_attribute (cell_layout
,
718 GPOINTER_TO_INT (j
->next
->data
));
724 egg_combo_select_init (EggComboSelect
* combo_select
)
729 widget
=GTK_WIDGET(combo_select
);
731 combo_select
->priv
= g_new0 (EggComboSelectPriv
, 1);
732 combo_select
->priv
->model
= NULL
;
733 combo_select
->priv
->popwin
= NULL
;
734 combo_select
->priv
->frame
= NULL
;
735 combo_select
->priv
->column
= NULL
;
736 combo_select
->priv
->active_row
= NULL
;
737 combo_select
->priv
->title
= NULL
;
739 gtk_box_set_spacing (GTK_BOX (widget
), 5);
740 gtk_box_set_homogeneous (GTK_BOX (widget
), FALSE
);
742 /* Create button and cell view */
743 combo_select
->priv
->button
= gtk_toggle_button_new ();
744 gtk_widget_show (combo_select
->priv
->button
);
745 gtk_box_pack_start (GTK_BOX (combo_select
), combo_select
->priv
->button
, FALSE
, FALSE
, 0);
747 combo_select
->priv
->cell_view
= gtk_cell_view_new ();
748 gtk_widget_show (combo_select
->priv
->cell_view
);
749 gtk_container_add (GTK_CONTAINER (combo_select
->priv
->button
),
750 combo_select
->priv
->cell_view
);
752 /* Create arrow widget */
753 combo_select
->priv
->arrow
= gtk_button_new ();
754 gtk_widget_show (combo_select
->priv
->arrow
);
755 arrow
= gtk_arrow_new (GTK_ARROW_LEFT
, GTK_SHADOW_NONE
);
756 gtk_widget_show (arrow
);
757 gtk_container_add (GTK_CONTAINER (combo_select
->priv
->arrow
), arrow
);
758 gtk_box_pack_start (GTK_BOX (combo_select
), combo_select
->priv
->arrow
, FALSE
, FALSE
, 0);
760 g_signal_connect (G_OBJECT (combo_select
->priv
->button
), "toggled",
761 G_CALLBACK (egg_combo_select_button_toggled
), combo_select
);
762 g_signal_connect (G_OBJECT (combo_select
->priv
->arrow
), "clicked",
763 G_CALLBACK (egg_combo_select_arrow_clicked
), combo_select
);
767 egg_combo_select_get_type ()
769 static GType combo_select_type
= 0;
771 if (!combo_select_type
)
773 static const GTypeInfo combo_select_info
=
775 sizeof (EggComboSelectClass
),
776 NULL
, /* base_init */
777 NULL
, /* base_finalize */
778 (GClassInitFunc
) egg_combo_select_class_init
,
779 NULL
, /* class_finalize */
780 NULL
, /* class_data */
781 sizeof (EggComboSelect
),
783 (GInstanceInitFunc
) egg_combo_select_init
785 static const GInterfaceInfo cell_layout_info
=
787 (GInterfaceInitFunc
) cell_layout_init
,
791 combo_select_type
= g_type_register_static (GTK_TYPE_HBOX
,
795 g_type_add_interface_static (combo_select_type
,
796 GTK_TYPE_CELL_LAYOUT
,
799 return combo_select_type
;
803 egg_combo_select_new ()
805 EggComboSelect
*combo_select
;
807 combo_select
= gtk_type_new (egg_combo_select_get_type ());
809 return(GTK_WIDGET(combo_select
));
814 EggComboSelect
*combo
;
823 path_visible (GtkTreeView
*view
,
826 /* Note that we assume all paths visible. FIXME.
832 tree_column_row_is_sensitive (EggComboSelect
*combo_select
,
839 tree_next_func (GtkTreeModel
*model
,
844 SearchData
*search_data
= (SearchData
*)data
;
846 if (search_data
->found
)
848 if (!tree_column_row_is_sensitive (search_data
->combo
, iter
))
851 if (search_data
->visible
&&
852 !path_visible (GTK_TREE_VIEW (search_data
->combo
->priv
->treeview
), path
))
855 search_data
->set
= TRUE
;
856 search_data
->iter
= *iter
;
861 if (gtk_tree_path_compare (path
, search_data
->path
) == 0)
862 search_data
->found
= TRUE
;
868 tree_next (EggComboSelect
*combo
,
874 SearchData search_data
;
876 search_data
.combo
= combo
;
877 search_data
.path
= gtk_tree_model_get_path (model
, iter
);
878 search_data
.visible
= visible
;
879 search_data
.found
= FALSE
;
880 search_data
.set
= FALSE
;
882 gtk_tree_model_foreach (model
, tree_next_func
, &search_data
);
884 *next
= search_data
.iter
;
886 gtk_tree_path_free (search_data
.path
);
888 return search_data
.set
;
892 tree_prev_func (GtkTreeModel
*model
,
897 SearchData
*search_data
= (SearchData
*)data
;
899 if (gtk_tree_path_compare (path
, search_data
->path
) == 0)
901 search_data
->found
= TRUE
;
905 if (!tree_column_row_is_sensitive (search_data
->combo
, iter
))
908 if (search_data
->visible
&&
909 !path_visible (GTK_TREE_VIEW (search_data
->combo
->priv
->treeview
), path
))
912 search_data
->set
= TRUE
;
913 search_data
->iter
= *iter
;
919 tree_prev (EggComboSelect
*combo
,
925 SearchData search_data
;
927 search_data
.combo
= combo
;
928 search_data
.path
= gtk_tree_model_get_path (model
, iter
);
929 search_data
.visible
= visible
;
930 search_data
.found
= FALSE
;
931 search_data
.set
= FALSE
;
933 gtk_tree_model_foreach (model
, tree_prev_func
, &search_data
);
935 *prev
= search_data
.iter
;
937 gtk_tree_path_free (search_data
.path
);
939 return search_data
.set
;
943 tree_last_func (GtkTreeModel
*model
,
948 SearchData
*search_data
= (SearchData
*)data
;
950 if (!tree_column_row_is_sensitive (search_data
->combo
, iter
))
953 /* Note that we rely on the fact that collapsed rows don't have nodes
955 if (search_data
->visible
&&
956 !path_visible (GTK_TREE_VIEW (search_data
->combo
->priv
->treeview
), path
))
959 search_data
->set
= TRUE
;
960 search_data
->iter
= *iter
;
966 tree_last (EggComboSelect
*combo
,
971 SearchData search_data
;
973 search_data
.combo
= combo
;
974 search_data
.visible
= visible
;
975 search_data
.set
= FALSE
;
977 gtk_tree_model_foreach (model
, tree_last_func
, &search_data
);
979 *last
= search_data
.iter
;
981 return search_data
.set
;
986 tree_first_func (GtkTreeModel
*model
,
991 SearchData
*search_data
= (SearchData
*)data
;
993 if (!tree_column_row_is_sensitive (search_data
->combo
, iter
))
996 if (search_data
->visible
&&
997 !path_visible (GTK_TREE_VIEW (search_data
->combo
->priv
->treeview
), path
))
1000 search_data
->set
= TRUE
;
1001 search_data
->iter
= *iter
;
1007 tree_first (EggComboSelect
*combo
,
1008 GtkTreeModel
*model
,
1012 SearchData search_data
;
1014 search_data
.combo
= combo
;
1015 search_data
.visible
= visible
;
1016 search_data
.set
= FALSE
;
1018 gtk_tree_model_foreach (model
, tree_first_func
, &search_data
);
1020 *first
= search_data
.iter
;
1022 return search_data
.set
;
1026 egg_combo_select_key_press (GtkWidget
* widget
, GdkEventKey
* event
, gpointer data
)
1028 EggComboSelect
*combo_select
= EGG_COMBO_SELECT (data
);
1029 guint state
= event
->state
& gtk_accelerator_get_default_mod_mask ();
1032 GtkTreeIter new_iter
;
1033 GtkTreeSelection
*selection
;
1035 if (combo_select
->priv
->model
== NULL
)
1038 if ((event
->keyval
== GDK_Down
|| event
->keyval
== GDK_KP_Down
) &&
1039 state
== GDK_MOD1_MASK
)
1041 egg_combo_select_popup (combo_select
);
1045 if (event
->keyval
== GDK_Escape
|| event
->keyval
== GDK_Return
||
1046 event
->keyval
== GDK_space
)
1048 egg_combo_select_popdown (combo_select
);
1053 switch (event
->keyval
)
1057 if (egg_combo_select_get_active_iter (combo_select
, &iter
))
1059 found
= tree_next (combo_select
, combo_select
->priv
->model
,
1060 &iter
, &new_iter
, FALSE
);
1063 /* else fall through */
1065 case GDK_KP_Page_Up
:
1068 found
= tree_first (combo_select
, combo_select
->priv
->model
, &new_iter
, FALSE
);
1073 if (egg_combo_select_get_active_iter (combo_select
, &iter
))
1075 found
= tree_prev (combo_select
, combo_select
->priv
->model
,
1076 &iter
, &new_iter
, FALSE
);
1079 /* else fall through */
1081 case GDK_KP_Page_Down
:
1084 found
= tree_last (combo_select
, combo_select
->priv
->model
, &new_iter
, FALSE
);
1090 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_select
->priv
->treeview
));
1091 g_signal_handlers_block_by_func (G_OBJECT (selection
),
1092 G_CALLBACK (on_treeview_selection_changed
), combo_select
);
1094 egg_combo_select_set_active_iter (combo_select
, &new_iter
);
1095 g_signal_handlers_unblock_by_func (G_OBJECT (selection
),
1096 G_CALLBACK (on_treeview_selection_changed
), combo_select
);
1103 egg_combo_select_button_press (GtkWidget
* widget
, GdkEvent
* event
, gpointer data
)
1107 child
= gtk_get_event_widget (event
);
1109 if (child
!= widget
)
1113 if (child
== widget
)
1115 child
= child
->parent
;
1118 egg_combo_select_popdown (EGG_COMBO_SELECT (data
));
1120 gtk_widget_hide (widget);
1121 gtk_grab_remove (widget);
1122 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1123 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(EGG_COMBO_SELECT(data)->priv->button), FALSE);
1129 egg_combo_select_size_request (GtkWidget
*widget
,
1130 GtkRequisition
*requisition
)
1132 EggComboSelect
*combo_select
;
1133 GtkRequisition box_requisition
;
1135 g_return_if_fail (widget
!= NULL
);
1136 g_return_if_fail (EGG_IS_COMBO_SELECT (widget
));
1137 g_return_if_fail (requisition
!= NULL
);
1139 GTK_WIDGET_CLASS (parent_class
)->size_request (widget
, &box_requisition
);
1141 combo_select
=EGG_COMBO_SELECT(widget
);
1143 size = MIN(box_requisition.width, box_requisition.height);
1144 size = MIN(combo_select->priv->button->requisition.width, box_requisition.height);
1145 size = MIN(combo_select->priv->button->requisition.width, box_requisition.height);
1147 widget->requisition.height = size;
1148 widget->requisition.width = size + combo_select->priv->arrow->requisition.width;
1150 if (box_requisition
.width
< 200)
1151 box_requisition
.width
= 200;
1152 widget
->requisition
.height
= box_requisition
.height
;
1153 widget
->requisition
.width
= box_requisition
.width
;
1157 egg_combo_select_size_allocate (GtkWidget
*widget
,
1158 GtkAllocation
*allocation
)
1160 EggComboSelect
*combo_select
;
1161 GtkAllocation button_allocation
;
1163 g_return_if_fail (widget
!= NULL
);
1164 g_return_if_fail (EGG_IS_COMBO_SELECT (widget
));
1165 g_return_if_fail (allocation
!= NULL
);
1167 GTK_WIDGET_CLASS (parent_class
)->size_allocate (widget
, allocation
);
1169 combo_select
= EGG_COMBO_SELECT (widget
);
1171 button_allocation
= combo_select
->priv
->button
->allocation
;
1173 button_allocation.width = MIN(button_allocation.width,
1174 combo_select->priv->button->requisition.width);
1175 button_allocation.height = MIN(button_allocation.height,
1176 combo_select->priv->button->requisition.height);
1177 button_allocation.x += (combo_select->priv->button->allocation.width-
1178 button_allocation.width) / 2;
1179 button_allocation.y += (combo_select->priv->button->allocation.height-
1180 button_allocation.height) / 2;
1182 if (button_allocation
.width
< allocation
->width
-
1183 combo_select
->priv
->arrow
->requisition
.width
)
1185 button_allocation
.width
=
1186 allocation
->width
- combo_select
->priv
->arrow
->requisition
.width
;
1188 gtk_widget_size_allocate (combo_select
->priv
->button
, &button_allocation
);
1190 button_allocation
.x
= combo_select
->priv
->button
->allocation
.x
+
1191 combo_select
->priv
->button
->allocation
.width
;
1192 button_allocation
.width
= combo_select
->priv
->arrow
->requisition
.width
;
1193 gtk_widget_size_allocate (combo_select
->priv
->arrow
, &button_allocation
);
1197 egg_combo_select_set_model (EggComboSelect
*combo_select
, GtkTreeModel
*model
)
1199 g_return_if_fail (EGG_IS_COMBO_SELECT (combo_select
));
1200 g_return_if_fail (GTK_IS_TREE_MODEL (model
));
1202 if (combo_select
->priv
->model
== NULL
)
1203 egg_combo_select_sync_cells (combo_select
,
1204 GTK_CELL_LAYOUT (combo_select
->priv
->cell_view
));
1206 g_object_ref (model
);
1209 if (combo_select
->priv
->active_row
)
1211 gtk_tree_row_reference_free (combo_select
->priv
->active_row
);
1212 combo_select
->priv
->active_row
= NULL
;
1215 if (combo_select
->priv
->model
)
1217 g_object_unref (combo_select
->priv
->model
);
1218 combo_select
->priv
->model
= NULL
;
1222 combo_select
->priv
->model
= model
;
1223 if (combo_select
->priv
->treeview
)
1224 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_select
->priv
->treeview
),
1226 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_select
->priv
->cell_view
),
1228 /*if (combo_select->priv->cell_view)
1229 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_select->priv->cell_view),
1235 egg_combo_select_set_active_internal (EggComboSelect
*combo_select
, GtkTreePath
*path
)
1237 GtkTreePath
*active_path
;
1240 if (combo_select
->priv
->model
== NULL
)
1243 if (path
&& combo_select
->priv
->active_row
)
1245 active_path
= gtk_tree_row_reference_get_path (combo_select
->priv
->active_row
);
1248 path_cmp
= gtk_tree_path_compare (path
, active_path
);
1249 gtk_tree_path_free (active_path
);
1255 if (combo_select
->priv
->active_row
)
1257 gtk_tree_row_reference_free (combo_select
->priv
->active_row
);
1258 combo_select
->priv
->active_row
= NULL
;
1263 if (combo_select
->priv
->treeview
)
1264 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_select
->priv
->treeview
)));
1266 if (combo_select
->priv
->cell_view
)
1267 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_select
->priv
->cell_view
), NULL
);
1271 combo_select
->priv
->active_row
=
1272 gtk_tree_row_reference_new (combo_select
->priv
->model
, path
);
1274 if (combo_select
->priv
->treeview
)
1276 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_select
->priv
->treeview
),
1280 if (combo_select
->priv
->cell_view
)
1281 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_select
->priv
->cell_view
),
1284 g_signal_emit_by_name (combo_select
, "changed", NULL
, NULL
);
1288 egg_combo_select_set_active (EggComboSelect
*combo_select
, gint iter_index
)
1290 GtkTreePath
*path
= NULL
;
1291 g_return_if_fail (EGG_IS_COMBO_SELECT (combo_select
));
1292 g_return_if_fail (iter_index
>= -1);
1294 if (combo_select
->priv
->model
== NULL
)
1297 if (iter_index
!= -1)
1298 path
= gtk_tree_path_new_from_indices (iter_index
, -1);
1300 egg_combo_select_set_active_internal (combo_select
, path
);
1303 gtk_tree_path_free (path
);
1307 egg_combo_select_get_active (EggComboSelect
*combo_select
)
1310 g_return_val_if_fail (EGG_IS_COMBO_SELECT (combo_select
), 0);
1312 if (combo_select
->priv
->active_row
)
1315 path
= gtk_tree_row_reference_get_path (combo_select
->priv
->active_row
);
1320 result
= gtk_tree_path_get_indices (path
)[0];
1321 gtk_tree_path_free (path
);
1331 egg_combo_select_set_active_iter (EggComboSelect
*combo_select
,
1336 g_return_if_fail (EGG_IS_COMBO_SELECT (combo_select
));
1337 if (combo_select
->priv
->model
== NULL
)
1339 path
= gtk_tree_model_get_path (combo_select
->priv
->model
, iter
);
1340 egg_combo_select_set_active_internal (combo_select
, path
);
1341 gtk_tree_path_free (path
);
1345 egg_combo_select_get_active_iter (EggComboSelect
*combo_select
,
1351 g_return_val_if_fail (EGG_IS_COMBO_SELECT (combo_select
), FALSE
);
1353 if (!combo_select
->priv
->active_row
)
1355 path
= gtk_tree_row_reference_get_path (combo_select
->priv
->active_row
);
1358 result
= gtk_tree_model_get_iter (combo_select
->priv
->model
, iter
, path
);
1359 gtk_tree_path_free (path
);
1365 egg_combo_select_set_title (EggComboSelect
*combo_select
, const gchar
*title
)
1367 g_free (combo_select
->priv
->title
);
1368 combo_select
->priv
->title
= g_strdup (title
);
1369 if (combo_select
->priv
->column
)
1370 gtk_tree_view_column_set_title (combo_select
->priv
->column
, title
);