1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 * anjuta-notebook-tabber
4 * Copyright (C) Johannes Schmid 2010 <jhs@gnome.org>
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
;
28 GdkWindow
* event_window
;
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
);
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
);
55 anjuta_tabber_finalize (GObject
*object
)
57 G_OBJECT_CLASS (anjuta_tabber_parent_class
)->finalize (object
);
61 * anjuta_tabber_notebook_page_removed:
63 * Called when a page is removed from the associated notebook. Removes
64 * the tab for this page
67 anjuta_tabber_notebook_page_removed (GtkNotebook
* notebook
,
68 GtkWidget
* notebook_child
,
72 GtkWidget
* child
= g_list_nth_data (tabber
->priv
->children
, page_num
);
73 gtk_container_remove (GTK_CONTAINER (tabber
), child
);
77 anjuta_tabber_notebook_switch_page (GtkNotebook
* notebook
,
78 GtkNotebookPage
* page
,
82 tabber
->priv
->active_page
= page_num
;
83 gtk_widget_queue_draw (GTK_WIDGET (tabber
));
87 * anjuta_tabber_connect_notebook:
89 * Connect signals to associated notebook
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
);
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
);
112 tabber
->priv
->notebook
= g_value_get_object (value
);
113 anjuta_tabber_connect_notebook (tabber
);
116 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
122 anjuta_tabber_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
124 g_return_if_fail (ANJUTA_IS_TABBER (object
));
129 g_value_set_object (value
, object
);
132 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
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
146 anjuta_tabber_get_padding(GtkWidget
* widget
)
149 gint tab_border
= gtk_container_get_border_width (GTK_CONTAINER(widget
));
152 gtk_widget_style_get (widget
,
153 "focus-line-width", &focus_width
,
156 padding
= 2 * (focus_width
+ tab_border
);
161 anjuta_tabber_size_request(GtkWidget
* widget
, GtkRequisition
* req
)
163 g_return_if_fail (ANJUTA_IS_TABBER (widget
));
165 AnjutaTabber
* tabber
= ANJUTA_TABBER (widget
);
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
;
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
);
182 anjuta_tabber_size_allocate(GtkWidget
* widget
, GtkAllocation
* allocation
)
184 g_return_if_fail (ANJUTA_IS_TABBER (widget
));
185 AnjutaTabber
* tabber
= ANJUTA_TABBER (widget
);
189 switch (gtk_widget_get_direction (widget
))
191 case GTK_TEXT_DIR_RTL
:
192 x
= allocation
->x
+ allocation
->width
;
194 case GTK_TEXT_DIR_LTR
:
199 gint y
= allocation
->y
;
200 gint padding
= anjuta_tabber_get_padding (widget
);
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
);
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
;
236 case GTK_TEXT_DIR_LTR
:
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
);
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
);
254 gint padding
= anjuta_tabber_get_padding (widget
);
256 for (child
= tabber
->priv
->children
; child
!= NULL
; child
= g_list_next (child
))
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
,
280 gtk_container_propagate_expose (GTK_CONTAINER (tabber
),
281 GTK_WIDGET(child
->data
), event
);
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
296 anjuta_tabber_get_widget_coordinates (GtkWidget
*widget
,
301 GdkWindow
*window
= ((GdkEventAny
*)event
)->window
;
304 if (!gdk_event_get_coords (event
, &tx
, &ty
))
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
);
315 window
= gdk_window_get_parent (window
);
330 anjuta_tabber_button_press_event (GtkWidget
* widget
, GdkEventButton
* event
)
332 AnjutaTabber
* tabber
= ANJUTA_TABBER (widget
);
335 if (event
->button
== 1)
338 if (!anjuta_tabber_get_widget_coordinates (widget
, (GdkEvent
*) event
, &x
, &y
))
341 for (child
= tabber
->priv
->children
; child
!= NULL
; child
= g_list_next (child
))
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
);
360 anjuta_tabber_realize (GtkWidget
*widget
)
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
);
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
);
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
);
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
);
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
));
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
);
450 gtk_widget_queue_resize (GTK_WIDGET (tabber
));
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
);
462 for (child
= tabber
->priv
->children
; child
!= NULL
; child
= g_list_next (child
))
464 (* callback
) (GTK_WIDGET(child
->data
), callback_data
);
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
,
494 g_param_spec_object ("notebook",
496 "GtkNotebook the tabber is associated with",
498 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_WRITABLE
));
500 g_type_class_add_private (klass
, sizeof (AnjutaTabberPriv
));
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
)
514 tabber
= GTK_WIDGET (g_object_new (ANJUTA_TYPE_TABBER
, "notebook", notebook
, NULL
));
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
);