]> Pileus Git - ~andy/gtk/blob - gtk/gtkhandlebox.c
Now we use a GtkWindow of type GTK_WINDOW_DIALOG as a destination for
[~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 "gdk/gdkx.h"
23 #include "gtkhandlebox.h"
24 #include "gtkmain.h"
25 #include "gtksignal.h"
26 #include "gtkwindow.h"
27
28
29 #define DRAG_HANDLE_SIZE 10
30 #define BORDER_SIZE 5
31 #define GHOST_HEIGHT 3
32 #define SNAP_TOLERANCE 10
33
34
35 static void gtk_handle_box_class_init     (GtkHandleBoxClass *klass);
36 static void gtk_handle_box_init           (GtkHandleBox      *handle_box);
37 static void gtk_handle_box_destroy        (GtkObject         *object);
38 static void gtk_handle_box_map            (GtkWidget         *widget);
39 static void gtk_handle_box_unmap          (GtkWidget         *widget);
40 static void gtk_handle_box_realize        (GtkWidget         *widget);
41 static void gtk_handle_box_unrealize      (GtkWidget         *widget);
42 static void gtk_handle_box_size_request   (GtkWidget         *widget,
43                                            GtkRequisition    *requisition);
44 static void gtk_handle_box_size_allocate  (GtkWidget         *widget,
45                                            GtkAllocation     *allocation);
46 static void gtk_handle_box_draw_ghost     (GtkWidget         *widget);
47 static void gtk_handle_box_paint          (GtkWidget         *widget,
48                                            GdkEventExpose    *event,
49                                            GdkRectangle      *area);
50 static void gtk_handle_box_draw           (GtkWidget         *widget,
51                                            GdkRectangle      *area);
52 static gint gtk_handle_box_expose         (GtkWidget         *widget,
53                                            GdkEventExpose    *event);
54 static gint gtk_handle_box_button_changed (GtkWidget         *widget,
55                                            GdkEventButton    *event);
56 static gint gtk_handle_box_motion         (GtkWidget         *widget,
57                                            GdkEventMotion    *event);
58 static gint gtk_handle_box_delete_float   (GtkWidget         *widget,
59                                            GdkEvent          *event,
60                                            gpointer           data);
61
62
63 static GtkBinClass *parent_class;
64
65
66 guint
67 gtk_handle_box_get_type (void)
68 {
69   static guint handle_box_type = 0;
70
71   if (!handle_box_type)
72     {
73       GtkTypeInfo handle_box_info =
74       {
75         "GtkHandleBox",
76         sizeof (GtkHandleBox),
77         sizeof (GtkHandleBoxClass),
78         (GtkClassInitFunc) gtk_handle_box_class_init,
79         (GtkObjectInitFunc) gtk_handle_box_init,
80         (GtkArgSetFunc) NULL,
81         (GtkArgGetFunc) NULL,
82       };
83
84       handle_box_type = gtk_type_unique (gtk_bin_get_type (), &handle_box_info);
85     }
86
87   return handle_box_type;
88 }
89
90 static void
91 gtk_handle_box_class_init (GtkHandleBoxClass *class)
92 {
93   GtkWidgetClass *widget_class;
94   GtkObjectClass *object_class;
95
96   object_class = (GtkObjectClass *) class;
97   widget_class = (GtkWidgetClass *) class;
98
99   parent_class = gtk_type_class (gtk_bin_get_type ());
100
101   object_class->destroy = gtk_handle_box_destroy;
102
103   widget_class->map = gtk_handle_box_map;
104   widget_class->unmap = gtk_handle_box_unmap;
105   widget_class->realize = gtk_handle_box_realize;
106   widget_class->unrealize = gtk_handle_box_unrealize;
107   widget_class->size_request = gtk_handle_box_size_request;
108   widget_class->size_allocate = gtk_handle_box_size_allocate;
109   widget_class->draw = gtk_handle_box_draw;
110   widget_class->expose_event = gtk_handle_box_expose;
111   widget_class->button_press_event = gtk_handle_box_button_changed;
112   widget_class->button_release_event = gtk_handle_box_button_changed;
113   widget_class->motion_notify_event = gtk_handle_box_motion;
114 }
115
116 static void
117 gtk_handle_box_init (GtkHandleBox *handle_box)
118 {
119   GTK_WIDGET_UNSET_FLAGS (handle_box, GTK_NO_WINDOW);
120   GTK_WIDGET_SET_FLAGS (handle_box, GTK_BASIC); /* FIXME: are we really a basic widget? */
121
122   handle_box->steady_window = NULL;
123   handle_box->float_window = NULL;
124   handle_box->is_being_dragged = FALSE;
125   handle_box->is_onroot = FALSE;
126   handle_box->fleur_cursor = gdk_cursor_new (GDK_FLEUR);
127   handle_box->dragoff_x = 0;
128   handle_box->dragoff_y = 0;
129   handle_box->steady_x = 0;
130   handle_box->steady_x = 0;
131 }
132
133 GtkWidget*
134 gtk_handle_box_new (void)
135 {
136   return GTK_WIDGET (gtk_type_new (gtk_handle_box_get_type ()));
137 }
138
139 static void
140 gtk_handle_box_destroy (GtkObject *object)
141 {
142   GtkHandleBox *hb;
143
144   g_return_if_fail (object != NULL);
145   g_return_if_fail (GTK_IS_HANDLE_BOX (object));
146
147   hb = GTK_HANDLE_BOX (object);
148
149   gdk_cursor_destroy (hb->fleur_cursor);
150
151   if (GTK_OBJECT_CLASS (parent_class)->destroy)
152     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
153 }
154
155 static void
156 gtk_handle_box_map (GtkWidget *widget)
157 {
158   GtkBin *bin;
159   GtkHandleBox *hb;
160
161   g_return_if_fail (widget != NULL);
162   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
163
164   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
165
166   bin = GTK_BIN (widget);
167   hb = GTK_HANDLE_BOX (widget);
168
169   gdk_window_show (hb->steady_window);
170   gdk_window_show (widget->window);
171
172   if (bin->child
173       && GTK_WIDGET_VISIBLE (bin->child)
174       && !GTK_WIDGET_MAPPED (bin->child))
175     gtk_widget_map (bin->child);
176 }
177
178 static void
179 gtk_handle_box_unmap (GtkWidget *widget)
180 {
181   GtkHandleBox *hb;
182
183   g_return_if_fail (widget != NULL);
184   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
185
186   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
187
188   hb = GTK_HANDLE_BOX (widget);
189
190   gdk_window_hide (widget->window);
191   gdk_window_hide (hb->steady_window);
192 }
193
194 static void
195 gtk_handle_box_realize (GtkWidget *widget)
196 {
197   GdkWindowAttr attributes;
198   gint attributes_mask;
199   GtkHandleBox *hb;
200
201   g_return_if_fail (widget != NULL);
202   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
203
204   hb = GTK_HANDLE_BOX (widget);
205
206   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
207
208   /* FIXME: we need a property that would tell the window manager not
209    * to put decoration on this window.  This is not part of the ICCCM,
210    * so we'll have to define our own (a la KWM) and hack some window
211    * managers to support it.
212    */
213
214   hb->float_window = gtk_window_new (GTK_WINDOW_DIALOG);
215   gtk_window_set_policy (GTK_WINDOW (hb->float_window), FALSE, FALSE, FALSE);
216   gtk_container_border_width (GTK_CONTAINER (hb->float_window), 0);
217   gtk_signal_connect (GTK_OBJECT (hb->float_window), "delete_event",
218                       (GtkSignalFunc) gtk_handle_box_delete_float,
219                       hb);
220   
221   attributes.x = widget->allocation.x;
222   attributes.y = widget->allocation.y;
223   attributes.width = widget->allocation.width;
224   attributes.height = widget->allocation.height;
225   attributes.window_type = GDK_WINDOW_CHILD;
226   attributes.wclass = GDK_INPUT_OUTPUT;
227   attributes.visual = gtk_widget_get_visual (widget);
228   attributes.colormap = gtk_widget_get_colormap (widget);
229   attributes.event_mask = (gtk_widget_get_events (widget)
230                            | GDK_EXPOSURE_MASK);
231
232   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
233
234   hb->steady_window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
235   gdk_window_set_user_data (hb->steady_window, widget);
236
237   attributes.x = 0;
238   attributes.y = 0;
239   attributes.event_mask |= (GDK_BUTTON1_MOTION_MASK
240                             | GDK_BUTTON_PRESS_MASK
241                             | GDK_BUTTON_RELEASE_MASK);
242
243   widget->window = gdk_window_new (hb->steady_window, &attributes, attributes_mask);
244   gdk_window_set_user_data (widget->window, widget);
245
246   widget->style = gtk_style_attach (widget->style, widget->window);
247   gtk_style_set_background (widget->style, hb->steady_window, GTK_STATE_NORMAL);
248   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
249 }
250
251 static void
252 gtk_handle_box_unrealize (GtkWidget *widget)
253 {
254   GtkHandleBox *hb;
255
256   g_return_if_fail (widget != NULL);
257   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
258
259   GTK_WIDGET_UNSET_FLAGS (widget, GTK_REALIZED | GTK_MAPPED);
260
261   gtk_style_detach (widget->style);
262
263   hb = GTK_HANDLE_BOX (widget);
264
265   if (widget->window)
266     gdk_window_destroy (widget->window);
267
268   if (hb->steady_window)
269     gdk_window_destroy (hb->steady_window);
270
271   hb->steady_window = NULL;
272   widget->window = NULL;
273
274   /* FIXME: do we have to unref the float_window before destroying it? */
275
276   gtk_widget_destroy (hb->float_window);
277   hb->float_window = NULL;
278 }
279
280 static void
281 gtk_handle_box_size_request (GtkWidget      *widget,
282                              GtkRequisition *requisition)
283 {
284   GtkBin *bin;
285   GtkHandleBox *hb;
286
287   g_return_if_fail (widget != NULL);
288   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
289   g_return_if_fail (requisition != NULL);
290
291   bin = GTK_BIN (widget);
292   hb = GTK_HANDLE_BOX (widget);
293
294   requisition->width = DRAG_HANDLE_SIZE + GTK_CONTAINER (widget)->border_width * 2;
295   requisition->height = GTK_CONTAINER (widget)->border_width * 2;
296
297   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
298     {
299       gtk_widget_size_request (bin->child, &bin->child->requisition);
300
301       requisition->width += bin->child->requisition.width;
302       requisition->height += bin->child->requisition.height;
303     }
304
305   hb->real_requisition = *requisition;
306   if (hb->is_onroot)
307       requisition->height = GHOST_HEIGHT;
308   /* FIXME: Should also set requisition->width to a small value? */
309 }
310
311 static void
312 gtk_handle_box_size_allocate (GtkWidget     *widget,
313                               GtkAllocation *allocation)
314 {
315   GtkBin *bin;
316   GtkAllocation child_allocation;
317   GtkHandleBox *hb;
318   gint border_width;
319
320   g_return_if_fail (widget != NULL);
321   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
322   g_return_if_fail (allocation != NULL);
323
324   widget->allocation = *allocation;
325   bin = GTK_BIN (widget);
326   hb = GTK_HANDLE_BOX (widget);
327
328   border_width = GTK_CONTAINER (widget)->border_width;
329
330   if (GTK_WIDGET_REALIZED (widget))
331     {
332       if (!hb->is_onroot)
333         {
334           gdk_window_move_resize (hb->steady_window,
335                                   allocation->x + border_width,
336                                   allocation->y + border_width,
337                                   allocation->width - border_width * 2,
338                                   allocation->height - border_width * 2);
339           gdk_window_move_resize (widget->window,
340                                   0,
341                                   0,
342                                   allocation->width - border_width * 2,
343                                   allocation->height - border_width * 2);
344         }
345       else
346         {
347           gtk_widget_set_usize (hb->float_window,
348                                 hb->real_requisition.width,
349                                 hb->real_requisition.height);
350           gdk_window_resize (widget->window,
351                              hb->real_requisition.width,
352                              hb->real_requisition.height);
353           gdk_window_move_resize (hb->steady_window,
354                                   allocation->x + border_width,
355                                   allocation->y + border_width,
356                                   allocation->width - border_width * 2,
357                                   GHOST_HEIGHT);
358         }
359     }
360
361   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
362     {
363       child_allocation.x = DRAG_HANDLE_SIZE;
364       child_allocation.y = 0;
365
366       if (hb->is_onroot)
367         {
368           child_allocation.width = hb->real_requisition.width - DRAG_HANDLE_SIZE;
369           child_allocation.height = hb->real_requisition.height;
370         }
371       else
372         {
373           child_allocation.width = allocation->width - DRAG_HANDLE_SIZE - border_width * 2;
374           child_allocation.height = allocation->height - border_width * 2;
375         }
376
377       gtk_widget_size_allocate (bin->child, &child_allocation);
378     }
379 }
380
381 static void
382 gtk_handle_box_draw_ghost (GtkWidget *widget)
383 {
384   gtk_draw_hline (widget->style,
385                   GTK_HANDLE_BOX (widget)->steady_window,
386                   GTK_WIDGET_STATE (widget),
387                   0,
388                   widget->allocation.width - GTK_CONTAINER (widget)->border_width * 2,
389                   0);
390 }
391
392 static void
393 gtk_handle_box_paint (GtkWidget      *widget,
394                       GdkEventExpose *event,
395                       GdkRectangle   *area)
396 {
397   GtkBin *bin;
398   GtkHandleBox *hb;
399   GdkRectangle child_area;
400   GdkEventExpose child_event;
401   gint x;
402
403   bin = GTK_BIN (widget);
404   hb = GTK_HANDLE_BOX (widget);
405
406   if (event != NULL)
407     area = &event->area;
408
409   for (x = 1; x < DRAG_HANDLE_SIZE; x += 3)
410     gtk_draw_vline (widget->style,
411                     widget->window,
412                     GTK_WIDGET_STATE (widget),
413                     0, hb->is_onroot ? hb->real_requisition.height : widget->allocation.height,
414                     x);
415
416   if (hb->is_onroot)
417     gtk_draw_shadow (widget->style,
418                      widget->window,
419                      GTK_WIDGET_STATE (widget),
420                      GTK_SHADOW_OUT,
421                      0, 0,
422                      hb->real_requisition.width,
423                      hb->real_requisition.height);
424   else
425     gtk_draw_shadow (widget->style,
426                      widget->window,
427                      GTK_WIDGET_STATE (widget),
428                      GTK_SHADOW_OUT,
429                      0, 0,
430                      widget->allocation.width,
431                      widget->allocation.height);
432
433   if (bin->child)
434     {
435       if (event == NULL) /* we were called from draw() */
436         {
437           if (gtk_widget_intersect (bin->child, area, &child_area))
438             gtk_widget_draw (bin->child, &child_area);
439         }
440       else /* we were called from expose() */
441         {
442           child_event = *event;
443           
444           if (GTK_WIDGET_NO_WINDOW (bin->child)
445               && gtk_widget_intersect (bin->child, &event->area, &child_event.area))
446             gtk_widget_event (bin->child, (GdkEvent *) &child_event);
447         }
448     }
449 }
450
451 static void
452 gtk_handle_box_draw (GtkWidget    *widget,
453                      GdkRectangle *area)
454 {
455   GdkRectangle child_area;
456   GtkHandleBox *hb;
457
458   g_return_if_fail (widget != NULL);
459   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
460   g_return_if_fail (area != NULL);
461
462   hb = GTK_HANDLE_BOX (widget);
463
464   if (GTK_WIDGET_DRAWABLE (widget))
465     {
466       if (hb->is_onroot)
467         {
468           /* The area parameter does not make sense in this case, so we
469            * repaint everything.
470            */
471
472           gtk_handle_box_draw_ghost (widget);
473
474           child_area.x = 0;
475           child_area.y = 0;
476           child_area.width = hb->real_requisition.width;
477           child_area.height = hb->real_requisition.height;
478
479           gtk_handle_box_paint (widget, NULL, &child_area);
480         }
481       else
482         gtk_handle_box_paint (widget, NULL, area);
483     }
484 }
485
486 static gint
487 gtk_handle_box_expose (GtkWidget      *widget,
488                        GdkEventExpose *event)
489 {
490   GtkHandleBox *hb;
491
492   g_return_val_if_fail (widget != NULL, FALSE);
493   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
494   g_return_val_if_fail (event != NULL, FALSE);
495
496   if (GTK_WIDGET_DRAWABLE (widget))
497     {
498       hb = GTK_HANDLE_BOX (widget);
499
500       if (event->window == hb->steady_window)
501         gtk_handle_box_draw_ghost (widget);
502       else if (event->window == widget->window)
503         gtk_handle_box_paint (widget, event, NULL);
504     }
505
506   return FALSE;
507 }
508
509 static gint
510 gtk_handle_box_button_changed (GtkWidget      *widget,
511                                GdkEventButton *event)
512 {
513   GtkHandleBox *hb;
514
515   g_return_val_if_fail (widget != NULL, FALSE);
516   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
517   g_return_val_if_fail (event != NULL, FALSE);
518
519   hb = GTK_HANDLE_BOX (widget);
520
521   if (event->button == 1)
522     {
523       if ((event->type == GDK_BUTTON_PRESS) && (event->x < DRAG_HANDLE_SIZE))
524         {
525           hb->dragoff_x = event->x;
526           hb->dragoff_y = event->y;
527
528           gdk_window_get_origin (hb->steady_window, &hb->steady_x, &hb->steady_y);
529
530           hb->is_being_dragged = TRUE;
531
532           gdk_flush();
533           gtk_grab_add (widget);
534           while (gdk_pointer_grab (widget->window,
535                                    FALSE,
536                                    GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
537                                    NULL,
538                                    hb->fleur_cursor,
539                                    GDK_CURRENT_TIME) != 0); /* wait for success */
540         }
541       else if ((event->type == GDK_BUTTON_RELEASE) && hb->is_being_dragged)
542         {
543           gdk_pointer_ungrab (GDK_CURRENT_TIME);
544           gtk_grab_remove (widget);
545           hb->is_being_dragged = FALSE;
546         }
547     }
548   return TRUE;
549 }
550
551 static gint
552 gtk_handle_box_motion (GtkWidget      *widget,
553                        GdkEventMotion *event)
554 {
555   GtkHandleBox *hb;
556   gint newx, newy;
557
558   g_return_val_if_fail (widget != NULL, FALSE);
559   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
560   g_return_val_if_fail (event != NULL, FALSE);
561
562   hb = GTK_HANDLE_BOX (widget);
563
564   if (hb->is_being_dragged)
565     {
566       newx = event->x_root - hb->dragoff_x;
567       newy = event->y_root - hb->dragoff_y;
568
569       if ((abs (hb->steady_x - newx) < SNAP_TOLERANCE)
570           && (abs (hb->steady_y - newy) < SNAP_TOLERANCE))
571         {
572           if (hb->is_onroot)
573             {
574               hb->is_onroot = FALSE;
575
576               gdk_pointer_ungrab (GDK_CURRENT_TIME);
577
578               gdk_window_reparent (widget->window, hb->steady_window, 0, 0);
579
580               gtk_widget_hide (hb->float_window);
581
582               while (gdk_pointer_grab (widget->window,
583                                        FALSE,
584                                        GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
585                                        NULL,
586                                        hb->fleur_cursor,
587                                        GDK_CURRENT_TIME) != 0); /* wait for success */
588
589               gtk_widget_queue_resize (widget);
590             }
591         }
592       else
593         {
594           if (hb->is_onroot)
595             {
596               gtk_widget_set_uposition (hb->float_window, newx, newy);
597               gdk_window_raise (hb->float_window->window);
598             }
599           else
600             {
601               hb->is_onroot = TRUE;
602
603               gdk_pointer_ungrab (GDK_CURRENT_TIME);
604               gtk_widget_show (hb->float_window);
605               gtk_widget_set_uposition (hb->float_window, newx, newy);
606               
607               gdk_window_reparent (widget->window, hb->float_window->window, 0, 0);
608
609               while (gdk_pointer_grab (widget->window,
610                                        FALSE,
611                                        GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
612                                        NULL,
613                                        hb->fleur_cursor,
614                                        GDK_CURRENT_TIME) != 0); /* wait for success */
615
616               gtk_widget_queue_resize (widget);
617             }
618         }
619     }
620
621   return TRUE;
622 }
623
624 static gint
625 gtk_handle_box_delete_float (GtkWidget *widget,
626                              GdkEvent  *event,
627                              gpointer   data)
628 {
629   GtkHandleBox *hb;
630
631   g_return_if_fail (widget != NULL);
632   g_return_if_fail (event != NULL);
633   g_return_if_fail (data != NULL);
634   g_return_if_fail (GTK_IS_HANDLE_BOX (data));
635
636   hb = GTK_HANDLE_BOX (data);
637
638   hb->is_onroot = FALSE;
639
640   gdk_window_reparent (GTK_WIDGET (hb)->window, hb->steady_window, 0, 0);
641   gtk_widget_hide (hb->float_window);
642   gtk_widget_queue_resize (GTK_WIDGET (hb));
643
644   return FALSE;
645 }