Post-release version bump
[anjuta.git] / libanjuta / anjuta-tree-combo.c
blob74ab5678466d1a4110157edc076dcc2241e26f25
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
3 /* anjuta-tree-combo.c
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.
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
30 #include <glib/gi18n.h>
32 #include "anjuta-tree-combo.h"
35 /* Types
36 *---------------------------------------------------------------------------*/
38 struct _AnjutaTreeComboBoxPrivate
40 GtkTreeModel *model;
42 GtkWidget *cell_view;
43 GtkCellArea *area;
45 GtkWidget *popup_window;
46 GtkWidget *scrolled_window;
47 GtkWidget *tree_view;
49 GdkDevice *grab_pointer;
50 GdkDevice *grab_keyboard;
52 guint scroll_timer;
53 gboolean auto_scroll;
54 guint resize_idle_id;
55 gboolean popup_in_progress;
57 gint active; /* Only temporary */
58 GtkTreeRowReference *active_row; /* Current selected row */
61 enum {
62 CHANGED,
63 POPUP,
64 POPDOWN,
65 LAST_SIGNAL
68 static guint signals[LAST_SIGNAL] = {0,};
70 #define SCROLL_TIME 100
72 static void anjuta_tree_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
74 G_DEFINE_TYPE_WITH_CODE (AnjutaTreeComboBox, anjuta_tree_combo_box, GTK_TYPE_TOGGLE_BUTTON,
75 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
76 anjuta_tree_combo_box_cell_layout_init))
80 /* Helpers functions
81 *---------------------------------------------------------------------------*/
83 static gboolean
84 popup_grab_on_window (GdkWindow *window,
85 GdkDevice *keyboard,
86 GdkDevice *pointer)
88 if (keyboard &&
89 gdk_device_grab (keyboard, window,
90 GDK_OWNERSHIP_WINDOW, TRUE,
91 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
92 NULL, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS)
93 return FALSE;
95 if (pointer &&
96 gdk_device_grab (pointer, window,
97 GDK_OWNERSHIP_WINDOW, TRUE,
98 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
99 GDK_POINTER_MOTION_MASK,
100 NULL, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS)
102 if (keyboard)
103 gdk_device_ungrab (keyboard, GDK_CURRENT_TIME);
105 return FALSE;
108 return TRUE;
113 /* Private functions
114 *---------------------------------------------------------------------------*/
116 static void
117 anjuta_tree_combo_box_popup_position (AnjutaTreeComboBox *combo,
118 gint *x,
119 gint *y,
120 gint *width,
121 gint *height)
123 AnjutaTreeComboBoxPrivate *priv = combo->priv;
124 GtkAllocation allocation;
125 GdkScreen *screen;
126 gint monitor_num;
127 GdkRectangle monitor;
128 GtkRequisition popup_req;
129 GtkPolicyType hpolicy, vpolicy;
130 GdkWindow *window;
132 GtkWidget *widget = GTK_WIDGET (combo);
134 *x = *y = 0;
136 gtk_widget_get_allocation (widget, &allocation);
138 if (!gtk_widget_get_has_window (widget))
140 *x += allocation.x;
141 *y += allocation.y;
144 window = gtk_widget_get_window (widget);
146 gdk_window_get_root_coords (gtk_widget_get_window (widget),
147 *x, *y, x, y);
149 *width = allocation.width;
151 hpolicy = vpolicy = GTK_POLICY_NEVER;
152 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
153 hpolicy, vpolicy);
155 gtk_widget_get_preferred_size (priv->scrolled_window, &popup_req, NULL);
157 if (popup_req.width > *width)
159 hpolicy = GTK_POLICY_ALWAYS;
160 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
161 hpolicy, vpolicy);
164 *height = popup_req.height;
166 screen = gtk_widget_get_screen (GTK_WIDGET (combo));
167 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
168 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
170 if (gtk_widget_get_direction (GTK_WIDGET (combo)) == GTK_TEXT_DIR_RTL)
171 *x = *x + allocation.width - *width;
173 if (*x < monitor.x)
174 *x = monitor.x;
175 else if (*x + *width > monitor.x + monitor.width)
176 *x = monitor.x + monitor.width - *width;
178 if (*y + allocation.height + *height <= monitor.y + monitor.height)
179 *y += allocation.height;
180 else if (*y - *height >= monitor.y)
181 *y -= *height;
182 else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
184 *y += allocation.height;
185 *height = monitor.y + monitor.height - *y;
187 else
189 *height = *y - monitor.y;
190 *y = monitor.y;
193 if (popup_req.height > *height)
195 vpolicy = GTK_POLICY_ALWAYS;
197 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
198 hpolicy, vpolicy);
202 static void
203 anjuta_tree_combo_box_popup_for_device (AnjutaTreeComboBox *combo,
204 GdkDevice *device)
206 AnjutaTreeComboBoxPrivate *priv = combo->priv;
207 gint x, y, width, height;
208 GtkWidget *toplevel;
209 GdkDevice *keyboard, *pointer;
211 g_return_if_fail (ANJUTA_IS_TREE_COMBO_BOX (combo));
212 g_return_if_fail (GDK_IS_DEVICE (device));
214 if (!gtk_widget_get_realized (GTK_WIDGET (combo)))
215 return;
217 if (gtk_widget_get_mapped (priv->popup_window))
218 return;
220 if (priv->grab_pointer && priv->grab_keyboard)
221 return;
223 if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
225 keyboard = device;
226 pointer = gdk_device_get_associated_device (device);
228 else
230 pointer = device;
231 keyboard = gdk_device_get_associated_device (device);
234 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo));
236 if (GTK_IS_WINDOW (toplevel))
237 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
238 GTK_WINDOW (priv->popup_window));
240 gtk_widget_show_all (priv->scrolled_window);
241 anjuta_tree_combo_box_popup_position (combo, &x, &y, &width, &height);
243 gtk_widget_set_size_request (priv->popup_window, width, height);
244 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
246 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
247 TRUE);
249 /* popup */
250 gtk_widget_show (priv->popup_window);
252 gtk_widget_grab_focus (priv->popup_window);
253 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo),
254 TRUE);
256 if (!gtk_widget_has_focus (priv->tree_view))
257 gtk_widget_grab_focus (priv->tree_view);
259 if (!popup_grab_on_window (gtk_widget_get_window (priv->popup_window),
260 keyboard, pointer))
262 gtk_widget_hide (priv->popup_window);
263 return;
266 gtk_device_grab_add (priv->popup_window, pointer, TRUE);
267 priv->grab_pointer = pointer;
268 priv->grab_keyboard = keyboard;
271 static void
272 anjuta_tree_combo_box_popup (AnjutaTreeComboBox *combo)
274 GdkDevice *device;
276 device = gtk_get_current_event_device ();
278 if (!device)
280 GdkDeviceManager *device_manager;
281 GdkDisplay *display;
282 GList *devices;
284 display = gtk_widget_get_display (GTK_WIDGET (combo));
285 device_manager = gdk_display_get_device_manager (display);
287 /* No device was set, pick the first master device */
288 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
289 device = devices->data;
290 g_list_free (devices);
293 anjuta_tree_combo_box_popup_for_device (combo, device);
296 static void
297 anjuta_tree_combo_box_popdown (AnjutaTreeComboBox *combo)
299 AnjutaTreeComboBoxPrivate *priv = combo->priv;
301 g_return_if_fail (ANJUTA_IS_TREE_COMBO_BOX (combo));
303 if (!gtk_widget_get_realized (GTK_WIDGET (combo)))
304 return;
306 gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
307 gtk_widget_hide (priv->popup_window);
308 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo),
309 FALSE);
311 priv->grab_pointer = NULL;
312 priv->grab_keyboard = NULL;
315 static void
316 anjuta_tree_combo_box_unset_model (AnjutaTreeComboBox *combo)
318 AnjutaTreeComboBoxPrivate *priv = combo->priv;
320 if (priv->model != NULL)
322 g_object_unref (priv->model);
323 priv->model = NULL;
326 if (priv->active_row)
328 gtk_tree_row_reference_free (priv->active_row);
329 priv->active_row = NULL;
332 if (priv->cell_view)
334 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
337 if (priv->tree_view)
339 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), NULL);
343 static void
344 anjuta_tree_combo_box_popup_destroy (AnjutaTreeComboBox *combo)
346 AnjutaTreeComboBoxPrivate *priv = combo->priv;
348 if (priv->scroll_timer)
350 g_source_remove (priv->scroll_timer);
351 priv->scroll_timer = 0;
354 if (priv->resize_idle_id)
356 g_source_remove (priv->resize_idle_id);
357 priv->resize_idle_id = 0;
360 if (priv->popup_window)
362 gtk_widget_destroy (priv->popup_window);
363 priv->popup_window = NULL;
367 static void
368 anjuta_tree_combo_box_set_active_path (AnjutaTreeComboBox *combo,
369 GtkTreePath *path)
371 AnjutaTreeComboBoxPrivate *priv = combo->priv;
372 gboolean valid;
374 valid = gtk_tree_row_reference_valid (priv->active_row);
376 if (path && valid)
378 GtkTreePath *active_path;
379 gint cmp;
381 active_path = gtk_tree_row_reference_get_path (priv->active_row);
382 cmp = gtk_tree_path_compare (path, active_path);
383 gtk_tree_path_free (active_path);
384 if (cmp == 0) return;
387 if (priv->active_row)
389 gtk_tree_row_reference_free (priv->active_row);
390 priv->active_row = NULL;
393 if (!path)
395 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
396 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
397 if (!valid) return;
399 else
401 priv->active_row = gtk_tree_row_reference_new (priv->model, path);
403 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), path);
404 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view), path);
405 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view), path, NULL, FALSE);
406 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->tree_view), path, NULL, TRUE, 0.5, 0.0);
409 g_signal_emit (combo, signals[CHANGED], 0);
414 /* Callbacks functions
415 *---------------------------------------------------------------------------*/
417 static void
418 anjuta_tree_combo_box_auto_scroll (AnjutaTreeComboBox *combo,
419 gint x,
420 gint y)
422 GtkAdjustment *adj;
423 GtkAllocation allocation;
424 GtkWidget *tree_view = combo->priv->tree_view;
425 gdouble value;
427 gtk_widget_get_allocation (tree_view, &allocation);
429 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo->priv->scrolled_window));
430 if (adj && gtk_adjustment_get_upper (adj) - gtk_adjustment_get_lower (adj) > gtk_adjustment_get_page_size (adj))
432 if (x <= allocation.x &&
433 gtk_adjustment_get_lower (adj) < gtk_adjustment_get_value (adj))
435 value = gtk_adjustment_get_value (adj) - (allocation.x - x + 1);
436 gtk_adjustment_set_value (adj, value);
438 else if (x >= allocation.x + allocation.width &&
439 gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj) > gtk_adjustment_get_value (adj))
441 value = gtk_adjustment_get_value (adj) + (x - allocation.x - allocation.width + 1);
442 gtk_adjustment_set_value (adj, MAX (value, 0.0));
446 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo->priv->scrolled_window));
447 if (adj && gtk_adjustment_get_upper (adj) - gtk_adjustment_get_lower (adj) > gtk_adjustment_get_page_size (adj))
449 if (y <= allocation.y &&
450 gtk_adjustment_get_lower (adj) < gtk_adjustment_get_value (adj))
452 value = gtk_adjustment_get_value (adj) - (allocation.y - y + 1);
453 gtk_adjustment_set_value (adj, value);
455 else if (y >= allocation.height &&
456 gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj) > gtk_adjustment_get_value (adj))
458 value = gtk_adjustment_get_value (adj) + (y - allocation.height + 1);
459 gtk_adjustment_set_value (adj, MAX (value, 0.0));
464 static gboolean
465 anjuta_tree_combo_box_scroll_timeout (AnjutaTreeComboBox *combo)
467 AnjutaTreeComboBoxPrivate *priv = combo->priv;
468 gint x, y;
470 if (priv->auto_scroll)
472 gdk_window_get_device_position (gtk_widget_get_window (priv->tree_view),
473 priv->grab_pointer,
474 &x, &y, NULL);
475 anjuta_tree_combo_box_auto_scroll (combo, x, y);
478 return TRUE;
481 static gboolean
482 anjuta_tree_combo_box_key_press (GtkWidget *widget,
483 GdkEventKey *event,
484 gpointer user_data)
486 AnjutaTreeComboBox *combo = ANJUTA_TREE_COMBO_BOX (user_data);
487 GtkTreeIter iter;
489 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter ||
490 event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
492 GtkTreeModel *model = NULL;
494 anjuta_tree_combo_box_popdown (combo);
496 if (combo->priv->model)
498 GtkTreeSelection *sel;
500 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo->priv->tree_view));
502 if (gtk_tree_selection_get_selected (sel, &model, &iter))
503 anjuta_tree_combo_box_set_active_iter (combo, &iter);
506 return TRUE;
509 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
511 /* The tree hasn't managed the
512 * event, forward it to the combobox
514 if (gtk_bindings_activate_event (G_OBJECT (combo), event)) return TRUE;
517 return FALSE;
520 static gboolean
521 anjuta_tree_combo_box_enter_notify (GtkWidget *widget,
522 GdkEventCrossing *event,
523 gpointer data)
525 AnjutaTreeComboBox *combo = ANJUTA_TREE_COMBO_BOX (data);
527 combo->priv->auto_scroll = TRUE;
529 return TRUE;
532 static gboolean
533 anjuta_tree_combo_box_resize_idle (gpointer user_data)
535 AnjutaTreeComboBox *combo = ANJUTA_TREE_COMBO_BOX (user_data);
536 AnjutaTreeComboBoxPrivate *priv = combo->priv;
537 gint x, y, width, height;
539 if (priv->tree_view && gtk_widget_get_mapped (priv->popup_window))
541 anjuta_tree_combo_box_popup_position (combo, &x, &y, &width, &height);
543 gtk_widget_set_size_request (priv->popup_window, width, height);
544 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
547 priv->resize_idle_id = 0;
549 return FALSE;
552 static void
553 anjuta_tree_combo_box_popup_resize (AnjutaTreeComboBox *combo)
555 AnjutaTreeComboBoxPrivate *priv = combo->priv;
557 if (!priv->resize_idle_id)
559 priv->resize_idle_id = gdk_threads_add_idle (anjuta_tree_combo_box_resize_idle, combo);
563 static void
564 anjuta_tree_combo_box_row_expanded (GtkTreeModel *model,
565 GtkTreePath *path,
566 GtkTreeIter *iter,
567 gpointer user_data)
569 AnjutaTreeComboBox *combo = ANJUTA_TREE_COMBO_BOX (user_data);
571 anjuta_tree_combo_box_popup_resize (combo);
574 static gboolean
575 anjuta_tree_combo_box_button_pressed (GtkWidget *widget,
576 GdkEventButton *event,
577 gpointer user_data)
579 AnjutaTreeComboBox *combo = ANJUTA_TREE_COMBO_BOX (user_data);
580 AnjutaTreeComboBoxPrivate *priv = combo->priv;
582 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
584 if (ewidget == priv->popup_window)
585 return TRUE;
587 if ((ewidget != GTK_WIDGET (combo)) ||
588 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo)))
590 return FALSE;
593 if (!gtk_widget_has_focus (GTK_WIDGET (combo)))
595 gtk_widget_grab_focus (GTK_WIDGET (combo));
598 anjuta_tree_combo_box_popup_for_device (combo, event->device);
600 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo), TRUE);
602 priv->auto_scroll = FALSE;
603 if (priv->scroll_timer == 0)
604 priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
605 (GSourceFunc) anjuta_tree_combo_box_scroll_timeout,
606 combo);
608 priv->popup_in_progress = TRUE;
610 return TRUE;
613 static gboolean
614 anjuta_tree_combo_box_button_released (GtkWidget *widget,
615 GdkEventButton *event,
616 gpointer user_data)
618 gboolean ret;
619 GtkTreePath *path = NULL;
620 GtkTreeIter iter;
622 AnjutaTreeComboBox *combo = ANJUTA_TREE_COMBO_BOX (user_data);
623 AnjutaTreeComboBoxPrivate *priv = combo->priv;
625 gboolean popup_in_progress = FALSE;
627 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
629 if (priv->popup_in_progress)
631 popup_in_progress = TRUE;
632 priv->popup_in_progress = FALSE;
635 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
636 FALSE);
637 if (priv->scroll_timer)
639 g_source_remove (priv->scroll_timer);
640 priv->scroll_timer = 0;
643 if (ewidget != priv->tree_view)
645 if ((ewidget == GTK_WIDGET (combo)) &&
646 !popup_in_progress &&
647 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo)))
649 anjuta_tree_combo_box_popdown (combo);
650 return TRUE;
653 /* released outside treeview */
654 if (ewidget != GTK_WIDGET (combo))
656 anjuta_tree_combo_box_popdown (combo);
658 return TRUE;
661 return FALSE;
664 /* select something cool */
665 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
666 event->x, event->y,
667 &path,
668 NULL, NULL, NULL);
670 if (!ret)
671 return TRUE; /* clicked outside window? */
673 gtk_tree_model_get_iter (priv->model, &iter, path);
674 gtk_tree_path_free (path);
676 anjuta_tree_combo_box_popdown (combo);
678 anjuta_tree_combo_box_set_active_iter (combo, &iter);
680 return TRUE;
683 static void
684 anjuta_tree_combo_box_button_toggled (GtkWidget *widget,
685 gpointer user_data)
687 AnjutaTreeComboBox *combo = ANJUTA_TREE_COMBO_BOX (user_data);
689 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
691 anjuta_tree_combo_box_popup (combo);
693 else
695 anjuta_tree_combo_box_popdown (combo);
699 static void
700 anjuta_tree_combo_box_popup_setup (AnjutaTreeComboBox *combo)
702 AnjutaTreeComboBoxPrivate *priv = combo->priv;
703 GtkTreeSelection *sel;
704 GtkWidget *toplevel;
706 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
707 gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
708 GDK_WINDOW_TYPE_HINT_COMBO);
710 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo));
711 if (GTK_IS_WINDOW (toplevel))
713 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
714 GTK_WINDOW (priv->popup_window));
715 gtk_window_set_transient_for (GTK_WINDOW (priv->popup_window),
716 GTK_WINDOW (toplevel));
719 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
720 gtk_window_set_screen (GTK_WINDOW (priv->popup_window),
721 gtk_widget_get_screen (GTK_WIDGET (combo)));
723 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
724 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
725 GTK_POLICY_NEVER,
726 GTK_POLICY_NEVER);
727 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
728 GTK_SHADOW_IN);
729 gtk_container_add (GTK_CONTAINER (priv->popup_window),
730 priv->scrolled_window);
733 priv->tree_view = gtk_tree_view_new ();
734 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
735 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
736 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
737 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view), TRUE);
738 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view),
739 gtk_tree_view_column_new_with_area (priv->area));
740 gtk_container_add (GTK_CONTAINER (priv->scrolled_window),
741 priv->tree_view);
743 /* set sample/popup widgets */
744 g_signal_connect (priv->tree_view, "key-press-event",
745 G_CALLBACK (anjuta_tree_combo_box_key_press),
746 combo);
747 g_signal_connect (priv->tree_view, "enter-notify-event",
748 G_CALLBACK (anjuta_tree_combo_box_enter_notify),
749 combo);
750 g_signal_connect (priv->tree_view, "row-expanded",
751 G_CALLBACK (anjuta_tree_combo_box_row_expanded),
752 combo);
753 g_signal_connect (priv->tree_view, "row-collapsed",
754 G_CALLBACK (anjuta_tree_combo_box_row_expanded),
755 combo);
756 g_signal_connect (priv->popup_window, "button-press-event",
757 G_CALLBACK (anjuta_tree_combo_box_button_pressed),
758 combo);
759 g_signal_connect (priv->popup_window, "button-release-event",
760 G_CALLBACK (anjuta_tree_combo_box_button_released),
761 combo);
763 gtk_widget_show (priv->scrolled_window);
769 /* Public functions
770 *---------------------------------------------------------------------------*/
772 void
773 anjuta_tree_combo_box_set_model (AnjutaTreeComboBox *combo,
774 GtkTreeModel *model)
776 g_return_if_fail (ANJUTA_IS_TREE_COMBO_BOX (combo));
778 anjuta_tree_combo_box_unset_model (combo);
780 if (model != NULL)
782 AnjutaTreeComboBoxPrivate *priv = combo->priv;
784 priv->model = g_object_ref (model);
785 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), model);
786 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), model);
789 if (priv->active != -1)
791 /* If an index was set in advance, apply it now */
792 anjuta_tree_combo_box_set_active (combo, priv->active);
793 priv->active = -1;
795 else
797 GtkTreeIter iter;
798 gtk_tree_model_get_iter_first (model, &iter);
799 anjuta_tree_combo_box_set_active_iter (combo, &iter);
804 GtkTreeModel*
805 anjuta_tree_combo_box_get_model (AnjutaTreeComboBox *combo)
807 return combo->priv->model;
810 void
811 anjuta_tree_combo_box_set_active (AnjutaTreeComboBox *combo,
812 gint index)
814 GtkTreePath *path = NULL;
816 g_return_if_fail (ANJUTA_IS_TREE_COMBO_BOX (combo));
817 g_return_if_fail (index >= -1);
819 if (combo->priv->model == NULL)
821 /* Save index, in case the model is set after the index */
822 combo->priv->active = index;
823 return;
826 if (index != -1) path = gtk_tree_path_new_from_indices (index, -1);
828 anjuta_tree_combo_box_set_active_path (combo, path);
830 gtk_tree_path_free (path);
834 void
835 anjuta_tree_combo_box_set_active_iter (AnjutaTreeComboBox *combo,
836 GtkTreeIter *iter)
838 GtkTreePath *path = NULL;
840 g_return_if_fail (ANJUTA_IS_TREE_COMBO_BOX (combo));
842 if (iter)
844 path = gtk_tree_model_get_path (combo->priv->model, iter);
847 anjuta_tree_combo_box_set_active_path (combo, path);
849 gtk_tree_path_free (path);
852 gboolean
853 anjuta_tree_combo_box_get_active_iter (AnjutaTreeComboBox *combo,
854 GtkTreeIter *iter)
856 AnjutaTreeComboBoxPrivate *priv = combo->priv;
857 GtkTreePath *path;
858 gboolean valid = FALSE;
860 path = gtk_tree_row_reference_get_path (priv->active_row);
861 if (path)
863 valid = gtk_tree_model_get_iter (priv->model, iter, path);
864 gtk_tree_path_free (path);
867 return valid;
871 GtkWidget *
872 anjuta_tree_combo_box_new (void)
874 return g_object_new (ANJUTA_TYPE_TREE_COMBO_BOX, NULL);
879 /* Implement GtkCellLayout interface
880 *---------------------------------------------------------------------------*/
882 static GtkCellArea *
883 anjuta_tree_combo_box_cell_layout_get_area (GtkCellLayout *cell_layout)
885 AnjutaTreeComboBox *combo = ANJUTA_TREE_COMBO_BOX (cell_layout);
886 AnjutaTreeComboBoxPrivate *priv = combo->priv;
888 return priv->area;
892 static void
893 anjuta_tree_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
895 iface->get_area = anjuta_tree_combo_box_cell_layout_get_area;
900 /* Implement GtkWidget functions
901 *---------------------------------------------------------------------------*/
903 static void
904 anjuta_tree_combo_box_destroy (GtkWidget *widget)
906 AnjutaTreeComboBox *combo = ANJUTA_TREE_COMBO_BOX (widget);
907 AnjutaTreeComboBoxPrivate *priv = combo->priv;
909 anjuta_tree_combo_box_popdown (combo);
911 GTK_WIDGET_CLASS (anjuta_tree_combo_box_parent_class)->destroy (widget);
913 priv->cell_view = NULL;
914 priv->tree_view = NULL;
919 /* Implement GObject functions
920 *---------------------------------------------------------------------------*/
922 static void
923 anjuta_tree_combo_box_dispose (GObject *object)
925 AnjutaTreeComboBox *combo = ANJUTA_TREE_COMBO_BOX (object);
926 AnjutaTreeComboBoxPrivate *priv = combo->priv;
928 if (priv->area)
930 g_object_unref (priv->area);
931 priv->area = NULL;
934 anjuta_tree_combo_box_unset_model (combo);
936 anjuta_tree_combo_box_popup_destroy (combo);
938 G_OBJECT_CLASS (anjuta_tree_combo_box_parent_class)->dispose (object);
941 static void
942 anjuta_tree_combo_box_init (AnjutaTreeComboBox *combo)
944 AnjutaTreeComboBoxPrivate *priv;
945 GtkWidget *box, *sep, *arrow;
947 priv = G_TYPE_INSTANCE_GET_PRIVATE (combo,
948 ANJUTA_TYPE_TREE_COMBO_BOX,
949 AnjutaTreeComboBoxPrivate);
950 combo->priv = priv;
952 priv->model = NULL;
953 priv->active = -1;
954 priv->active_row = NULL;
956 gtk_widget_push_composite_child ();
958 gtk_widget_set_halign (GTK_WIDGET (combo), GTK_ALIGN_FILL);
959 gtk_widget_show (GTK_WIDGET (combo));
961 g_signal_connect (combo, "button-press-event",
962 G_CALLBACK (anjuta_tree_combo_box_button_pressed), combo);
963 g_signal_connect (combo, "toggled",
964 G_CALLBACK (anjuta_tree_combo_box_button_toggled), combo);
967 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
968 gtk_container_add (GTK_CONTAINER (combo), box);
969 gtk_widget_show (box);
971 priv->area = gtk_cell_area_box_new ();
972 g_object_ref_sink (priv->area);
974 priv->cell_view = gtk_cell_view_new_with_context (priv->area, NULL);
975 gtk_box_pack_start (GTK_BOX (box), priv->cell_view, TRUE, TRUE, 0);
976 gtk_widget_show (priv->cell_view);
978 sep = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
979 gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0);
980 gtk_widget_show (sep);
982 arrow = gtk_arrow_new (GTK_ARROW_DOWN,GTK_SHADOW_NONE);
983 gtk_box_pack_start (GTK_BOX (box), arrow, FALSE, FALSE, 0);
984 gtk_widget_show (arrow);
986 gtk_widget_pop_composite_child ();
988 anjuta_tree_combo_box_popup_setup (combo);
991 static void
992 anjuta_tree_combo_box_class_init (AnjutaTreeComboBoxClass * class)
994 GObjectClass *gobject_class;
995 GtkWidgetClass *widget_class;
996 GtkBindingSet *binding_set;
998 widget_class = GTK_WIDGET_CLASS (class);
999 widget_class->destroy = anjuta_tree_combo_box_destroy;
1001 gobject_class = G_OBJECT_CLASS (class);
1002 gobject_class->dispose = anjuta_tree_combo_box_dispose;
1004 /* Signals */
1005 signals[CHANGED] = g_signal_new ("changed",
1006 G_OBJECT_CLASS_TYPE (class),
1007 G_SIGNAL_RUN_LAST,
1008 G_STRUCT_OFFSET (AnjutaTreeComboBoxClass, changed),
1009 NULL, NULL,
1010 g_cclosure_marshal_VOID__VOID,
1011 G_TYPE_NONE, 0);
1013 signals[POPUP] = g_signal_new_class_handler ("popup",
1014 G_OBJECT_CLASS_TYPE (class),
1015 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1016 G_CALLBACK (anjuta_tree_combo_box_popup),
1017 NULL, NULL,
1018 g_cclosure_marshal_VOID__VOID,
1019 G_TYPE_NONE, 0);
1021 signals[POPDOWN] = g_signal_new_class_handler ("popdown",
1022 G_OBJECT_CLASS_TYPE (class),
1023 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1024 G_CALLBACK (anjuta_tree_combo_box_popdown),
1025 NULL, NULL,
1026 g_cclosure_marshal_VOID__VOID,
1027 G_TYPE_NONE, 0);
1029 /* Key bindings */
1030 binding_set = gtk_binding_set_by_class (widget_class);
1032 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_MOD1_MASK,
1033 "popup", 0);
1034 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_MOD1_MASK,
1035 "popup", 0);
1036 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_MOD1_MASK,
1037 "popdown", 0);
1038 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_MOD1_MASK,
1039 "popdown", 0);
1040 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
1041 "popdown", 0);
1043 g_type_class_add_private (class, sizeof (AnjutaTreeComboBoxPrivate));