1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
5 * Copyright (C) 2011 Sébastien Granjoux
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
30 #include <glib/gi18n.h>
32 #include "anjuta-tree-combo.h"
36 *---------------------------------------------------------------------------*/
38 struct _AnjutaTreeComboBoxPrivate
45 GtkWidget
*invalid_notebook
;
46 GtkWidget
*invalid_label
;
48 GtkWidget
*popup_window
;
49 GtkWidget
*scrolled_window
;
52 GdkDevice
*grab_pointer
;
53 GdkDevice
*grab_keyboard
;
58 gboolean popup_in_progress
;
60 gint active
; /* Only temporary */
61 GtkTreeRowReference
*active_row
; /* Current selected row */
63 GtkTreeModelFilterVisibleFunc valid_func
;
65 GDestroyNotify valid_destroy
;
85 static guint signals
[LAST_SIGNAL
] = {0,};
87 #define SCROLL_TIME 100
89 static void anjuta_tree_combo_box_cell_layout_init (GtkCellLayoutIface
*iface
);
91 G_DEFINE_TYPE_WITH_CODE (AnjutaTreeComboBox
, anjuta_tree_combo_box
, GTK_TYPE_TOGGLE_BUTTON
,
92 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT
,
93 anjuta_tree_combo_box_cell_layout_init
))
98 *---------------------------------------------------------------------------*/
101 popup_grab_on_window (GdkWindow
*window
,
106 gdk_device_grab (keyboard
, window
,
107 GDK_OWNERSHIP_WINDOW
, TRUE
,
108 GDK_KEY_PRESS_MASK
| GDK_KEY_RELEASE_MASK
,
109 NULL
, GDK_CURRENT_TIME
) != GDK_GRAB_SUCCESS
)
113 gdk_device_grab (pointer
, window
,
114 GDK_OWNERSHIP_WINDOW
, TRUE
,
115 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
116 GDK_POINTER_MOTION_MASK
,
117 NULL
, GDK_CURRENT_TIME
) != GDK_GRAB_SUCCESS
)
120 gdk_device_ungrab (keyboard
, GDK_CURRENT_TIME
);
131 *---------------------------------------------------------------------------*/
134 anjuta_tree_combo_box_popup_position (AnjutaTreeComboBox
*combo
,
140 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
141 GtkAllocation allocation
;
144 GdkRectangle monitor
;
145 GtkRequisition popup_req
;
146 GtkPolicyType hpolicy
, vpolicy
;
149 GtkWidget
*widget
= GTK_WIDGET (combo
);
153 gtk_widget_get_allocation (widget
, &allocation
);
155 if (!gtk_widget_get_has_window (widget
))
161 window
= gtk_widget_get_window (widget
);
163 gdk_window_get_root_coords (gtk_widget_get_window (widget
),
166 *width
= allocation
.width
;
168 hpolicy
= vpolicy
= GTK_POLICY_NEVER
;
169 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv
->scrolled_window
),
172 gtk_widget_get_preferred_size (priv
->scrolled_window
, &popup_req
, NULL
);
174 if (popup_req
.width
> *width
)
176 hpolicy
= GTK_POLICY_ALWAYS
;
177 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv
->scrolled_window
),
181 *height
= popup_req
.height
;
183 screen
= gtk_widget_get_screen (GTK_WIDGET (combo
));
184 monitor_num
= gdk_screen_get_monitor_at_window (screen
, window
);
185 gdk_screen_get_monitor_geometry (screen
, monitor_num
, &monitor
);
187 if (gtk_widget_get_direction (GTK_WIDGET (combo
)) == GTK_TEXT_DIR_RTL
)
188 *x
= *x
+ allocation
.width
- *width
;
192 else if (*x
+ *width
> monitor
.x
+ monitor
.width
)
193 *x
= monitor
.x
+ monitor
.width
- *width
;
195 if (*y
+ allocation
.height
+ *height
<= monitor
.y
+ monitor
.height
)
196 *y
+= allocation
.height
;
197 else if (*y
- *height
>= monitor
.y
)
199 else if (monitor
.y
+ monitor
.height
- (*y
+ allocation
.height
) > *y
- monitor
.y
)
201 *y
+= allocation
.height
;
202 *height
= monitor
.y
+ monitor
.height
- *y
;
206 *height
= *y
- monitor
.y
;
210 if (popup_req
.height
> *height
)
212 vpolicy
= GTK_POLICY_ALWAYS
;
214 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv
->scrolled_window
),
220 anjuta_tree_combo_box_popup_for_device (AnjutaTreeComboBox
*combo
,
223 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
224 gint x
, y
, width
, height
;
226 GdkDevice
*keyboard
, *pointer
;
228 g_return_if_fail (ANJUTA_IS_TREE_COMBO_BOX (combo
));
229 g_return_if_fail (GDK_IS_DEVICE (device
));
231 if (!gtk_widget_get_realized (GTK_WIDGET (combo
)))
234 if (gtk_widget_get_mapped (priv
->popup_window
))
237 if (priv
->grab_pointer
&& priv
->grab_keyboard
)
240 if (gdk_device_get_source (device
) == GDK_SOURCE_KEYBOARD
)
243 pointer
= gdk_device_get_associated_device (device
);
248 keyboard
= gdk_device_get_associated_device (device
);
251 toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (combo
));
253 if (GTK_IS_WINDOW (toplevel
))
254 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel
)),
255 GTK_WINDOW (priv
->popup_window
));
257 gtk_widget_show_all (priv
->scrolled_window
);
258 anjuta_tree_combo_box_popup_position (combo
, &x
, &y
, &width
, &height
);
260 gtk_widget_set_size_request (priv
->popup_window
, width
, height
);
261 gtk_window_move (GTK_WINDOW (priv
->popup_window
), x
, y
);
263 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv
->tree_view
),
267 gtk_widget_show (priv
->popup_window
);
269 gtk_widget_grab_focus (priv
->popup_window
);
270 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo
),
273 if (!gtk_widget_has_focus (priv
->tree_view
))
274 gtk_widget_grab_focus (priv
->tree_view
);
276 if (!popup_grab_on_window (gtk_widget_get_window (priv
->popup_window
),
279 gtk_widget_hide (priv
->popup_window
);
283 gtk_device_grab_add (priv
->popup_window
, pointer
, TRUE
);
284 priv
->grab_pointer
= pointer
;
285 priv
->grab_keyboard
= keyboard
;
289 anjuta_tree_combo_box_popup (AnjutaTreeComboBox
*combo
)
292 GtkWidgetClass
*widget_class
;
293 GtkBindingSet
*binding_set
;
295 device
= gtk_get_current_event_device ();
299 GdkDeviceManager
*device_manager
;
303 display
= gtk_widget_get_display (GTK_WIDGET (combo
));
304 device_manager
= gdk_display_get_device_manager (display
);
306 /* No device was set, pick the first master device */
307 devices
= gdk_device_manager_list_devices (device_manager
, GDK_DEVICE_TYPE_MASTER
);
308 device
= devices
->data
;
309 g_list_free (devices
);
312 anjuta_tree_combo_box_popup_for_device (combo
, device
);
314 widget_class
= GTK_WIDGET_GET_CLASS(combo
);
315 binding_set
= gtk_binding_set_by_class (widget_class
);
316 gtk_binding_entry_add_signal (binding_set
, GDK_KEY_Escape
, 0,
321 anjuta_tree_combo_box_popdown (AnjutaTreeComboBox
*combo
)
323 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
324 GtkWidgetClass
*widget_class
;
325 GtkBindingSet
*binding_set
;
327 g_return_if_fail (ANJUTA_IS_TREE_COMBO_BOX (combo
));
329 widget_class
= GTK_WIDGET_GET_CLASS(combo
);
330 binding_set
= gtk_binding_set_by_class (widget_class
);
331 gtk_binding_entry_remove (binding_set
, GDK_KEY_Escape
, 0);
333 if (!gtk_widget_get_realized (GTK_WIDGET (combo
)))
336 gtk_device_grab_remove (priv
->popup_window
, priv
->grab_pointer
);
337 gtk_widget_hide (priv
->popup_window
);
338 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo
),
341 priv
->grab_pointer
= NULL
;
342 priv
->grab_keyboard
= NULL
;
346 anjuta_tree_combo_box_unset_model (AnjutaTreeComboBox
*combo
)
348 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
350 if (priv
->model
!= NULL
)
352 g_object_unref (priv
->model
);
356 if (priv
->active_row
)
358 gtk_tree_row_reference_free (priv
->active_row
);
359 priv
->active_row
= NULL
;
364 gtk_cell_view_set_model (GTK_CELL_VIEW (priv
->cell_view
), NULL
);
369 gtk_tree_view_set_model (GTK_TREE_VIEW (priv
->tree_view
), NULL
);
374 anjuta_tree_combo_box_popup_destroy (AnjutaTreeComboBox
*combo
)
376 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
378 if (priv
->scroll_timer
)
380 g_source_remove (priv
->scroll_timer
);
381 priv
->scroll_timer
= 0;
384 if (priv
->resize_idle_id
)
386 g_source_remove (priv
->resize_idle_id
);
387 priv
->resize_idle_id
= 0;
390 if (priv
->popup_window
)
392 gtk_widget_destroy (priv
->popup_window
);
393 priv
->popup_window
= NULL
;
398 anjuta_tree_combo_box_set_active_path (AnjutaTreeComboBox
*combo
,
401 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
404 valid
= gtk_tree_row_reference_valid (priv
->active_row
);
408 GtkTreePath
*active_path
;
411 active_path
= gtk_tree_row_reference_get_path (priv
->active_row
);
412 cmp
= gtk_tree_path_compare (path
, active_path
);
413 gtk_tree_path_free (active_path
);
414 if (cmp
== 0) return;
417 if (priv
->active_row
)
419 gtk_tree_row_reference_free (priv
->active_row
);
420 priv
->active_row
= NULL
;
425 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv
->tree_view
)));
426 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv
->cell_view
), NULL
);
427 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv
->invalid_notebook
), INVALID_PAGE
);
434 priv
->active_row
= gtk_tree_row_reference_new (priv
->model
, path
);
436 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv
->tree_view
), path
);
437 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv
->tree_view
), path
, NULL
, FALSE
);
438 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv
->tree_view
), path
, NULL
, TRUE
, 0.5, 0.0);
440 if ((priv
->valid_func
== NULL
) ||
441 (gtk_tree_model_get_iter (combo
->priv
->model
, &iter
, path
) &&
442 priv
->valid_func (combo
->priv
->model
, &iter
, priv
->valid_data
)))
444 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv
->cell_view
), path
);
445 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv
->invalid_notebook
), VALID_PAGE
);
449 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv
->cell_view
), NULL
);
450 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv
->invalid_notebook
), INVALID_PAGE
);
454 g_signal_emit (combo
, signals
[CHANGED
], 0);
459 /* Callbacks functions
460 *---------------------------------------------------------------------------*/
463 anjuta_tree_can_select_node (GtkTreeSelection
*selection
,
466 gboolean path_currently_selected
,
469 AnjutaTreeComboBoxPrivate
*priv
= ANJUTA_TREE_COMBO_BOX (data
)->priv
;
472 return path_currently_selected
||
473 (priv
->valid_func
== NULL
) ||
474 (gtk_tree_model_get_iter (priv
->model
, &iter
, path
) &&
475 priv
->valid_func (priv
->model
, &iter
, priv
->valid_data
));
479 anjuta_tree_combo_box_auto_scroll (AnjutaTreeComboBox
*combo
,
484 GtkAllocation allocation
;
485 GtkWidget
*tree_view
= combo
->priv
->tree_view
;
488 gtk_widget_get_allocation (tree_view
, &allocation
);
490 adj
= gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo
->priv
->scrolled_window
));
491 if (adj
&& gtk_adjustment_get_upper (adj
) - gtk_adjustment_get_lower (adj
) > gtk_adjustment_get_page_size (adj
))
493 if (x
<= allocation
.x
&&
494 gtk_adjustment_get_lower (adj
) < gtk_adjustment_get_value (adj
))
496 value
= gtk_adjustment_get_value (adj
) - (allocation
.x
- x
+ 1);
497 gtk_adjustment_set_value (adj
, value
);
499 else if (x
>= allocation
.x
+ allocation
.width
&&
500 gtk_adjustment_get_upper (adj
) - gtk_adjustment_get_page_size (adj
) > gtk_adjustment_get_value (adj
))
502 value
= gtk_adjustment_get_value (adj
) + (x
- allocation
.x
- allocation
.width
+ 1);
503 gtk_adjustment_set_value (adj
, MAX (value
, 0.0));
507 adj
= gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo
->priv
->scrolled_window
));
508 if (adj
&& gtk_adjustment_get_upper (adj
) - gtk_adjustment_get_lower (adj
) > gtk_adjustment_get_page_size (adj
))
510 if (y
<= allocation
.y
&&
511 gtk_adjustment_get_lower (adj
) < gtk_adjustment_get_value (adj
))
513 value
= gtk_adjustment_get_value (adj
) - (allocation
.y
- y
+ 1);
514 gtk_adjustment_set_value (adj
, value
);
516 else if (y
>= allocation
.height
&&
517 gtk_adjustment_get_upper (adj
) - gtk_adjustment_get_page_size (adj
) > gtk_adjustment_get_value (adj
))
519 value
= gtk_adjustment_get_value (adj
) + (y
- allocation
.height
+ 1);
520 gtk_adjustment_set_value (adj
, MAX (value
, 0.0));
526 anjuta_tree_combo_box_scroll_timeout (AnjutaTreeComboBox
*combo
)
528 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
531 if (priv
->auto_scroll
)
533 gdk_window_get_device_position (gtk_widget_get_window (priv
->tree_view
),
536 anjuta_tree_combo_box_auto_scroll (combo
, x
, y
);
543 anjuta_tree_combo_box_key_press (GtkWidget
*widget
,
547 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (user_data
);
550 if (event
->keyval
== GDK_KEY_Return
|| event
->keyval
== GDK_KEY_ISO_Enter
|| event
->keyval
== GDK_KEY_KP_Enter
||
551 event
->keyval
== GDK_KEY_space
|| event
->keyval
== GDK_KEY_KP_Space
)
553 GtkTreeModel
*model
= NULL
;
555 if (combo
->priv
->model
)
557 GtkTreeSelection
*sel
;
559 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (combo
->priv
->tree_view
));
561 if (gtk_tree_selection_get_selected (sel
, &model
, &iter
))
562 anjuta_tree_combo_box_set_active_iter (combo
, &iter
);
565 anjuta_tree_combo_box_popdown (combo
);
570 if (!gtk_bindings_activate_event (G_OBJECT (widget
), event
))
572 /* The tree hasn't managed the
573 * event, forward it to the combobox
575 if (gtk_bindings_activate_event (G_OBJECT (combo
), event
)) return TRUE
;
582 anjuta_tree_combo_box_enter_notify (GtkWidget
*widget
,
583 GdkEventCrossing
*event
,
586 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (data
);
588 combo
->priv
->auto_scroll
= TRUE
;
594 anjuta_tree_combo_box_resize_idle (gpointer user_data
)
596 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (user_data
);
597 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
598 gint x
, y
, width
, height
;
600 if (priv
->tree_view
&& gtk_widget_get_mapped (priv
->popup_window
))
602 anjuta_tree_combo_box_popup_position (combo
, &x
, &y
, &width
, &height
);
604 gtk_widget_set_size_request (priv
->popup_window
, width
, height
);
605 gtk_window_move (GTK_WINDOW (priv
->popup_window
), x
, y
);
608 priv
->resize_idle_id
= 0;
614 anjuta_tree_combo_box_popup_resize (AnjutaTreeComboBox
*combo
)
616 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
618 if (!priv
->resize_idle_id
)
620 priv
->resize_idle_id
= gdk_threads_add_idle (anjuta_tree_combo_box_resize_idle
, combo
);
625 anjuta_tree_combo_box_row_expanded (GtkTreeModel
*model
,
630 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (user_data
);
632 anjuta_tree_combo_box_popup_resize (combo
);
636 anjuta_tree_combo_box_button_pressed (GtkWidget
*widget
,
637 GdkEventButton
*event
,
640 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (user_data
);
641 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
643 GtkWidget
*ewidget
= gtk_get_event_widget ((GdkEvent
*)event
);
645 if (ewidget
== priv
->popup_window
)
648 if ((ewidget
!= GTK_WIDGET (combo
)) ||
649 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo
)))
654 if (!gtk_widget_has_focus (GTK_WIDGET (combo
)))
656 gtk_widget_grab_focus (GTK_WIDGET (combo
));
659 anjuta_tree_combo_box_popup_for_device (combo
, event
->device
);
661 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo
), TRUE
);
663 priv
->auto_scroll
= FALSE
;
664 if (priv
->scroll_timer
== 0)
665 priv
->scroll_timer
= gdk_threads_add_timeout (SCROLL_TIME
,
666 (GSourceFunc
) anjuta_tree_combo_box_scroll_timeout
,
669 priv
->popup_in_progress
= TRUE
;
675 anjuta_tree_combo_box_button_released (GtkWidget
*widget
,
676 GdkEventButton
*event
,
680 GtkTreePath
*path
= NULL
;
683 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (user_data
);
684 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
686 gboolean popup_in_progress
= FALSE
;
688 GtkWidget
*ewidget
= gtk_get_event_widget ((GdkEvent
*)event
);
690 if (priv
->popup_in_progress
)
692 popup_in_progress
= TRUE
;
693 priv
->popup_in_progress
= FALSE
;
696 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv
->tree_view
),
698 if (priv
->scroll_timer
)
700 g_source_remove (priv
->scroll_timer
);
701 priv
->scroll_timer
= 0;
704 if (ewidget
!= priv
->tree_view
)
706 if ((ewidget
== GTK_WIDGET (combo
)) &&
707 !popup_in_progress
&&
708 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo
)))
710 anjuta_tree_combo_box_popdown (combo
);
714 /* released outside treeview */
715 if (ewidget
!= GTK_WIDGET (combo
))
717 anjuta_tree_combo_box_popdown (combo
);
725 /* select something cool */
726 ret
= gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv
->tree_view
),
732 return TRUE
; /* clicked outside window? */
734 gtk_tree_model_get_iter (priv
->model
, &iter
, path
);
736 if (anjuta_tree_can_select_node (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv
->tree_view
)),
742 anjuta_tree_combo_box_popdown (combo
);
743 anjuta_tree_combo_box_set_active_iter (combo
, &iter
);
745 gtk_tree_path_free (path
);
751 anjuta_tree_combo_box_button_toggled (GtkWidget
*widget
,
754 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (user_data
);
756 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget
)))
758 anjuta_tree_combo_box_popup (combo
);
762 anjuta_tree_combo_box_popdown (combo
);
767 anjuta_tree_combo_box_popup_setup (AnjutaTreeComboBox
*combo
)
769 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
770 GtkTreeSelection
*sel
;
773 priv
->popup_window
= gtk_window_new (GTK_WINDOW_POPUP
);
774 gtk_window_set_type_hint (GTK_WINDOW (priv
->popup_window
),
775 GDK_WINDOW_TYPE_HINT_COMBO
);
777 toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (combo
));
778 if (GTK_IS_WINDOW (toplevel
))
780 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel
)),
781 GTK_WINDOW (priv
->popup_window
));
782 gtk_window_set_transient_for (GTK_WINDOW (priv
->popup_window
),
783 GTK_WINDOW (toplevel
));
786 gtk_window_set_resizable (GTK_WINDOW (priv
->popup_window
), FALSE
);
787 gtk_window_set_screen (GTK_WINDOW (priv
->popup_window
),
788 gtk_widget_get_screen (GTK_WIDGET (combo
)));
790 priv
->scrolled_window
= gtk_scrolled_window_new (NULL
, NULL
);
791 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv
->scrolled_window
),
794 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv
->scrolled_window
),
796 gtk_container_add (GTK_CONTAINER (priv
->popup_window
),
797 priv
->scrolled_window
);
800 priv
->tree_view
= gtk_tree_view_new ();
801 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (priv
->tree_view
));
802 gtk_tree_selection_set_mode (sel
, GTK_SELECTION_BROWSE
);
803 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv
->tree_view
), FALSE
);
804 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv
->tree_view
), TRUE
);
805 gtk_tree_view_append_column (GTK_TREE_VIEW (priv
->tree_view
),
806 gtk_tree_view_column_new_with_area (priv
->area
));
807 gtk_container_add (GTK_CONTAINER (priv
->scrolled_window
),
810 /* set sample/popup widgets */
811 g_signal_connect (priv
->tree_view
, "key-press-event",
812 G_CALLBACK (anjuta_tree_combo_box_key_press
),
814 g_signal_connect (priv
->tree_view
, "enter-notify-event",
815 G_CALLBACK (anjuta_tree_combo_box_enter_notify
),
817 g_signal_connect (priv
->tree_view
, "row-expanded",
818 G_CALLBACK (anjuta_tree_combo_box_row_expanded
),
820 g_signal_connect (priv
->tree_view
, "row-collapsed",
821 G_CALLBACK (anjuta_tree_combo_box_row_expanded
),
823 g_signal_connect (priv
->popup_window
, "button-press-event",
824 G_CALLBACK (anjuta_tree_combo_box_button_pressed
),
826 g_signal_connect (priv
->popup_window
, "button-release-event",
827 G_CALLBACK (anjuta_tree_combo_box_button_released
),
830 gtk_widget_show (priv
->scrolled_window
);
835 *---------------------------------------------------------------------------*/
838 anjuta_tree_combo_box_set_model (AnjutaTreeComboBox
*combo
,
841 g_return_if_fail (ANJUTA_IS_TREE_COMBO_BOX (combo
));
843 anjuta_tree_combo_box_unset_model (combo
);
847 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
849 priv
->model
= g_object_ref (model
);
850 gtk_cell_view_set_model (GTK_CELL_VIEW (priv
->cell_view
), model
);
851 gtk_tree_view_set_model (GTK_TREE_VIEW (priv
->tree_view
), model
);
854 if (priv
->active
!= -1)
856 /* If an index was set in advance, apply it now */
857 anjuta_tree_combo_box_set_active (combo
, priv
->active
);
863 gtk_tree_model_get_iter_first (model
, &iter
);
864 anjuta_tree_combo_box_set_active_iter (combo
, &iter
);
870 anjuta_tree_combo_box_get_model (AnjutaTreeComboBox
*combo
)
872 return combo
->priv
->model
;
876 anjuta_tree_combo_box_set_active (AnjutaTreeComboBox
*combo
,
879 GtkTreePath
*path
= NULL
;
881 g_return_if_fail (ANJUTA_IS_TREE_COMBO_BOX (combo
));
882 g_return_if_fail (index
>= -1);
884 if (combo
->priv
->model
== NULL
)
886 /* Save index, in case the model is set after the index */
887 combo
->priv
->active
= index
;
891 if (index
!= -1) path
= gtk_tree_path_new_from_indices (index
, -1);
893 anjuta_tree_combo_box_set_active_path (combo
, path
);
895 gtk_tree_path_free (path
);
900 anjuta_tree_combo_box_set_active_iter (AnjutaTreeComboBox
*combo
,
903 GtkTreePath
*path
= NULL
;
905 g_return_if_fail (ANJUTA_IS_TREE_COMBO_BOX (combo
));
909 path
= gtk_tree_model_get_path (combo
->priv
->model
, iter
);
912 anjuta_tree_combo_box_set_active_path (combo
, path
);
914 gtk_tree_path_free (path
);
918 anjuta_tree_combo_box_get_active_iter (AnjutaTreeComboBox
*combo
,
921 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
923 gboolean valid
= FALSE
;
925 if (priv
->active_row
!= NULL
)
927 path
= gtk_tree_row_reference_get_path (priv
->active_row
);
930 valid
= gtk_tree_model_get_iter (priv
->model
, iter
, path
);
931 gtk_tree_path_free (path
);
939 anjuta_tree_combo_box_set_valid_function (AnjutaTreeComboBox
*combo
,
940 GtkTreeModelFilterVisibleFunc func
,
942 GDestroyNotify destroy
)
944 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
945 GtkTreeSelection
*selection
;
947 if (priv
->valid_destroy
!= NULL
)
949 priv
->valid_destroy (priv
->valid_data
);
951 priv
->valid_func
= func
;
952 priv
->valid_data
= data
;
953 priv
->valid_destroy
= destroy
;
956 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (priv
->tree_view
));
957 gtk_tree_selection_set_select_function (selection
, func
!= NULL
? anjuta_tree_can_select_node
: NULL
, func
!= NULL
? combo
: NULL
, NULL
);
961 anjuta_tree_combo_box_set_invalid_text (AnjutaTreeComboBox
*combo
,
964 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
966 gtk_label_set_text (GTK_LABEL (priv
->invalid_label
), str
);
971 anjuta_tree_combo_box_new (void)
973 return g_object_new (ANJUTA_TYPE_TREE_COMBO_BOX
, NULL
);
978 /* Implement GtkCellLayout interface
979 *---------------------------------------------------------------------------*/
982 anjuta_tree_combo_box_cell_layout_get_area (GtkCellLayout
*cell_layout
)
984 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (cell_layout
);
985 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
992 anjuta_tree_combo_box_cell_layout_init (GtkCellLayoutIface
*iface
)
994 iface
->get_area
= anjuta_tree_combo_box_cell_layout_get_area
;
999 /* Implement GtkWidget functions
1000 *---------------------------------------------------------------------------*/
1003 anjuta_tree_combo_box_destroy (GtkWidget
*widget
)
1005 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (widget
);
1006 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
1008 anjuta_tree_combo_box_popdown (combo
);
1010 GTK_WIDGET_CLASS (anjuta_tree_combo_box_parent_class
)->destroy (widget
);
1012 priv
->cell_view
= NULL
;
1013 priv
->tree_view
= NULL
;
1018 /* Implement GObject functions
1019 *---------------------------------------------------------------------------*/
1022 anjuta_tree_combo_box_dispose (GObject
*object
)
1024 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (object
);
1025 AnjutaTreeComboBoxPrivate
*priv
= combo
->priv
;
1029 g_object_unref (priv
->area
);
1033 if (priv
->valid_destroy
!= NULL
)
1035 priv
->valid_destroy (priv
->valid_data
);
1036 priv
->valid_destroy
= NULL
;
1038 anjuta_tree_combo_box_unset_model (combo
);
1040 anjuta_tree_combo_box_popup_destroy (combo
);
1042 G_OBJECT_CLASS (anjuta_tree_combo_box_parent_class
)->dispose (object
);
1046 anjuta_tree_combo_box_init (AnjutaTreeComboBox
*combo
)
1048 AnjutaTreeComboBoxPrivate
*priv
;
1049 GtkWidget
*box
, *sep
, *arrow
;
1050 GtkWidget
*image
, *ibox
;
1052 priv
= G_TYPE_INSTANCE_GET_PRIVATE (combo
,
1053 ANJUTA_TYPE_TREE_COMBO_BOX
,
1054 AnjutaTreeComboBoxPrivate
);
1059 priv
->active_row
= NULL
;
1061 priv
->valid_func
= NULL
;
1062 priv
->valid_destroy
= NULL
;
1064 gtk_widget_push_composite_child ();
1066 gtk_widget_set_halign (GTK_WIDGET (combo
), GTK_ALIGN_FILL
);
1067 gtk_widget_show (GTK_WIDGET (combo
));
1069 g_signal_connect (combo
, "button-press-event",
1070 G_CALLBACK (anjuta_tree_combo_box_button_pressed
), combo
);
1071 g_signal_connect (combo
, "toggled",
1072 G_CALLBACK (anjuta_tree_combo_box_button_toggled
), combo
);
1075 box
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 3);
1076 gtk_container_add (GTK_CONTAINER (combo
), box
);
1077 gtk_widget_show (box
);
1079 priv
->invalid_notebook
= gtk_notebook_new ();
1080 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv
->invalid_notebook
), FALSE
);
1081 gtk_notebook_set_show_border (GTK_NOTEBOOK (priv
->invalid_notebook
), FALSE
);
1082 gtk_box_pack_start (GTK_BOX (box
), priv
->invalid_notebook
, TRUE
, TRUE
, 0);
1084 ibox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 2);
1085 gtk_notebook_append_page (GTK_NOTEBOOK (priv
->invalid_notebook
), ibox
, NULL
);
1086 gtk_widget_show (ibox
);
1088 image
= gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING
, GTK_ICON_SIZE_SMALL_TOOLBAR
);
1089 gtk_box_pack_start (GTK_BOX (ibox
), image
, FALSE
, FALSE
, 0);
1090 gtk_widget_show (image
);
1092 priv
->invalid_label
= gtk_label_new (_("<Invalid>"));
1093 gtk_misc_set_alignment (GTK_MISC (priv
->invalid_label
), 0, 1);
1094 gtk_misc_set_padding (GTK_MISC (priv
->invalid_label
), 3, 3);
1095 gtk_box_pack_start (GTK_BOX (ibox
), priv
->invalid_label
, TRUE
, TRUE
, 0);
1096 gtk_widget_show (priv
->invalid_label
);
1098 priv
->area
= gtk_cell_area_box_new ();
1099 g_object_ref_sink (priv
->area
);
1101 priv
->cell_view
= gtk_cell_view_new_with_context (priv
->area
, NULL
);
1102 gtk_notebook_append_page (GTK_NOTEBOOK (priv
->invalid_notebook
), priv
->cell_view
, NULL
);
1103 gtk_widget_show (priv
->cell_view
);
1105 sep
= gtk_separator_new (GTK_ORIENTATION_VERTICAL
);
1106 gtk_box_pack_start (GTK_BOX (box
), sep
, FALSE
, FALSE
, 0);
1107 gtk_widget_show (sep
);
1109 arrow
= gtk_arrow_new (GTK_ARROW_DOWN
,GTK_SHADOW_NONE
);
1110 gtk_box_pack_start (GTK_BOX (box
), arrow
, FALSE
, FALSE
, 0);
1111 gtk_widget_show (arrow
);
1113 gtk_widget_pop_composite_child ();
1115 gtk_widget_show_all (GTK_WIDGET (combo
));
1117 anjuta_tree_combo_box_popup_setup (combo
);
1121 anjuta_tree_combo_box_set_property (GObject
*object
,
1123 const GValue
*value
,
1126 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (object
);
1131 anjuta_tree_combo_box_set_model (combo
, g_value_get_object (value
));
1134 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1140 anjuta_tree_combo_box_get_property (GObject
*object
,
1145 AnjutaTreeComboBox
*combo
= ANJUTA_TREE_COMBO_BOX (object
);
1150 g_value_set_object (value
, combo
->priv
->model
);
1153 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1159 anjuta_tree_combo_box_class_init (AnjutaTreeComboBoxClass
* class)
1161 GObjectClass
*gobject_class
;
1162 GtkWidgetClass
*widget_class
;
1163 GtkBindingSet
*binding_set
;
1165 widget_class
= GTK_WIDGET_CLASS (class);
1166 widget_class
->destroy
= anjuta_tree_combo_box_destroy
;
1168 gobject_class
= G_OBJECT_CLASS (class);
1169 gobject_class
->dispose
= anjuta_tree_combo_box_dispose
;
1170 gobject_class
->set_property
= anjuta_tree_combo_box_set_property
;
1171 gobject_class
->get_property
= anjuta_tree_combo_box_get_property
;
1175 signals
[CHANGED
] = g_signal_new ("changed",
1176 G_OBJECT_CLASS_TYPE (class),
1178 G_STRUCT_OFFSET (AnjutaTreeComboBoxClass
, changed
),
1180 g_cclosure_marshal_VOID__VOID
,
1183 signals
[POPUP
] = g_signal_new_class_handler ("popup",
1184 G_OBJECT_CLASS_TYPE (class),
1185 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
1186 G_CALLBACK (anjuta_tree_combo_box_popup
),
1188 g_cclosure_marshal_VOID__VOID
,
1191 signals
[POPDOWN
] = g_signal_new_class_handler ("popdown",
1192 G_OBJECT_CLASS_TYPE (class),
1193 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
1194 G_CALLBACK (anjuta_tree_combo_box_popdown
),
1196 g_cclosure_marshal_VOID__VOID
,
1200 * AnjutaTreeCombo:model:
1202 * The model from which the combo box takes the values shown
1206 g_object_class_install_property (gobject_class
,
1208 g_param_spec_object ("model",
1209 _("ComboBox model"),
1210 _("The model for the combo box"),
1211 GTK_TYPE_TREE_MODEL
,
1212 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
));
1215 binding_set
= gtk_binding_set_by_class (widget_class
);
1217 gtk_binding_entry_add_signal (binding_set
, GDK_KEY_Down
, GDK_MOD1_MASK
,
1219 gtk_binding_entry_add_signal (binding_set
, GDK_KEY_KP_Down
, GDK_MOD1_MASK
,
1221 gtk_binding_entry_add_signal (binding_set
, GDK_KEY_Up
, GDK_MOD1_MASK
,
1223 gtk_binding_entry_add_signal (binding_set
, GDK_KEY_KP_Up
, GDK_MOD1_MASK
,
1226 g_type_class_add_private (class, sizeof (AnjutaTreeComboBoxPrivate
));