]> Pileus Git - ~andy/gtk/blob - gtk/gtkhandlebox.c
main part for GtkArgSetFunc/GtkArgGetFunc implementation.
[~andy/gtk] / gtk / gtkhandlebox.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 1998 Elliot Lee
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20
21 #include <stdlib.h>
22 #include "gtksignal.h"
23 #include "gtkhandlebox.h"
24 #include <gdk/gdkx.h>
25
26
27 #define DRAG_HANDLE_SIZE 10
28 #define BORDER_SIZE 5
29
30
31 static void gtk_handle_box_class_init    (GtkHandleBoxClass *klass);
32 static void gtk_handle_box_init          (GtkHandleBox      *handle_box);
33 static void gtk_handle_box_realize       (GtkWidget         *widget);
34 static void gtk_handle_box_size_request  (GtkWidget         *widget,
35                                           GtkRequisition    *requisition);
36 static void gtk_handle_box_size_allocate (GtkWidget         *widget,
37                                           GtkAllocation     *allocation);
38 static void gtk_handle_box_paint         (GtkWidget         *widget,
39                                           GdkRectangle      *area);
40 static void gtk_handle_box_draw          (GtkWidget         *widget,
41                                           GdkRectangle      *area);
42 static gint gtk_handle_box_expose        (GtkWidget         *widget,
43                                           GdkEventExpose    *event);
44 static gint gtk_handle_box_button_changed(GtkWidget         *widget,
45                                           GdkEventButton    *event);
46 static gint gtk_handle_box_motion        (GtkWidget         *widget,
47                                           GdkEventMotion    *event);
48
49
50 guint
51 gtk_handle_box_get_type ()
52 {
53   static guint handle_box_type = 0;
54
55   if (!handle_box_type)
56     {
57       GtkTypeInfo handle_box_info =
58       {
59         "GtkHandleBox",
60         sizeof (GtkHandleBox),
61         sizeof (GtkHandleBoxClass),
62         (GtkClassInitFunc) gtk_handle_box_class_init,
63         (GtkObjectInitFunc) gtk_handle_box_init,
64         (GtkArgSetFunc) NULL,
65         (GtkArgGetFunc) NULL,
66       };
67
68       handle_box_type = gtk_type_unique (gtk_event_box_get_type (), &handle_box_info);
69     }
70
71   return handle_box_type;
72 }
73
74 static void
75 gtk_handle_box_class_init (GtkHandleBoxClass *class)
76 {
77   GtkWidgetClass *widget_class;
78
79   widget_class = (GtkWidgetClass*) class;
80   widget_class->realize = gtk_handle_box_realize;
81   widget_class->size_request = gtk_handle_box_size_request;
82   widget_class->size_allocate = gtk_handle_box_size_allocate;
83   widget_class->draw = gtk_handle_box_draw;
84   widget_class->expose_event = gtk_handle_box_expose;
85   widget_class->button_press_event = gtk_handle_box_button_changed;
86   widget_class->button_release_event = gtk_handle_box_button_changed;
87   widget_class->motion_notify_event = gtk_handle_box_motion;
88 }
89
90 static void
91 gtk_handle_box_init (GtkHandleBox *handle_box)
92 {
93   GTK_WIDGET_UNSET_FLAGS (handle_box, GTK_NO_WINDOW);
94   GTK_WIDGET_SET_FLAGS (handle_box, GTK_BASIC);
95   handle_box->is_being_dragged = FALSE;
96   handle_box->is_onroot = FALSE;
97   handle_box->real_parent = NULL;
98 }
99
100 GtkWidget*
101 gtk_handle_box_new ()
102 {
103   return GTK_WIDGET ( gtk_type_new (gtk_handle_box_get_type ()));
104 }
105
106 static void
107 gtk_handle_box_realize (GtkWidget *widget)
108 {
109   GdkWindowAttr attributes;
110   gint attributes_mask;
111
112   g_return_if_fail (widget != NULL);
113   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
114
115   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
116
117   attributes.x = widget->allocation.x;
118   attributes.y = widget->allocation.y;
119   attributes.width = widget->allocation.width;
120   attributes.height = widget->allocation.height;
121   attributes.window_type = GDK_WINDOW_CHILD;
122   attributes.wclass = GDK_INPUT_OUTPUT;
123   attributes.visual = gtk_widget_get_visual (widget);
124   attributes.colormap = gtk_widget_get_colormap (widget);
125   attributes.event_mask = gtk_widget_get_events (widget)
126                         | GDK_BUTTON_MOTION_MASK
127                         | GDK_BUTTON_PRESS_MASK
128                         | GDK_BUTTON_RELEASE_MASK
129                         | GDK_EXPOSURE_MASK
130                         | GDK_ENTER_NOTIFY_MASK
131                         | GDK_LEAVE_NOTIFY_MASK;
132
133   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
134
135   widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
136   gdk_window_set_user_data (widget->window, widget);
137
138   widget->style = gtk_style_attach (widget->style, widget->window);
139   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
140 }
141
142 static void
143 gtk_handle_box_size_request (GtkWidget      *widget,
144                              GtkRequisition *requisition)
145 {
146   GtkBin *bin;
147   GtkHandleBox *hb;
148
149   g_return_if_fail (widget != NULL);
150   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
151   g_return_if_fail (requisition != NULL);
152
153   bin = GTK_BIN (widget);
154   hb = GTK_HANDLE_BOX(widget);
155
156   requisition->width = DRAG_HANDLE_SIZE + GTK_CONTAINER(widget)->border_width * 2;
157   requisition->height = GTK_CONTAINER(widget)->border_width * 2;
158
159   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
160     {
161       gtk_widget_size_request (bin->child, &bin->child->requisition);
162
163       requisition->width += bin->child->requisition.width;
164       requisition->height += bin->child->requisition.height;
165     }
166
167   hb->real_requisition = *requisition;
168   if (hb->is_onroot)
169       requisition->height = 3;
170 }
171
172 static void
173 gtk_handle_box_size_allocate (GtkWidget     *widget,
174                               GtkAllocation *allocation)
175 {
176   GtkBin *bin;
177   GtkAllocation child_allocation;
178   GtkHandleBox *hb;
179
180   g_return_if_fail (widget != NULL);
181   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
182   g_return_if_fail (allocation != NULL);
183
184   widget->allocation = *allocation;
185   bin = GTK_BIN (widget);
186   hb = GTK_HANDLE_BOX(widget);
187
188   child_allocation.x = GTK_CONTAINER(widget)->border_width + DRAG_HANDLE_SIZE;
189   child_allocation.y = GTK_CONTAINER(widget)->border_width;
190
191   if (hb->is_onroot)
192     {
193       child_allocation.width = bin->child->requisition.width;
194       child_allocation.height = bin->child->requisition.height;
195     }
196   else
197     {
198       child_allocation.width = (allocation->width - DRAG_HANDLE_SIZE
199                                 - GTK_CONTAINER(widget)->border_width * 2);
200       child_allocation.height = allocation->height - GTK_CONTAINER(widget)->border_width * 2;
201     }
202
203   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
204     {
205       gtk_widget_size_allocate (bin->child, &child_allocation);
206     }
207
208   if (GTK_WIDGET_REALIZED (widget))
209     {
210       gdk_window_resize (widget->window,
211                          child_allocation.width + DRAG_HANDLE_SIZE,
212                          child_allocation.height);
213       if (!hb->is_onroot)
214         gdk_window_move (widget->window,
215                          allocation->x + GTK_CONTAINER(widget)->border_width,
216                          allocation->y + GTK_CONTAINER(widget)->border_width);
217     }
218 }
219
220 static void
221 gtk_handle_box_paint(GtkWidget    *widget,
222                      GdkRectangle *area)
223 {
224   GtkHandleBox *hb;
225   gint startx, endx, x;
226   gint line_y2;
227
228   hb = GTK_HANDLE_BOX(widget);
229
230   startx = 1;
231   endx = DRAG_HANDLE_SIZE;
232   
233   if (area->x > startx)
234     startx = area->x;
235   
236   if ((area->x + area->width) < endx)
237     endx = area->x + area->width;
238   
239   line_y2 = area->y + area->height;
240
241   for(x = startx; x < DRAG_HANDLE_SIZE; x += 3)
242     gtk_draw_vline(widget->style,
243                    widget->window,
244                    GTK_WIDGET_STATE(widget),
245                    area->y, line_y2,
246                    x);
247
248   if (GTK_BIN(widget)->child)
249     gtk_draw_shadow(widget->style,
250                     widget->window,
251                     GTK_WIDGET_STATE(widget),
252                     GTK_SHADOW_OUT,
253                     0, 0,
254                     GTK_BIN(widget)->child->allocation.width + DRAG_HANDLE_SIZE,
255                     GTK_BIN(widget)->child->allocation.height);
256
257   if (hb->is_onroot)
258     gtk_draw_hline(widget->style,
259                    widget->parent->window,
260                    GTK_WIDGET_STATE(widget),
261                    widget->allocation.x,
262                    widget->allocation.x + widget->allocation.width,
263                    widget->allocation.y);
264 }
265
266 static void
267 gtk_handle_box_draw (GtkWidget    *widget,
268                      GdkRectangle *area)
269 {
270   GtkBin *bin;
271   GdkRectangle child_area;
272
273   g_return_if_fail (widget != NULL);
274   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
275   g_return_if_fail (area != NULL);
276
277   if (GTK_WIDGET_DRAWABLE (widget))
278     {
279       bin = GTK_BIN (widget);
280       
281       gtk_handle_box_paint(widget, area);
282       if (bin->child)
283         {
284           if (gtk_widget_intersect (bin->child, area, &child_area))
285             gtk_widget_draw (bin->child, &child_area);
286         }
287     }
288 }
289
290 static gint
291 gtk_handle_box_expose (GtkWidget      *widget,
292                        GdkEventExpose *event)
293 {
294   GtkBin *bin;
295   GdkEventExpose child_event;
296
297   g_return_val_if_fail (widget != NULL, FALSE);
298   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
299   g_return_val_if_fail (event != NULL, FALSE);
300
301   if (GTK_WIDGET_DRAWABLE (widget))
302     {
303       bin = GTK_BIN (widget);
304       gtk_handle_box_paint(widget, &event->area);
305
306       child_event = *event;
307       if (bin->child &&
308           GTK_WIDGET_NO_WINDOW (bin->child) &&
309           gtk_widget_intersect (bin->child, &event->area, &child_event.area))
310         gtk_widget_event (bin->child, (GdkEvent*) &child_event);
311     }
312
313   return FALSE;
314 }
315
316 static gint dragoff_x, dragoff_y;
317 static gint parentx, parenty;
318
319 static void
320 gtk_handle_box_get_parent_position(GtkWidget *widget,
321                                    gint *x, gint *y)
322 {
323   gdk_window_get_origin(widget->parent->window, x, y);
324   *x += widget->allocation.x;
325   *y += widget->allocation.y;
326 }
327
328 static gint
329 gtk_handle_box_button_changed(GtkWidget *widget,
330                               GdkEventButton *event)
331 {
332   GtkHandleBox *hb;
333   g_return_val_if_fail(widget != NULL, FALSE);
334   g_return_val_if_fail(GTK_IS_HANDLE_BOX(widget), FALSE);
335   g_return_val_if_fail(event != NULL, FALSE);
336
337   hb = GTK_HANDLE_BOX(widget);
338   if (event->button == 1)
339     {
340       if (event->type == GDK_BUTTON_PRESS
341           && event->x < DRAG_HANDLE_SIZE)
342         {
343           dragoff_x = event->x;
344           dragoff_y = event->y;
345           gtk_handle_box_get_parent_position(widget,
346                                              &parentx,
347                                              &parenty);
348           hb->is_being_dragged = TRUE;
349           gdk_pointer_grab(widget->window,
350                            TRUE,
351                            GDK_POINTER_MOTION_MASK
352                            |GDK_BUTTON_RELEASE_MASK,
353                            GDK_ROOT_PARENT(),
354                            NULL, 
355                            GDK_CURRENT_TIME);
356         }
357       else if (event->type == GDK_BUTTON_RELEASE)
358         {
359           gdk_pointer_ungrab(GDK_CURRENT_TIME);
360           hb->is_being_dragged = FALSE;
361         }
362     }
363   return TRUE;
364 }
365
366 void
367 gtk_handle_box_set_location  (GtkWidget *widget,
368                               gboolean in_root,
369                               gint x, gint y)
370 {
371   GtkHandleBox *hb;
372
373   hb = GTK_HANDLE_BOX(widget);
374
375   if (in_root != FALSE)
376     {
377       GTK_HANDLE_BOX(widget)->is_onroot = TRUE;
378       if (x < 0)
379         x = parentx;
380       if (y < 0)
381         y = parenty;
382       gdk_window_set_override_redirect(widget->window, TRUE);
383       gdk_window_reparent(widget->window, GDK_ROOT_PARENT(),
384                           x, y);
385       gdk_window_raise(widget->window);
386       widget->requisition = hb->real_requisition;
387       gtk_widget_queue_resize(widget->parent);
388       gdk_pointer_ungrab(GDK_CURRENT_TIME);
389       gdk_pointer_grab(widget->window,
390                        TRUE,
391                        GDK_POINTER_MOTION_MASK
392                        |GDK_BUTTON_RELEASE_MASK,
393                        GDK_ROOT_PARENT(),
394                        NULL, 
395                        GDK_CURRENT_TIME);
396     }
397   else
398     {
399       GTK_HANDLE_BOX(widget)->is_onroot = FALSE;
400       gdk_window_reparent(widget->window, widget->parent->window,
401                           widget->allocation.x, widget->allocation.y);
402       widget->requisition.height = 3;
403       gtk_widget_queue_resize(widget->parent);
404     }
405 }
406
407 static gint
408 gtk_handle_box_motion        (GtkWidget *widget,
409                               GdkEventMotion *event)
410 {
411   GtkHandleBox *hb;
412   gint newx, newy;
413
414   g_return_val_if_fail(widget != NULL, FALSE);
415   g_return_val_if_fail(GTK_IS_HANDLE_BOX(widget), FALSE);
416   g_return_val_if_fail(event != NULL, FALSE);
417
418   hb = GTK_HANDLE_BOX(widget);
419
420   if (hb->is_being_dragged) {
421     newx = event->x_root - dragoff_x;
422     newy = event->y_root - dragoff_y;
423     if (newx < 0)
424       newx = 0;
425     if (newy < 0)
426       newy = 0;
427     if (abs(parentx - newx) < 10
428         && abs(parenty - newy) < 10)
429       {
430         if (hb->is_onroot == TRUE)
431           gtk_handle_box_set_location(widget, FALSE, 0, 0);
432       }
433     else
434       {
435         if (hb->is_onroot == FALSE)
436           gtk_handle_box_set_location(widget, TRUE, parentx, parenty);
437         gdk_window_move(widget->window, newx, newy);
438       }
439   }
440   return TRUE;
441 }