1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * gdl-dock-tablabel.c
4 *
5 * This file is part of the GNOME Devtools Libraries.
6 *
7 * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include "gdl-i18n.h"
29 #include <gtk/gtk.h>
31 #include "gdl-dock-tablabel.h"
32 #include "gdl-tools.h"
33 #include "gdl-dock-item.h"
34 #include "libgdlmarshal.h"
37 /* ----- Private prototypes ----- */
39 static void gdl_dock_tablabel_class_init (GdlDockTablabelClass *klass);
40 static void gdl_dock_tablabel_instance_init (GdlDockTablabel *tablabel);
42 static void gdl_dock_tablabel_set_property (GObject *object,
43 guint prop_id,
44 const GValue *value,
45 GParamSpec *pspec);
46 static void gdl_dock_tablabel_get_property (GObject *object,
47 guint prop_id,
48 GValue *value,
49 GParamSpec *pspec);
51 static void gdl_dock_tablabel_item_notify (GObject *master,
52 GParamSpec *pspec,
53 gpointer data);
55 static void gdl_dock_tablabel_size_request (GtkWidget *widget,
56 GtkRequisition *requisition);
57 static void gdl_dock_tablabel_size_allocate (GtkWidget *widget,
58 GtkAllocation *allocation);
60 static void gdl_dock_tablabel_paint (GtkWidget *widget,
61 GdkEventExpose *event);
62 static gint gdl_dock_tablabel_expose (GtkWidget *widget,
63 GdkEventExpose *event);
65 static gboolean gdl_dock_tablabel_button_event (GtkWidget *widget,
66 GdkEventButton *event);
67 static gboolean gdl_dock_tablabel_motion_event (GtkWidget *widget,
68 GdkEventMotion *event);
70 static void gdl_dock_tablabel_realize (GtkWidget *widget);
71 static void gdl_dock_tablabel_unrealize (GtkWidget *widget);
72 static void gdl_dock_tablabel_map (GtkWidget *widget);
73 static void gdl_dock_tablabel_unmap (GtkWidget *widget);
75 /* ----- Private data types and variables ----- */
77 #define DEFAULT_DRAG_HANDLE_SIZE 10
78 #define HANDLE_RATIO 1.0
80 enum {
81 BUTTON_PRESSED_HANDLE,
82 LAST_SIGNAL
83 };
85 enum {
86 PROP_0,
87 PROP_ITEM
88 };
91 static guint dock_tablabel_signals [LAST_SIGNAL] = { 0 };
94 /* ----- Private interface ----- */
96 GDL_CLASS_BOILERPLATE (GdlDockTablabel, gdl_dock_tablabel,
97 GtkBin, GTK_TYPE_BIN);
99 static void
100 gdl_dock_tablabel_class_init (GdlDockTablabelClass *klass)
101 {
102 GObjectClass *g_object_class;
103 GtkObjectClass *object_class;
104 GtkWidgetClass *widget_class;
105 GtkContainerClass *container_class;
107 g_object_class = G_OBJECT_CLASS (klass);
108 object_class = GTK_OBJECT_CLASS (klass);
109 widget_class = GTK_WIDGET_CLASS (klass);
110 container_class = GTK_CONTAINER_CLASS (klass);
112 g_object_class->set_property = gdl_dock_tablabel_set_property;
113 g_object_class->get_property = gdl_dock_tablabel_get_property;
115 widget_class->size_request = gdl_dock_tablabel_size_request;
116 widget_class->size_allocate = gdl_dock_tablabel_size_allocate;
117 widget_class->expose_event = gdl_dock_tablabel_expose;
118 widget_class->button_press_event = gdl_dock_tablabel_button_event;
119 widget_class->button_release_event = gdl_dock_tablabel_button_event;
120 widget_class->motion_notify_event = gdl_dock_tablabel_motion_event;
121 widget_class->realize = gdl_dock_tablabel_realize;
122 widget_class->unrealize = gdl_dock_tablabel_unrealize;
123 widget_class->map = gdl_dock_tablabel_map;
124 widget_class->unmap = gdl_dock_tablabel_unmap;
126 g_object_class_install_property (
127 g_object_class, PROP_ITEM,
128 g_param_spec_object ("item", _("Controlling dock item"),
129 _("Dockitem which 'owns' this tablabel"),
130 GDL_TYPE_DOCK_ITEM,
131 G_PARAM_READWRITE));
133 dock_tablabel_signals [BUTTON_PRESSED_HANDLE] =
134 g_signal_new ("button_pressed_handle",
135 G_TYPE_FROM_CLASS (klass),
136 G_SIGNAL_RUN_LAST,
137 G_STRUCT_OFFSET (GdlDockTablabelClass,
138 button_pressed_handle),
139 NULL, NULL,
140 gdl_marshal_VOID__BOXED,
141 G_TYPE_NONE,
142 1,
143 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
145 klass->button_pressed_handle = NULL;
146 }
148 static void
149 gdl_dock_tablabel_instance_init (GdlDockTablabel *tablabel)
150 {
151 GtkWidget *widget;
152 GtkWidget *label_widget;
154 widget = GTK_WIDGET (tablabel);
156 tablabel->drag_handle_size = DEFAULT_DRAG_HANDLE_SIZE;
157 tablabel->item = NULL;
159 label_widget = gtk_label_new ("Dock item");
160 gtk_container_add (GTK_CONTAINER (tablabel), label_widget);
161 gtk_widget_show (label_widget);
163 tablabel->active = FALSE;
164 gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_ACTIVE);
165 }
167 static void
168 gdl_dock_tablabel_set_property (GObject *object,
169 guint prop_id,
170 const GValue *value,
171 GParamSpec *pspec)
172 {
173 GdlDockTablabel *tablabel;
174 GtkBin *bin;
176 tablabel = GDL_DOCK_TABLABEL (object);
178 switch (prop_id) {
179 case PROP_ITEM:
180 if (tablabel->item) {
181 g_object_remove_weak_pointer (G_OBJECT (tablabel->item),
182 (gpointer *) &tablabel->item);
183 g_signal_handlers_disconnect_by_func (
184 tablabel->item, gdl_dock_tablabel_item_notify, tablabel);
185 };
187 tablabel->item = g_value_get_object (value);
188 if (tablabel->item) {
189 gboolean locked;
190 gchar *long_name;
192 g_object_add_weak_pointer (G_OBJECT (tablabel->item),
193 (gpointer *) &tablabel->item);
195 g_signal_connect (tablabel->item, "notify::locked",
196 G_CALLBACK (gdl_dock_tablabel_item_notify),
197 tablabel);
198 g_signal_connect (tablabel->item, "notify::long_name",
199 G_CALLBACK (gdl_dock_tablabel_item_notify),
200 tablabel);
201 g_signal_connect (tablabel->item, "notify::grip_size",
202 G_CALLBACK (gdl_dock_tablabel_item_notify),
203 tablabel);
205 g_object_get (tablabel->item,
206 "locked", &locked,
207 "long-name", &long_name,
208 "grip-size", &tablabel->drag_handle_size,
209 NULL);
211 if (locked)
212 tablabel->drag_handle_size = 0;
214 bin = GTK_BIN (tablabel);
215 if (bin->child && g_object_class_find_property (
216 G_OBJECT_GET_CLASS (bin->child), "label"))
217 g_object_set (bin->child, "label", long_name, NULL);
218 g_free (long_name);
219 };
220 break;
222 default:
223 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224 break;
225 }
226 }
228 static void
229 gdl_dock_tablabel_get_property (GObject *object,
230 guint prop_id,
231 GValue *value,
232 GParamSpec *pspec)
233 {
234 GdlDockTablabel *tablabel;
236 tablabel = GDL_DOCK_TABLABEL (object);
238 switch (prop_id) {
239 case PROP_ITEM:
240 g_value_set_object (value, tablabel->item);
241 break;
242 default:
243 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244 break;
245 }
246 }
248 static void
249 gdl_dock_tablabel_item_notify (GObject *master,
250 GParamSpec *pspec,
251 gpointer data)
252 {
253 GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (data);
254 gboolean locked;
255 gchar *label;
256 GtkBin *bin;
258 g_object_get (master,
259 "locked", &locked,
260 "grip-size", &tablabel->drag_handle_size,
261 "long-name", &label,
262 NULL);
264 if (locked)
265 tablabel->drag_handle_size = 0;
267 bin = GTK_BIN (tablabel);
268 if (bin->child && g_object_class_find_property (
269 G_OBJECT_GET_CLASS (bin->child), "label"))
270 g_object_set (bin->child, "label", label, NULL);
271 g_free (label);
273 gtk_widget_queue_resize (GTK_WIDGET (tablabel));
274 }
276 static void
277 gdl_dock_tablabel_size_request (GtkWidget *widget,
278 GtkRequisition *requisition)
279 {
280 GtkBin *bin;
281 GtkRequisition child_req;
282 GdlDockTablabel *tablabel;
284 g_return_if_fail (widget != NULL);
285 g_return_if_fail (GDL_IS_DOCK_TABLABEL (widget));
286 g_return_if_fail (requisition != NULL);
288 tablabel = GDL_DOCK_TABLABEL (widget);
289 bin = GTK_BIN (widget);
291 requisition->width = tablabel->drag_handle_size;
292 requisition->height = 0;
294 if (bin->child)
295 gtk_widget_size_request (bin->child, &child_req);
296 else
297 child_req.width = child_req.height = 0;
299 requisition->width += child_req.width;
300 requisition->height += child_req.height;
302 requisition->width += GTK_CONTAINER (widget)->border_width * 2;
303 requisition->height += GTK_CONTAINER (widget)->border_width * 2;
305 widget->requisition = *requisition;
306 }
308 static void
309 gdl_dock_tablabel_size_allocate (GtkWidget *widget,
310 GtkAllocation *allocation)
311 {
312 GtkBin *bin;
313 GdlDockTablabel *tablabel;
314 gint border_width;
316 g_return_if_fail (widget != NULL);
317 g_return_if_fail (GDL_IS_DOCK_TABLABEL (widget));
318 g_return_if_fail (allocation != NULL);
320 bin = GTK_BIN (widget);
321 tablabel = GDL_DOCK_TABLABEL (widget);
323 border_width = GTK_CONTAINER (widget)->border_width;
325 widget->allocation = *allocation;
327 if (GTK_WIDGET_REALIZED (widget))
328 gdk_window_move_resize (tablabel->event_window,
329 allocation->x,
330 allocation->y,
331 allocation->width,
332 allocation->height);
334 if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) {
335 GtkAllocation child_allocation;
337 child_allocation.x = widget->allocation.x + border_width;
338 child_allocation.y = widget->allocation.y + border_width;
340 allocation->width = MAX (1, (int) allocation->width -
341 (int) tablabel->drag_handle_size);
342 child_allocation.x += tablabel->drag_handle_size;
344 child_allocation.width =
345 MAX (1, (int) allocation->width - 2 * border_width);
346 child_allocation.height =
347 MAX (1, (int) allocation->height - 2 * border_width);
349 gtk_widget_size_allocate (bin->child, &child_allocation);
350 }
351 }
353 static void
354 gdl_dock_tablabel_paint (GtkWidget *widget,
355 GdkEventExpose *event)
356 {
357 GdkRectangle dest, rect;
358 GtkBin *bin;
359 GdlDockTablabel *tablabel;
360 gint border_width;
362 bin = GTK_BIN (widget);
363 tablabel = GDL_DOCK_TABLABEL (widget);
364 border_width = GTK_CONTAINER (widget)->border_width;
366 rect.x = widget->allocation.x + border_width;
367 rect.y = widget->allocation.y + border_width;
368 rect.width = tablabel->drag_handle_size * HANDLE_RATIO;
369 rect.height = widget->allocation.height - 2*border_width;
371 if (gdk_rectangle_intersect (&event->area, &rect, &dest)) {
372 gtk_paint_handle (widget->style, widget->window,
373 tablabel->active ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE,
374 GTK_SHADOW_NONE,
375 &dest, widget, "dock-tablabel",
376 rect.x, rect.y, rect.width, rect.height,
377 GTK_ORIENTATION_VERTICAL);
378 };
379 }
381 static gint
382 gdl_dock_tablabel_expose (GtkWidget *widget,
383 GdkEventExpose *event)
384 {
385 g_return_val_if_fail (widget != NULL, FALSE);
386 g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE);
387 g_return_val_if_fail (event != NULL, FALSE);
389 if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget)) {
390 GDL_CALL_PARENT_GBOOLEAN(GTK_WIDGET_CLASS, expose_event, (widget,event));
391 gdl_dock_tablabel_paint (widget, event);
392 };
394 return FALSE;
395 }
397 static gboolean
398 gdl_dock_tablabel_button_event (GtkWidget *widget,
399 GdkEventButton *event)
400 {
401 GdlDockTablabel *tablabel;
402 gboolean event_handled;
404 g_return_val_if_fail (widget != NULL, FALSE);
405 g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE);
406 g_return_val_if_fail (event != NULL, FALSE);
408 tablabel = GDL_DOCK_TABLABEL (widget);
410 event_handled = FALSE;
412 if (event->window != tablabel->event_window)
413 return FALSE;
415 switch (event->type) {
416 case GDK_BUTTON_PRESS:
417 if (tablabel->active) {
418 gboolean in_handle;
419 gint rel_x, rel_y;
420 guint border_width;
421 GtkBin *bin;
423 bin = GTK_BIN (widget);
424 border_width = GTK_CONTAINER (widget)->border_width;
426 rel_x = event->x - border_width;
427 rel_y = event->y - border_width;
429 /* Check if user clicked on the drag handle. */
430 in_handle = (rel_x < tablabel->drag_handle_size * HANDLE_RATIO) &&
431 (rel_x > 0);
433 if (event->button == 1) {
434 tablabel->pre_drag = TRUE;
435 tablabel->drag_start_event = *event;
436 }
437 else {
438 g_signal_emit (widget,
439 dock_tablabel_signals [BUTTON_PRESSED_HANDLE],
440 0,
441 event);
442 }
444 event_handled = TRUE;
445 }
446 break;
448 case GDK_BUTTON_RELEASE:
449 tablabel->pre_drag = FALSE;
450 break;
452 default:
453 break;
454 }
456 if (!event_handled) {
457 /* propagate the event to the parent's gdkwindow */
458 GdkEventButton e;
460 e = *event;
461 e.window = gtk_widget_get_parent_window (widget);
462 e.x += widget->allocation.x;
463 e.y += widget->allocation.y;
465 gdk_event_put ((GdkEvent *) &e);
466 };
468 return event_handled;
469 }
471 static gboolean
472 gdl_dock_tablabel_motion_event (GtkWidget *widget,
473 GdkEventMotion *event)
474 {
475 GdlDockTablabel *tablabel;
476 gboolean event_handled;
478 g_return_val_if_fail (widget != NULL, FALSE);
479 g_return_val_if_fail (GDL_IS_DOCK_TABLABEL (widget), FALSE);
480 g_return_val_if_fail (event != NULL, FALSE);
482 tablabel = GDL_DOCK_TABLABEL (widget);
484 event_handled = FALSE;
486 if (event->window != tablabel->event_window)
487 return FALSE;
489 if (tablabel->pre_drag) {
490 if (gtk_drag_check_threshold (widget,
491 tablabel->drag_start_event.x,
492 tablabel->drag_start_event.y,
493 event->x,
494 event->y)) {
495 tablabel->pre_drag = FALSE;
496 g_signal_emit (widget,
497 dock_tablabel_signals [BUTTON_PRESSED_HANDLE],
498 0,
499 &tablabel->drag_start_event);
500 event_handled = TRUE;
501 }
502 }
504 if (!event_handled) {
505 /* propagate the event to the parent's gdkwindow */
506 GdkEventMotion e;
508 e = *event;
509 e.window = gtk_widget_get_parent_window (widget);
510 e.x += widget->allocation.x;
511 e.y += widget->allocation.y;
513 gdk_event_put ((GdkEvent *) &e);
514 };
516 return event_handled;
517 }
519 static void
520 gdl_dock_tablabel_realize (GtkWidget *widget)
521 {
522 GdlDockTablabel *tablabel;
523 GdkWindowAttr attributes;
524 int attributes_mask;
526 tablabel = GDL_DOCK_TABLABEL (widget);
528 attributes.window_type = GDK_WINDOW_CHILD;
529 attributes.x = widget->allocation.x;
530 attributes.y = widget->allocation.y;
531 attributes.width = widget->allocation.width;
532 attributes.height = widget->allocation.height;
533 attributes.wclass = GDK_INPUT_ONLY;
534 attributes.event_mask = gtk_widget_get_events (widget);
535 attributes.event_mask |= (GDK_EXPOSURE_MASK |
536 GDK_BUTTON_PRESS_MASK |
537 GDK_BUTTON_RELEASE_MASK |
538 GDK_ENTER_NOTIFY_MASK |
539 GDK_POINTER_MOTION_MASK |
540 GDK_LEAVE_NOTIFY_MASK);
541 attributes_mask = GDK_WA_X | GDK_WA_Y;
543 widget->window = gtk_widget_get_parent_window (widget);
544 g_object_ref (widget->window);
546 tablabel->event_window =
547 gdk_window_new (gtk_widget_get_parent_window (widget),
548 &attributes, attributes_mask);
549 gdk_window_set_user_data (tablabel->event_window, widget);
551 widget->style = gtk_style_attach (widget->style, widget->window);
553 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
554 }
556 static void
557 gdl_dock_tablabel_unrealize (GtkWidget *widget)
558 {
559 GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget);
561 if (tablabel->event_window) {
562 gdk_window_set_user_data (tablabel->event_window, NULL);
563 gdk_window_destroy (tablabel->event_window);
564 tablabel->event_window = NULL;
565 }
567 GDL_CALL_PARENT (GTK_WIDGET_CLASS, unrealize, (widget));
568 }
570 static void
571 gdl_dock_tablabel_map (GtkWidget *widget)
572 {
573 GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget);
575 GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
577 gdk_window_show (tablabel->event_window);
578 }
580 static void
581 gdl_dock_tablabel_unmap (GtkWidget *widget)
582 {
583 GdlDockTablabel *tablabel = GDL_DOCK_TABLABEL (widget);
585 gdk_window_hide (tablabel->event_window);
587 GDL_CALL_PARENT (GTK_WIDGET_CLASS, unmap, (widget));
588 }
590 /* ----- Public interface ----- */
592 GtkWidget *
593 gdl_dock_tablabel_new (GdlDockItem *item)
594 {
595 GdlDockTablabel *tablabel;
597 tablabel = GDL_DOCK_TABLABEL (g_object_new (GDL_TYPE_DOCK_TABLABEL,
598 "item", item,
599 NULL));
601 return GTK_WIDGET (tablabel);
602 }
604 void
605 gdl_dock_tablabel_activate (GdlDockTablabel *tablabel)
606 {
607 g_return_if_fail (tablabel != NULL);
609 tablabel->active = TRUE;
610 gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_NORMAL);
611 }
613 void
614 gdl_dock_tablabel_deactivate (GdlDockTablabel *tablabel)
615 {
616 g_return_if_fail (tablabel != NULL);
618 tablabel->active = FALSE;
619 /* yeah, i know it contradictive */
620 gtk_widget_set_state (GTK_WIDGET (tablabel), GTK_STATE_ACTIVE);
621 }