Merge branch 'master' into git-shell
[anjuta.git] / libanjuta / anjuta-tabber.c
blob1757f40b5477d9b9a4e9744d851a375a60132d7e
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta-notebook-tabber
4 * Copyright (C) Johannes Schmid 2010 <jhs@gnome.org>
5 *
6 * anjuta-notebook-tabber is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * anjuta-notebook-tabber is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "anjuta-tabber.h"
22 struct _AnjutaTabberPriv
24 GtkNotebook* notebook;
25 GList* children;
26 gint active_page;
28 GdkWindow* event_window;
31 enum
33 PROP_0,
34 PROP_NOTEBOOK
37 #define ANJUTA_TABBER_GET_PRIVATE(o) \
38 (G_TYPE_INSTANCE_GET_PRIVATE ((o), ANJUTA_TYPE_TABBER, AnjutaTabberPriv))
41 G_DEFINE_TYPE (AnjutaTabber, anjuta_tabber, GTK_TYPE_CONTAINER);
43 static void
44 anjuta_tabber_init (AnjutaTabber *object)
46 AnjutaTabber* tabber = ANJUTA_TABBER (object);
47 tabber->priv = ANJUTA_TABBER_GET_PRIVATE (tabber);
48 tabber->priv->children = NULL;
49 tabber->priv->active_page = 0;
51 gtk_widget_set_has_window (GTK_WIDGET(tabber), FALSE);
54 static void
55 anjuta_tabber_finalize (GObject *object)
57 G_OBJECT_CLASS (anjuta_tabber_parent_class)->finalize (object);
60 /**
61 * anjuta_tabber_notebook_page_removed:
63 * Called when a page is removed from the associated notebook. Removes
64 * the tab for this page
66 static void
67 anjuta_tabber_notebook_page_removed (GtkNotebook* notebook,
68 GtkWidget* notebook_child,
69 gint page_num,
70 AnjutaTabber* tabber)
72 GtkWidget* child = g_list_nth_data (tabber->priv->children, page_num);
73 gtk_container_remove (GTK_CONTAINER (tabber), child);
76 static void
77 anjuta_tabber_notebook_switch_page (GtkNotebook* notebook,
78 GtkNotebookPage* page,
79 guint page_num,
80 AnjutaTabber* tabber)
82 tabber->priv->active_page = page_num;
83 gtk_widget_queue_draw (GTK_WIDGET (tabber));
86 /**
87 * anjuta_tabber_connect_notebook:
89 * Connect signals to associated notebook
92 static void
93 anjuta_tabber_connect_notebook (AnjutaTabber* tabber)
95 g_signal_connect (tabber->priv->notebook, "page-removed",
96 G_CALLBACK(anjuta_tabber_notebook_page_removed), tabber);
97 g_signal_connect (tabber->priv->notebook, "switch-page",
98 G_CALLBACK(anjuta_tabber_notebook_switch_page), tabber);
102 static void
103 anjuta_tabber_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
105 g_return_if_fail (ANJUTA_IS_TABBER (object));
107 AnjutaTabber* tabber = ANJUTA_TABBER (object);
109 switch (prop_id)
111 case PROP_NOTEBOOK:
112 tabber->priv->notebook = g_value_get_object (value);
113 anjuta_tabber_connect_notebook (tabber);
114 break;
115 default:
116 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
117 break;
121 static void
122 anjuta_tabber_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
124 g_return_if_fail (ANJUTA_IS_TABBER (object));
126 switch (prop_id)
128 case PROP_NOTEBOOK:
129 g_value_set_object (value, object);
130 break;
131 default:
132 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
133 break;
138 * anjuta_tabber_get_padding:
139 * @widget: a AnjutaTabber widget
141 * Receives the padding from the widget style
143 * Returns: the padding used for the tabs
145 static gint
146 anjuta_tabber_get_padding(GtkWidget* widget)
148 gint focus_width;
149 gint tab_border = gtk_container_get_border_width (GTK_CONTAINER(widget));
150 gint padding;
152 gtk_widget_style_get (widget,
153 "focus-line-width", &focus_width,
154 NULL);
156 padding = 2 * (focus_width + tab_border);
157 return padding;
160 static void
161 anjuta_tabber_size_request(GtkWidget* widget, GtkRequisition* req)
163 g_return_if_fail (ANJUTA_IS_TABBER (widget));
165 AnjutaTabber* tabber = ANJUTA_TABBER (widget);
166 GList* child;
167 gint padding = anjuta_tabber_get_padding (widget);
169 for (child = tabber->priv->children; child != NULL; child = g_list_next (child))
171 GtkRequisition child_req;
172 GtkStyle *style;
174 gtk_widget_size_request (GTK_WIDGET (child->data), &child_req);
175 style = gtk_widget_get_style (widget);
176 req->width += child_req.width + 2 * (style->xthickness + padding);
177 req->height = MAX(req->height, child_req.height + 2 * style->ythickness);
181 static void
182 anjuta_tabber_size_allocate(GtkWidget* widget, GtkAllocation* allocation)
184 g_return_if_fail (ANJUTA_IS_TABBER (widget));
185 AnjutaTabber* tabber = ANJUTA_TABBER (widget);
187 GList* child;
188 gint x;
189 switch (gtk_widget_get_direction (widget))
191 case GTK_TEXT_DIR_RTL:
192 x = allocation->x + allocation->width;
193 break;
194 case GTK_TEXT_DIR_LTR:
195 default:
196 x = allocation->x;
199 gint y = allocation->y;
200 gint padding = anjuta_tabber_get_padding (widget);
201 gint child_width;
202 gint n_children = g_list_length (tabber->priv->children);
204 gtk_widget_set_allocation (widget, allocation);
206 if (gtk_widget_get_realized (widget))
208 gdk_window_move_resize (tabber->priv->event_window,
209 allocation->x, allocation->y,
210 allocation->width, allocation->height);
211 if (gtk_widget_get_mapped (widget))
212 gdk_window_show_unraised (tabber->priv->event_window);
215 if (n_children > 0)
217 GtkStyle *style = gtk_widget_get_style (widget);
219 child_width = allocation->width / n_children - 2 * (padding + style->xthickness);
221 for (child = tabber->priv->children; child != NULL; child = g_list_next (child))
223 GtkRequisition child_req;
224 GtkAllocation child_alloc;
225 gtk_widget_get_child_requisition (GTK_WIDGET (child->data), &child_req);
227 child_alloc.width = child_width;
228 child_alloc.height = MAX(child_req.height, allocation->height);
230 switch (gtk_widget_get_direction (widget))
232 case GTK_TEXT_DIR_RTL:
233 child_alloc.x = x - child_width - style->xthickness - padding;
234 x -= child_width + style->xthickness + 2 * padding;
235 break;
236 case GTK_TEXT_DIR_LTR:
237 default:
238 child_alloc.x = x + style->xthickness + padding;
239 x += child_width + style->xthickness + 2 * padding;
241 child_alloc.y = y + style->ythickness + padding;
243 gtk_widget_size_allocate (GTK_WIDGET (child->data), &child_alloc);
248 static gboolean
249 anjuta_tabber_expose_event (GtkWidget* widget, GdkEventExpose *event)
251 g_return_val_if_fail (ANJUTA_IS_TABBER (widget), FALSE);
252 AnjutaTabber* tabber = ANJUTA_TABBER (widget);
253 GList* child;
254 gint padding = anjuta_tabber_get_padding (widget);
256 for (child = tabber->priv->children; child != NULL; child = g_list_next (child))
258 GtkAllocation alloc;
259 GtkStyle *style;
260 GtkStateType state = (g_list_nth (tabber->priv->children, tabber->priv->active_page) == child) ?
261 GTK_STATE_NORMAL : GTK_STATE_ACTIVE;
262 gtk_widget_get_allocation (GTK_WIDGET (child->data), &alloc);
264 style = gtk_widget_get_style (widget);
266 alloc.x -= style->xthickness + padding;
267 alloc.y -= style->ythickness;
268 alloc.width += 2 * (style->xthickness + padding);
269 alloc.height += style->ythickness;
271 gtk_paint_extension (style,
272 gtk_widget_get_window (widget),
273 state, GTK_SHADOW_OUT,
274 NULL, widget, "tab",
275 alloc.x,
276 alloc.y,
277 alloc.width,
278 alloc.height,
279 GTK_POS_BOTTOM);
280 gtk_container_propagate_expose (GTK_CONTAINER (tabber),
281 GTK_WIDGET(child->data), event);
283 return FALSE;
287 * anjuta_tabber_get_widget_coordintes
288 * @widget: widget for the coordinates
289 * @event: event to get coordinates from
290 * @x: return location for x coordinate
291 * @y: return location for y coordinate
293 * Returns: TRUE if coordinates were set, FALSE otherwise
295 static gboolean
296 anjuta_tabber_get_widget_coordinates (GtkWidget *widget,
297 GdkEvent *event,
298 gint *x,
299 gint *y)
301 GdkWindow *window = ((GdkEventAny *)event)->window;
302 gdouble tx, ty;
304 if (!gdk_event_get_coords (event, &tx, &ty))
305 return FALSE;
307 while (window && window != gtk_widget_get_window (widget))
309 gint window_x, window_y;
311 gdk_window_get_position (window, &window_x, &window_y);
312 tx += window_x;
313 ty += window_y;
315 window = gdk_window_get_parent (window);
318 if (window)
320 *x = tx;
321 *y = ty;
323 return TRUE;
325 else
326 return FALSE;
329 static gboolean
330 anjuta_tabber_button_press_event (GtkWidget* widget, GdkEventButton* event)
332 AnjutaTabber* tabber = ANJUTA_TABBER (widget);
333 GList* child;
335 if (event->button == 1)
337 gint x, y;
338 if (!anjuta_tabber_get_widget_coordinates (widget, (GdkEvent*) event, &x, &y))
339 return FALSE;
341 for (child = tabber->priv->children; child != NULL; child = g_list_next (child))
343 GtkAllocation alloc;
344 gtk_widget_get_allocation (GTK_WIDGET (child->data), &alloc);
346 if (alloc.x <= x && (alloc.x + alloc.width) >= x &&
347 alloc.y <= y && (alloc.y + alloc.height) >= y)
349 gint page = g_list_position (tabber->priv->children, child);
350 gtk_notebook_set_current_page (tabber->priv->notebook, page);
351 return TRUE;
356 return FALSE;
359 static void
360 anjuta_tabber_realize (GtkWidget *widget)
362 GdkWindow* window;
363 GdkWindowAttr attributes;
364 GtkAllocation allocation;
365 AnjutaTabber* tabber = ANJUTA_TABBER (widget);
367 gtk_widget_set_realized (widget, TRUE);
369 window = gtk_widget_get_parent_window (widget);
370 gtk_widget_set_window (widget, window);
371 g_object_ref (window);
373 gtk_widget_get_allocation (widget, &allocation);
375 attributes.window_type = GDK_WINDOW_CHILD;
376 attributes.x = allocation.x;
377 attributes.y = allocation.y;
378 attributes.width = allocation.width;
379 attributes.height = allocation.height;
380 attributes.wclass = GDK_INPUT_ONLY;
381 attributes.event_mask = gtk_widget_get_events (widget);
382 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK);
384 tabber->priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
385 &attributes, GDK_WA_X | GDK_WA_Y);
386 gdk_window_set_user_data (tabber->priv->event_window, tabber);
388 gtk_widget_style_attach (widget);
391 static void
392 anjuta_tabber_unrealize (GtkWidget *widget)
394 AnjutaTabber* tabber = ANJUTA_TABBER (widget);
395 gdk_window_set_user_data (tabber->priv->event_window, NULL);
396 gdk_window_destroy (tabber->priv->event_window);
397 tabber->priv->event_window = NULL;
399 GTK_WIDGET_CLASS (anjuta_tabber_parent_class)->unrealize (widget);
402 static void
403 anjuta_tabber_map (GtkWidget* widget)
405 AnjutaTabber* tabber = ANJUTA_TABBER (widget);
406 gtk_widget_set_mapped (widget, TRUE);
408 gdk_window_show_unraised (tabber->priv->event_window);
410 GTK_WIDGET_CLASS (anjuta_tabber_parent_class)->map (widget);
413 static void
414 anjuta_tabber_unmap (GtkWidget* widget)
416 AnjutaTabber* tabber = ANJUTA_TABBER (widget);
418 gtk_widget_set_mapped (widget, FALSE);
419 gdk_window_hide (tabber->priv->event_window);
421 GTK_WIDGET_CLASS (anjuta_tabber_parent_class)->unmap (widget);
425 static void
426 anjuta_tabber_add (GtkContainer* container, GtkWidget* widget)
428 g_return_if_fail (ANJUTA_IS_TABBER (container));
429 g_return_if_fail (GTK_IS_WIDGET (widget));
431 AnjutaTabber* tabber = ANJUTA_TABBER (container);
433 tabber->priv->children = g_list_append (tabber->priv->children, widget);
434 gtk_widget_set_parent (widget, GTK_WIDGET (tabber));
437 static void
438 anjuta_tabber_remove (GtkContainer* container, GtkWidget* widget)
440 g_return_if_fail (ANJUTA_IS_TABBER (container));
441 g_return_if_fail (GTK_IS_WIDGET (widget));
443 AnjutaTabber* tabber = ANJUTA_TABBER (container);
444 gboolean visible = gtk_widget_get_visible (widget);
446 gtk_widget_unparent (widget);
447 tabber->priv->children = g_list_remove (tabber->priv->children, widget);
449 if (visible)
450 gtk_widget_queue_resize (GTK_WIDGET (tabber));
453 static void
454 anjuta_tabber_forall (GtkContainer* container,
455 gboolean include_internals,
456 GtkCallback callback,
457 gpointer callback_data)
459 g_return_if_fail (ANJUTA_IS_TABBER (container));
460 AnjutaTabber* tabber = ANJUTA_TABBER (container);
461 GList* child;
462 for (child = tabber->priv->children; child != NULL; child = g_list_next (child))
464 (* callback) (GTK_WIDGET(child->data), callback_data);
468 static void
469 anjuta_tabber_class_init (AnjutaTabberClass *klass)
471 GObjectClass* object_class = G_OBJECT_CLASS (klass);
472 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
473 GtkContainerClass* container_class = GTK_CONTAINER_CLASS (klass);
475 object_class->finalize = anjuta_tabber_finalize;
476 object_class->set_property = anjuta_tabber_set_property;
477 object_class->get_property = anjuta_tabber_get_property;
479 widget_class->size_request = anjuta_tabber_size_request;
480 widget_class->size_allocate = anjuta_tabber_size_allocate;
481 widget_class->expose_event = anjuta_tabber_expose_event;
482 widget_class->button_press_event = anjuta_tabber_button_press_event;
483 widget_class->realize = anjuta_tabber_realize;
484 widget_class->unrealize = anjuta_tabber_unrealize;
485 widget_class->map = anjuta_tabber_map;
486 widget_class->unmap = anjuta_tabber_unmap;
488 container_class->add = anjuta_tabber_add;
489 container_class->remove = anjuta_tabber_remove;
490 container_class->forall = anjuta_tabber_forall;
492 g_object_class_install_property (object_class,
493 PROP_NOTEBOOK,
494 g_param_spec_object ("notebook",
495 "a GtkNotebook",
496 "GtkNotebook the tabber is associated with",
497 G_TYPE_OBJECT,
498 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
500 g_type_class_add_private (klass, sizeof (AnjutaTabberPriv));
504 * anjuta_tabber_new:
505 * @notebook: the GtkNotebook the tabber should be associated with
507 * Creates a new AnjutaTabber widget
509 * Returns: newly created AnjutaTabber widget
511 GtkWidget* anjuta_tabber_new (GtkNotebook* notebook)
513 GtkWidget* tabber;
514 tabber = GTK_WIDGET (g_object_new (ANJUTA_TYPE_TABBER, "notebook", notebook, NULL));
516 return tabber;
520 * anjuta_tabber_add_tab:
521 * @tabber: a AnjutaTabber widget
522 * @tab_label: widget used as tab label
524 * Adds a tab to the AnjutaTabber widget
526 void anjuta_tabber_add_tab (AnjutaTabber* tabber, GtkWidget* tab_label)
528 gtk_container_add (GTK_CONTAINER (tabber), tab_label);