08a71fc8871b2fcb5aecfc64d1ea3c7beaf59bcd
[pspp] / lib / gtk-contrib / gtkxpaned.c
1 /*******************************************************************************
2  **3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 
3  **      10        20        30        40        50        60        70        80
4  **
5  **  library for GtkXPaned-widget, a 2x2 grid-like variation of GtkPaned of gtk+
6  **  Copyright (C) 2012 Free Software Foundation, Inc.
7  **  Copyright (C) 2005-2006 Mirco "MacSlow" Müller <macslow@bangang.de>
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 Street, Fifth Floor, Boston, MA 02110-1301 USA
22  **
23  **  GtkXPaned is based on GtkPaned which was done by...
24  **
25  **  "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald"
26  **
27  **  and later modified by...
28  **
29  **  "the GTK+ Team and others 1997-2000"
30  **
31  *******************************************************************************/
32
33 #include <config.h>
34 #include "gtkxpaned.h"
35
36 #include <gtk/gtk.h>
37 #include <ui/gui/psppire-marshal.h>
38 #include <gdk/gdkkeysyms.h>
39
40
41 enum WidgetProperties
42   {
43     PROP_0,
44     PROP_X_POSITION,
45     PROP_Y_POSITION,
46     PROP_POSITION_SET,
47     PROP_MIN_X_POSITION,
48     PROP_MIN_Y_POSITION,
49     PROP_MAX_X_POSITION,
50     PROP_MAX_Y_POSITION
51   };
52
53 enum ChildProperties
54   {
55     CHILD_PROP_0,
56     CHILD_PROP_RESIZE,
57     CHILD_PROP_SHRINK
58   };
59
60 enum WidgetSignals
61   {
62     CYCLE_CHILD_FOCUS,
63     TOGGLE_HANDLE_FOCUS,
64     MOVE_HANDLE,
65     CYCLE_HANDLE_FOCUS,
66     ACCEPT_POSITION,
67     CANCEL_POSITION,
68     LAST_SIGNAL
69   };
70
71 static void gtk_xpaned_class_init (GtkXPanedClass * klass);
72
73 static void gtk_xpaned_init (GtkXPaned * xpaned);
74
75 static void gtk_xpaned_size_request (GtkWidget * widget,
76                                      GtkRequisition * requisition);
77
78 static void gtk_xpaned_size_allocate (GtkWidget * widget,
79                                       GtkAllocation * allocation);
80
81 static void gtk_xpaned_set_property (GObject * object,
82                                      guint prop_id,
83                                      const GValue * value,
84                                      GParamSpec * pspec);
85
86 static void gtk_xpaned_get_property (GObject * object,
87                                      guint prop_id,
88                                      GValue * value, GParamSpec * pspec);
89
90 static void gtk_xpaned_set_child_property (GtkContainer * container,
91                                            GtkWidget * child,
92                                            guint property_id,
93                                            const GValue * value,
94                                            GParamSpec * pspec);
95
96 static void gtk_xpaned_get_child_property (GtkContainer * container,
97                                            GtkWidget * child,
98                                            guint property_id,
99                                            GValue * value,
100                                            GParamSpec * pspec);
101
102 static void gtk_xpaned_finalize (GObject * object);
103
104 static void gtk_xpaned_realize (GtkWidget * widget);
105
106 static void gtk_xpaned_unrealize (GtkWidget * widget);
107
108 static void gtk_xpaned_map (GtkWidget * widget);
109
110 static void gtk_xpaned_unmap (GtkWidget * widget);
111
112 static gboolean gtk_xpaned_expose (GtkWidget * widget,
113                                    GdkEventExpose * event);
114
115 static gboolean gtk_xpaned_enter (GtkWidget * widget,
116                                   GdkEventCrossing * event);
117
118 static gboolean gtk_xpaned_leave (GtkWidget * widget,
119                                   GdkEventCrossing * event);
120
121 static gboolean gtk_xpaned_button_press (GtkWidget * widget,
122                                          GdkEventButton * event);
123
124 static gboolean gtk_xpaned_button_release (GtkWidget * widget,
125                                            GdkEventButton * event);
126
127 static gboolean gtk_xpaned_motion (GtkWidget * widget,
128                                    GdkEventMotion * event);
129
130 static gboolean gtk_xpaned_focus (GtkWidget * widget,
131                                   GtkDirectionType direction);
132
133 static void gtk_xpaned_add (GtkContainer * container, GtkWidget * widget);
134
135 static void gtk_xpaned_remove (GtkContainer * container, GtkWidget * widget);
136
137 static void gtk_xpaned_forall (GtkContainer * container,
138                                gboolean include_internals,
139                                GtkCallback callback, gpointer callback_data);
140
141 static void gtk_xpaned_set_focus_child (GtkContainer * container,
142                                         GtkWidget * child);
143
144 static void gtk_xpaned_set_saved_focus (GtkXPaned * xpaned,
145                                         GtkWidget * widget);
146
147 static void gtk_xpaned_set_first_xpaned (GtkXPaned * xpaned,
148                                          GtkXPaned * first_xpaned);
149
150 static void gtk_xpaned_set_last_top_left_child_focus (GtkXPaned * xpaned,
151                                                       GtkWidget * widget);
152
153 static void gtk_xpaned_set_last_top_right_child_focus (GtkXPaned * xpaned,
154                                                        GtkWidget * widget);
155
156 static void gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned * xpaned,
157                                                          GtkWidget * widget);
158
159 static void gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned * xpaned,
160                                                           GtkWidget * widget);
161
162 static gboolean gtk_xpaned_cycle_child_focus (GtkXPaned * xpaned,
163                                               gboolean reverse);
164
165 static gboolean gtk_xpaned_cycle_handle_focus (GtkXPaned * xpaned,
166                                                gboolean reverse);
167
168 static gboolean gtk_xpaned_move_handle (GtkXPaned * xpaned,
169                                         GtkScrollType scroll);
170
171 static gboolean gtk_xpaned_accept_position (GtkXPaned * xpaned);
172
173 static gboolean gtk_xpaned_cancel_position (GtkXPaned * xpaned);
174
175 static gboolean gtk_xpaned_toggle_handle_focus (GtkXPaned * xpaned);
176
177 static GType gtk_xpaned_child_type (GtkContainer * container);
178
179 static GtkContainerClass *parent_class = NULL;
180
181 struct _GtkXPanedPrivate
182 {
183   GtkWidget *saved_focus;
184   GtkXPaned *first_xpaned;
185 };
186
187 GType
188 gtk_xpaned_get_type (void)
189 {
190   static GType xpaned_type = 0;
191
192   if (!xpaned_type)
193     {
194       static const GTypeInfo xpaned_info = {
195         sizeof (GtkXPanedClass),
196         NULL,                   /* base_init */
197         NULL,                   /* base_finalize */
198         (GClassInitFunc) gtk_xpaned_class_init,
199         NULL,                   /* class_finalize */
200         NULL,                   /* class_data */
201         sizeof (GtkXPaned),
202         0,                      /* n_preallocs */
203         (GInstanceInitFunc) gtk_xpaned_init
204       };
205
206       xpaned_type = g_type_register_static (GTK_TYPE_CONTAINER,
207                                             "GtkXPaned", &xpaned_info, 0);
208     }
209
210   return xpaned_type;
211 }
212
213 GtkWidget *
214 gtk_xpaned_new (void)
215 {
216   GtkXPaned *xpaned;
217
218   xpaned = g_object_new (GTK_TYPE_XPANED, NULL);
219
220   return GTK_WIDGET (xpaned);
221 }
222
223 static guint signals[LAST_SIGNAL] = { 0 };
224
225 static void
226 add_tab_bindings (GtkBindingSet * binding_set, GdkModifierType modifiers)
227 {
228   gtk_binding_entry_add_signal (binding_set,
229                                 GDK_Tab, modifiers, "toggle_handle_focus", 0);
230
231   gtk_binding_entry_add_signal (binding_set,
232                                 GDK_KP_Tab,
233                                 modifiers, "toggle_handle_focus", 0);
234 }
235
236 static void
237 add_move_binding (GtkBindingSet * binding_set,
238                   guint keyval, GdkModifierType mask, GtkScrollType scroll)
239 {
240   gtk_binding_entry_add_signal (binding_set,
241                                 keyval,
242                                 mask,
243                                 "move_handle",
244                                 1, GTK_TYPE_SCROLL_TYPE, scroll);
245 }
246
247 static void
248 gtk_xpaned_class_init (GtkXPanedClass * class)
249 {
250   GObjectClass *object_class;
251   GtkWidgetClass *widget_class;
252   GtkContainerClass *container_class;
253   GtkXPanedClass *xpaned_class;
254   GtkBindingSet *binding_set;
255
256   object_class = (GObjectClass *) class;
257   widget_class = (GtkWidgetClass *) class;
258   container_class = (GtkContainerClass *) class;
259   xpaned_class = (GtkXPanedClass *) class;
260
261   parent_class = g_type_class_peek_parent (class);
262
263   object_class->set_property = gtk_xpaned_set_property;
264   object_class->get_property = gtk_xpaned_get_property;
265   object_class->finalize = gtk_xpaned_finalize;
266
267   widget_class->realize = gtk_xpaned_realize;
268   widget_class->unrealize = gtk_xpaned_unrealize;
269   widget_class->map = gtk_xpaned_map;
270   widget_class->unmap = gtk_xpaned_unmap;
271   widget_class->expose_event = gtk_xpaned_expose;
272   widget_class->focus = gtk_xpaned_focus;
273   widget_class->enter_notify_event = gtk_xpaned_enter;
274   widget_class->leave_notify_event = gtk_xpaned_leave;
275   widget_class->button_press_event = gtk_xpaned_button_press;
276   widget_class->button_release_event = gtk_xpaned_button_release;
277   widget_class->motion_notify_event = gtk_xpaned_motion;
278   widget_class->size_request = gtk_xpaned_size_request;
279   widget_class->size_allocate = gtk_xpaned_size_allocate;
280
281   container_class->add = gtk_xpaned_add;
282   container_class->remove = gtk_xpaned_remove;
283   container_class->forall = gtk_xpaned_forall;
284   container_class->child_type = gtk_xpaned_child_type;
285   container_class->set_focus_child = gtk_xpaned_set_focus_child;
286   container_class->set_child_property = gtk_xpaned_set_child_property;
287   container_class->get_child_property = gtk_xpaned_get_child_property;
288
289   xpaned_class->cycle_child_focus = gtk_xpaned_cycle_child_focus;
290   xpaned_class->toggle_handle_focus = gtk_xpaned_toggle_handle_focus;
291   xpaned_class->move_handle = gtk_xpaned_move_handle;
292   xpaned_class->cycle_handle_focus = gtk_xpaned_cycle_handle_focus;
293   xpaned_class->accept_position = gtk_xpaned_accept_position;
294   xpaned_class->cancel_position = gtk_xpaned_cancel_position;
295
296   g_object_class_install_property (object_class,
297                                    PROP_X_POSITION,
298                                    g_param_spec_int ("x-position",
299                                                      ("x-Position"),
300                                                      ("x-Position of paned separator in pixels (0 means all the way to the left)"),
301                                                      0,
302                                                      G_MAXINT,
303                                                      0,
304                                                      G_PARAM_READABLE |
305                                                      G_PARAM_WRITABLE));
306
307   g_object_class_install_property (object_class,
308                                    PROP_Y_POSITION,
309                                    g_param_spec_int ("y-position",
310                                                      "y-Position",
311                                                      "y-Position of paned separator in pixels (0 means all the way to the top)",
312                                                      0,
313                                                      G_MAXINT,
314                                                      0,
315                                                      G_PARAM_READABLE |
316                                                      G_PARAM_WRITABLE));
317
318   g_object_class_install_property (object_class,
319                                    PROP_POSITION_SET,
320                                    g_param_spec_boolean ("position-set",
321                                                          "Position Set",
322                                                          "TRUE if the Position property should be used",
323                                                          FALSE,
324                                                          G_PARAM_READABLE |
325                                                          G_PARAM_WRITABLE));
326
327   gtk_widget_class_install_style_property (widget_class,
328                                            g_param_spec_int ("handle-size",
329                                                              "Handle Size",
330                                                              "Width of handle",
331                                                              0,
332                                                              G_MAXINT,
333                                                              3,
334                                                              G_PARAM_READABLE));
335   /**
336    * GtkXPaned:min-x-position:
337    *
338    * The smallest possible value for the x-position property. This property is derived from the
339    * size and shrinkability of the widget's children.
340    *
341    * Since: 2.4
342    */
343   g_object_class_install_property (object_class,
344                                    PROP_MIN_X_POSITION,
345                                    g_param_spec_int ("min-x-position",
346                                                      "Minimal x-Position",
347                                                      "Smallest possible value for the \"x-position\" property",
348                                                      0,
349                                                      G_MAXINT,
350                                                      0, G_PARAM_READABLE));
351
352   /**
353    * GtkXPaned:min-y-position:
354    *
355    * The smallest possible value for the y-position property. This property is derived from the
356    * size and shrinkability of the widget's children.
357    *
358    * Since: 2.4
359    */
360   g_object_class_install_property (object_class,
361                                    PROP_MIN_Y_POSITION,
362                                    g_param_spec_int ("min-y-position",
363                                                      "Minimal y-Position",
364                                                      "Smallest possible value for the \"y-position\" property",
365                                                      0,
366                                                      G_MAXINT,
367                                                      0, G_PARAM_READABLE));
368
369   /**
370    * GtkPaned:max-x-position:
371    *
372    * The largest possible value for the x-position property. This property is derived from the
373    * size and shrinkability of the widget's children.
374    *
375    * Since: 2.4
376    */
377   g_object_class_install_property (object_class,
378                                    PROP_MAX_X_POSITION,
379                                    g_param_spec_int ("max-x-position",
380                                                      "Maximal x-Position",
381                                                      "Largest possible value for the \"x-position\" property",
382                                                      0,
383                                                      G_MAXINT,
384                                                      G_MAXINT,
385                                                      G_PARAM_READABLE));
386
387   /**
388    * GtkPaned:max-y-position:
389    *
390    * The largest possible value for the y-position property. This property is derived from the
391    * size and shrinkability of the widget's children.
392    *
393    * Since: 2.4
394    */
395   g_object_class_install_property (object_class,
396                                    PROP_MAX_Y_POSITION,
397                                    g_param_spec_int ("max-y-position",
398                                                      "Maximal y-Position",
399                                                      "Largest possible value for the \"y-position\" property",
400                                                      0,
401                                                      G_MAXINT,
402                                                      G_MAXINT,
403                                                      G_PARAM_READABLE));
404
405   /**
406    * GtkPaned:resize:
407    *
408    * The "resize" child property determines whether the child expands and 
409    * shrinks along with the paned widget.
410    * 
411    * Since: 2.4 
412    */
413   gtk_container_class_install_child_property (container_class,
414                                               CHILD_PROP_RESIZE,
415                                               g_param_spec_boolean ("resize",
416                                                                     "Resize",
417                                                                     "If TRUE, the child expands and shrinks along with the paned widget",
418                                                                     TRUE,
419                                                                     G_PARAM_READWRITE));
420
421   /**
422    * GtkPaned:shrink:
423    *
424    * The "shrink" child property determines whether the child can be made 
425    * smaller than its requisition.
426    * 
427    * Since: 2.4 
428    */
429   gtk_container_class_install_child_property (container_class,
430                                               CHILD_PROP_SHRINK,
431                                               g_param_spec_boolean ("shrink",
432                                                                     "Shrink",
433                                                                     "If TRUE, the child can be made smaller than its requisition",
434                                                                     TRUE,
435                                                                     G_PARAM_READWRITE));
436
437   signals[CYCLE_CHILD_FOCUS] = g_signal_new ("cycle-child-focus",
438                                              G_TYPE_FROM_CLASS (object_class),
439                                              G_SIGNAL_RUN_LAST |
440                                              G_SIGNAL_ACTION,
441                                              G_STRUCT_OFFSET (GtkXPanedClass,
442                                                               cycle_child_focus),
443                                              NULL, NULL,
444                                              psppire_marshal_BOOLEAN__BOOLEAN,
445                                              G_TYPE_BOOLEAN, 1,
446                                              G_TYPE_BOOLEAN);
447
448   signals[TOGGLE_HANDLE_FOCUS] = g_signal_new ("toggle-handle-focus",
449                                                G_TYPE_FROM_CLASS
450                                                (object_class),
451                                                G_SIGNAL_RUN_LAST |
452                                                G_SIGNAL_ACTION,
453                                                G_STRUCT_OFFSET
454                                                (GtkXPanedClass,
455                                                 toggle_handle_focus), NULL,
456                                                NULL,
457                                                psppire_marshal_BOOLEAN__VOID,
458                                                G_TYPE_BOOLEAN, 0);
459
460   signals[MOVE_HANDLE] = g_signal_new ("move-handle",
461                                        G_TYPE_FROM_CLASS (object_class),
462                                        G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
463                                        G_STRUCT_OFFSET (GtkXPanedClass,
464                                                         move_handle), NULL,
465                                        NULL, psppire_marshal_BOOLEAN__ENUM,
466                                        G_TYPE_BOOLEAN, 1,
467                                        GTK_TYPE_SCROLL_TYPE);
468
469   signals[CYCLE_HANDLE_FOCUS] = g_signal_new ("cycle-handle-focus",
470                                               G_TYPE_FROM_CLASS
471                                               (object_class),
472                                               G_SIGNAL_RUN_LAST |
473                                               G_SIGNAL_ACTION,
474                                               G_STRUCT_OFFSET (GtkXPanedClass,
475                                                                cycle_handle_focus),
476                                               NULL, NULL,
477                                               psppire_marshal_BOOLEAN__BOOLEAN,
478                                               G_TYPE_BOOLEAN, 1,
479                                               G_TYPE_BOOLEAN);
480
481   signals[ACCEPT_POSITION] = g_signal_new ("accept-position",
482                                            G_TYPE_FROM_CLASS (object_class),
483                                            G_SIGNAL_RUN_LAST |
484                                            G_SIGNAL_ACTION,
485                                            G_STRUCT_OFFSET (GtkXPanedClass,
486                                                             accept_position),
487                                            NULL, NULL,
488                                            psppire_marshal_BOOLEAN__VOID,
489                                            G_TYPE_BOOLEAN, 0);
490
491   signals[CANCEL_POSITION] = g_signal_new ("cancel-position",
492                                            G_TYPE_FROM_CLASS (object_class),
493                                            G_SIGNAL_RUN_LAST |
494                                            G_SIGNAL_ACTION,
495                                            G_STRUCT_OFFSET (GtkXPanedClass,
496                                                             cancel_position),
497                                            NULL, NULL,
498                                            psppire_marshal_BOOLEAN__VOID,
499                                            G_TYPE_BOOLEAN, 0);
500
501   binding_set = gtk_binding_set_by_class (class);
502
503   /* F6 and friends */
504   gtk_binding_entry_add_signal (binding_set,
505                                 GDK_F6, 0,
506                                 "cycle-child-focus", 1,
507                                 G_TYPE_BOOLEAN, FALSE);
508
509   gtk_binding_entry_add_signal (binding_set,
510                                 GDK_F6, GDK_SHIFT_MASK,
511                                 "cycle-child-focus", 1, G_TYPE_BOOLEAN, TRUE);
512
513   /* F8 and friends */
514   gtk_binding_entry_add_signal (binding_set,
515                                 GDK_F8, 0,
516                                 "cycle-handle-focus", 1,
517                                 G_TYPE_BOOLEAN, FALSE);
518
519   gtk_binding_entry_add_signal (binding_set,
520                                 GDK_F8, GDK_SHIFT_MASK,
521                                 "cycle-handle-focus", 1,
522                                 G_TYPE_BOOLEAN, TRUE);
523
524   add_tab_bindings (binding_set, 0);
525   add_tab_bindings (binding_set, GDK_CONTROL_MASK);
526   add_tab_bindings (binding_set, GDK_SHIFT_MASK);
527   add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK);
528
529   /* accept and cancel positions */
530   gtk_binding_entry_add_signal (binding_set,
531                                 GDK_Escape, 0, "cancel-position", 0);
532
533   gtk_binding_entry_add_signal (binding_set,
534                                 GDK_Return, 0, "accept-position", 0);
535
536   gtk_binding_entry_add_signal (binding_set,
537                                 GDK_KP_Enter, 0, "accept-position", 0);
538
539   gtk_binding_entry_add_signal (binding_set,
540                                 GDK_space, 0, "accept-position", 0);
541
542   gtk_binding_entry_add_signal (binding_set,
543                                 GDK_KP_Space, 0, "accept-position", 0);
544
545   /* move handle */
546   add_move_binding (binding_set, GDK_Left, 0, GTK_SCROLL_STEP_LEFT);
547   add_move_binding (binding_set, GDK_KP_Left, 0, GTK_SCROLL_STEP_LEFT);
548   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
549                     GTK_SCROLL_PAGE_LEFT);
550   add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
551                     GTK_SCROLL_PAGE_LEFT);
552
553   add_move_binding (binding_set, GDK_Right, 0, GTK_SCROLL_STEP_RIGHT);
554   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
555                     GTK_SCROLL_PAGE_RIGHT);
556   add_move_binding (binding_set, GDK_KP_Right, 0, GTK_SCROLL_STEP_RIGHT);
557   add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
558                     GTK_SCROLL_PAGE_RIGHT);
559
560   add_move_binding (binding_set, GDK_Up, 0, GTK_SCROLL_STEP_UP);
561   add_move_binding (binding_set, GDK_Up, GDK_CONTROL_MASK,
562                     GTK_SCROLL_PAGE_UP);
563   add_move_binding (binding_set, GDK_KP_Up, 0, GTK_SCROLL_STEP_UP);
564   add_move_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK,
565                     GTK_SCROLL_PAGE_UP);
566   add_move_binding (binding_set, GDK_Page_Up, 0, GTK_SCROLL_PAGE_UP);
567   add_move_binding (binding_set, GDK_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP);
568
569   add_move_binding (binding_set, GDK_Down, 0, GTK_SCROLL_STEP_DOWN);
570   add_move_binding (binding_set, GDK_Down, GDK_CONTROL_MASK,
571                     GTK_SCROLL_PAGE_DOWN);
572   add_move_binding (binding_set, GDK_KP_Down, 0, GTK_SCROLL_STEP_DOWN);
573   add_move_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK,
574                     GTK_SCROLL_PAGE_DOWN);
575   add_move_binding (binding_set, GDK_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);
576   add_move_binding (binding_set, GDK_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);
577
578   add_move_binding (binding_set, GDK_Home, 0, GTK_SCROLL_START);
579   add_move_binding (binding_set, GDK_KP_Home, 0, GTK_SCROLL_START);
580   add_move_binding (binding_set, GDK_End, 0, GTK_SCROLL_END);
581   add_move_binding (binding_set, GDK_KP_End, 0, GTK_SCROLL_END);
582 }
583
584 static GType
585 gtk_xpaned_child_type (GtkContainer * container)
586 {
587   if (!GTK_XPANED (container)->top_left_child ||
588       !GTK_XPANED (container)->top_right_child ||
589       !GTK_XPANED (container)->bottom_left_child ||
590       !GTK_XPANED (container)->bottom_right_child)
591     return GTK_TYPE_WIDGET;
592   else
593     return G_TYPE_NONE;
594 }
595
596 static void
597 gtk_xpaned_init (GtkXPaned * xpaned)
598 {
599   gtk_widget_set_can_focus (GTK_WIDGET (xpaned), TRUE);
600   gtk_widget_set_has_window (GTK_WIDGET (xpaned), FALSE);
601
602   xpaned->top_left_child = NULL;
603   xpaned->top_right_child = NULL;
604   xpaned->bottom_left_child = NULL;
605   xpaned->bottom_right_child = NULL;
606   xpaned->handle_east = NULL;
607   xpaned->handle_west = NULL;
608   xpaned->handle_north = NULL;
609   xpaned->handle_south = NULL;
610   xpaned->handle_middle = NULL;
611   xpaned->xor_gc = NULL;
612   xpaned->cursor_type_east = GDK_SB_V_DOUBLE_ARROW;
613   xpaned->cursor_type_west = GDK_SB_V_DOUBLE_ARROW;
614   xpaned->cursor_type_north = GDK_SB_H_DOUBLE_ARROW;
615   xpaned->cursor_type_south = GDK_SB_H_DOUBLE_ARROW;
616   xpaned->cursor_type_middle = GDK_FLEUR;
617
618   xpaned->handle_pos_east.width = 5;
619   xpaned->handle_pos_east.height = 5;
620   xpaned->handle_pos_west.width = 5;
621   xpaned->handle_pos_west.height = 5;
622   xpaned->handle_pos_north.width = 5;
623   xpaned->handle_pos_north.height = 5;
624   xpaned->handle_pos_south.width = 5;
625   xpaned->handle_pos_south.height = 5;
626   xpaned->handle_pos_middle.width = 5;
627   xpaned->handle_pos_middle.height = 5;
628
629   xpaned->position_set = FALSE;
630   xpaned->last_allocation.width = -1;
631   xpaned->last_allocation.height = -1;
632   xpaned->in_drag_vert = FALSE;
633   xpaned->in_drag_horiz = FALSE;
634   xpaned->in_drag_vert_and_horiz = FALSE;
635
636   xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE;
637   xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE;
638   xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE;
639   xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE;
640
641   xpaned->priv = g_new0 (GtkXPanedPrivate, 1);
642   xpaned->last_top_left_child_focus = NULL;
643   xpaned->last_top_right_child_focus = NULL;
644   xpaned->last_bottom_left_child_focus = NULL;
645   xpaned->last_bottom_right_child_focus = NULL;
646   xpaned->in_recursion = FALSE;
647   xpaned->handle_prelit = FALSE;
648   xpaned->original_position.x = -1;
649   xpaned->original_position.y = -1;
650   xpaned->unmaximized_position.x = -1;
651   xpaned->unmaximized_position.y = -1;
652
653   xpaned->handle_pos_east.x = -1;
654   xpaned->handle_pos_east.y = -1;
655   xpaned->handle_pos_west.x = -1;
656   xpaned->handle_pos_west.y = -1;
657   xpaned->handle_pos_north.x = -1;
658   xpaned->handle_pos_north.y = -1;
659   xpaned->handle_pos_south.x = -1;
660   xpaned->handle_pos_south.y = -1;
661   xpaned->handle_pos_middle.x = -1;
662   xpaned->handle_pos_middle.y = -1;
663
664   xpaned->drag_pos.x = -1;
665   xpaned->drag_pos.y = -1;
666 }
667
668 static void
669 gtk_xpaned_size_request (GtkWidget * widget, GtkRequisition * requisition)
670 {
671   GtkXPaned *xpaned = GTK_XPANED (widget);
672   GtkRequisition child_requisition;
673
674   requisition->width = 0;
675   requisition->height = 0;
676
677   if (xpaned->top_left_child
678       && gtk_widget_get_visible (xpaned->top_left_child))
679     {
680       gtk_widget_size_request (xpaned->top_left_child, &child_requisition);
681
682       requisition->width = child_requisition.width;
683       requisition->height = child_requisition.height;
684     }
685
686   if (xpaned->top_right_child
687       && gtk_widget_get_visible (xpaned->top_right_child))
688     {
689       gtk_widget_size_request (xpaned->top_right_child, &child_requisition);
690
691       requisition->width += child_requisition.width;
692       requisition->height =
693         MAX (requisition->height, child_requisition.height);
694     }
695
696   if (xpaned->bottom_left_child
697       && gtk_widget_get_visible (xpaned->bottom_left_child))
698     {
699       gtk_widget_size_request (xpaned->bottom_left_child, &child_requisition);
700
701       requisition->width = MAX (requisition->width, child_requisition.width);
702       requisition->height += child_requisition.height;
703     }
704
705   if (xpaned->bottom_right_child
706       && gtk_widget_get_visible (xpaned->bottom_right_child))
707     {
708       gtk_widget_size_request (xpaned->bottom_right_child,
709                                &child_requisition);
710
711       requisition->width = child_requisition.width;
712       requisition->height = child_requisition.height;
713     }
714
715   /* add 2 times the set border-width to the GtkXPaneds requisition */
716   requisition->width += GTK_CONTAINER (xpaned)->border_width * 2;
717   requisition->height += GTK_CONTAINER (xpaned)->border_width * 2;
718
719   /* also add the handle "thickness" to GtkXPaneds width- and height-requisitions */
720   if (xpaned->top_left_child
721       && gtk_widget_get_visible (xpaned->top_left_child)
722       && xpaned->top_right_child
723       && gtk_widget_get_visible (xpaned->top_right_child)
724       && xpaned->bottom_left_child
725       && gtk_widget_get_visible (xpaned->bottom_left_child)
726       && xpaned->bottom_right_child
727       && gtk_widget_get_visible (xpaned->bottom_right_child))
728     {
729       gint handle_size;
730
731       gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
732       requisition->width += handle_size;
733       requisition->height += handle_size;
734     }
735 }
736
737 void
738 gtk_xpaned_compute_position (GtkXPaned * xpaned,
739                              const GtkAllocation * allocation,
740                              GtkRequisition * top_left_child_req,
741                              GtkRequisition * top_right_child_req,
742                              GtkRequisition * bottom_left_child_req,
743                              GtkRequisition * bottom_right_child_req);
744
745
746 static void
747 gtk_xpaned_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
748 {
749   GtkXPaned *xpaned = GTK_XPANED (widget);
750   gint border_width = GTK_CONTAINER (xpaned)->border_width;
751   GtkAllocation top_left_child_allocation;
752   GtkAllocation top_right_child_allocation;
753   GtkAllocation bottom_left_child_allocation;
754   GtkAllocation bottom_right_child_allocation;
755   GtkRequisition top_left_child_requisition;
756   GtkRequisition top_right_child_requisition;
757   GtkRequisition bottom_left_child_requisition;
758   GtkRequisition bottom_right_child_requisition;
759   gint handle_size;
760
761   /* determine size of handle(s) */
762   gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
763
764   widget->allocation = *allocation;
765
766   if (xpaned->top_left_child
767       && gtk_widget_get_visible (xpaned->top_left_child)
768       && xpaned->top_right_child
769       && gtk_widget_get_visible (xpaned->top_right_child)
770       && xpaned->bottom_left_child
771       && gtk_widget_get_visible (xpaned->bottom_left_child)
772       && xpaned->bottom_right_child
773       && gtk_widget_get_visible (xpaned->bottom_right_child))
774     {
775       /* what sizes do the children want to be at least at */
776       gtk_widget_get_child_requisition (xpaned->top_left_child,
777                                         &top_left_child_requisition);
778       gtk_widget_get_child_requisition (xpaned->top_right_child,
779                                         &top_right_child_requisition);
780       gtk_widget_get_child_requisition (xpaned->bottom_left_child,
781                                         &bottom_left_child_requisition);
782       gtk_widget_get_child_requisition (xpaned->bottom_right_child,
783                                         &bottom_right_child_requisition);
784
785       /* determine the total requisition-sum of all requisitions of borders,
786        * handles, children etc. */
787       gtk_xpaned_compute_position (xpaned,
788                                    allocation,
789                                    &top_left_child_requisition,
790                                    &top_right_child_requisition,
791                                    &bottom_left_child_requisition,
792                                    &bottom_right_child_requisition);
793
794       /* calculate the current positions and sizes of the handles */
795       xpaned->handle_pos_east.x =
796         widget->allocation.x + border_width +
797         xpaned->top_left_child_size.width + handle_size;
798       xpaned->handle_pos_east.y =
799         widget->allocation.y + border_width +
800         xpaned->top_left_child_size.height;
801       xpaned->handle_pos_east.width =
802         widget->allocation.width - xpaned->top_left_child_size.width -
803         2 * border_width - handle_size;
804       xpaned->handle_pos_east.height = handle_size;
805
806       xpaned->handle_pos_west.x = widget->allocation.x + border_width;
807       xpaned->handle_pos_west.y = xpaned->handle_pos_east.y;
808       xpaned->handle_pos_west.width =
809         widget->allocation.width - xpaned->handle_pos_east.width -
810         2 * border_width - handle_size;
811       xpaned->handle_pos_west.height = handle_size;
812
813       xpaned->handle_pos_north.x = xpaned->handle_pos_east.x - handle_size;
814       xpaned->handle_pos_north.y = widget->allocation.y + border_width;
815       xpaned->handle_pos_north.width = handle_size;
816       xpaned->handle_pos_north.height =
817         xpaned->handle_pos_east.y - widget->allocation.y - border_width;
818
819       xpaned->handle_pos_south.x = xpaned->handle_pos_north.x;
820       xpaned->handle_pos_south.y = xpaned->handle_pos_east.y + handle_size;
821       xpaned->handle_pos_south.width = handle_size;
822       xpaned->handle_pos_south.height =
823         widget->allocation.height - xpaned->handle_pos_north.height -
824         2 * border_width - handle_size;
825
826
827 #define CENTRUM 20
828       xpaned->handle_pos_middle.x = xpaned->handle_pos_north.x;
829       xpaned->handle_pos_middle.y = xpaned->handle_pos_east.y;
830       xpaned->handle_pos_middle.width = handle_size + CENTRUM;
831       xpaned->handle_pos_middle.height = handle_size + CENTRUM;
832
833       /* set allocation for top-left child */
834       top_left_child_allocation.x = widget->allocation.x + border_width;
835       top_left_child_allocation.y = widget->allocation.y + border_width;
836       top_left_child_allocation.width = xpaned->handle_pos_west.width;
837       top_left_child_allocation.height = xpaned->handle_pos_north.height;
838
839       /* set allocation for top-right child */
840       top_right_child_allocation.x =
841         widget->allocation.x + border_width + handle_size +
842         top_left_child_allocation.width;
843       top_right_child_allocation.y = widget->allocation.y + border_width;
844       top_right_child_allocation.width = xpaned->handle_pos_east.width;
845       top_right_child_allocation.height = xpaned->handle_pos_north.height;
846
847       /* set allocation for bottom-left child */
848       bottom_left_child_allocation.x = xpaned->handle_pos_west.x;
849       bottom_left_child_allocation.y = xpaned->handle_pos_south.y;
850       bottom_left_child_allocation.width = xpaned->handle_pos_west.width;
851       bottom_left_child_allocation.height = xpaned->handle_pos_south.height;
852
853       /* set allocation for bottom-right child */
854       bottom_right_child_allocation.x = top_right_child_allocation.x;
855       bottom_right_child_allocation.y = bottom_left_child_allocation.y;
856       bottom_right_child_allocation.width = xpaned->handle_pos_east.width;
857       bottom_right_child_allocation.height = xpaned->handle_pos_south.height;
858
859       if (gtk_widget_get_realized (widget))
860         {
861           if (gtk_widget_get_mapped (widget))
862             {
863               gdk_window_show (xpaned->handle_east);
864               gdk_window_show (xpaned->handle_west);
865               gdk_window_show (xpaned->handle_north);
866               gdk_window_show (xpaned->handle_south);
867               gdk_window_show (xpaned->handle_middle);
868             }
869
870           gdk_window_move_resize (xpaned->handle_east,
871                                   xpaned->handle_pos_east.x,
872                                   xpaned->handle_pos_east.y,
873                                   xpaned->handle_pos_east.width,
874                                   xpaned->handle_pos_east.height);
875
876           gdk_window_move_resize (xpaned->handle_west,
877                                   xpaned->handle_pos_west.x,
878                                   xpaned->handle_pos_west.y,
879                                   xpaned->handle_pos_west.width,
880                                   xpaned->handle_pos_west.height);
881
882           gdk_window_move_resize (xpaned->handle_north,
883                                   xpaned->handle_pos_north.x,
884                                   xpaned->handle_pos_north.y,
885                                   xpaned->handle_pos_north.width,
886                                   xpaned->handle_pos_north.height);
887
888           gdk_window_move_resize (xpaned->handle_south,
889                                   xpaned->handle_pos_south.x,
890                                   xpaned->handle_pos_south.y,
891                                   xpaned->handle_pos_south.width,
892                                   xpaned->handle_pos_south.height);
893
894           gdk_window_move_resize (xpaned->handle_middle,
895                                   xpaned->handle_pos_middle.x,
896                                   xpaned->handle_pos_middle.y,
897                                   xpaned->handle_pos_middle.width,
898                                   xpaned->handle_pos_middle.height);
899         }
900
901       /* Now allocate the childen, making sure, when resizing not to
902        * overlap the windows
903        */
904       if (gtk_widget_get_mapped (widget))
905         {
906           gtk_widget_size_allocate (xpaned->top_right_child,
907                                     &top_right_child_allocation);
908           gtk_widget_size_allocate (xpaned->top_left_child,
909                                     &top_left_child_allocation);
910           gtk_widget_size_allocate (xpaned->bottom_left_child,
911                                     &bottom_left_child_allocation);
912           gtk_widget_size_allocate (xpaned->bottom_right_child,
913                                     &bottom_right_child_allocation);
914         }
915     }
916 }
917
918 static void
919 gtk_xpaned_set_property (GObject * object,
920                          guint prop_id,
921                          const GValue * value, GParamSpec * pspec)
922 {
923   GtkXPaned *xpaned = GTK_XPANED (object);
924
925   switch (prop_id)
926     {
927     case PROP_X_POSITION:
928       gtk_xpaned_set_position_x (xpaned, g_value_get_int (value));
929       break;
930
931     case PROP_Y_POSITION:
932       gtk_xpaned_set_position_y (xpaned, g_value_get_int (value));
933       break;
934
935     case PROP_POSITION_SET:
936       xpaned->position_set = g_value_get_boolean (value);
937       gtk_widget_queue_resize (GTK_WIDGET (xpaned));
938       break;
939
940     default:
941       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
942       break;
943     }
944 }
945
946 static void
947 gtk_xpaned_get_property (GObject * object,
948                          guint prop_id, GValue * value, GParamSpec * pspec)
949 {
950   GtkXPaned *xpaned = GTK_XPANED (object);
951
952   switch (prop_id)
953     {
954     case PROP_X_POSITION:
955       g_value_set_int (value, xpaned->top_left_child_size.width);
956       break;
957
958     case PROP_Y_POSITION:
959       g_value_set_int (value, xpaned->top_left_child_size.height);
960       break;
961
962     case PROP_POSITION_SET:
963       g_value_set_boolean (value, xpaned->position_set);
964       break;
965
966     case PROP_MIN_X_POSITION:
967       g_value_set_int (value, xpaned->min_position.x);
968       break;
969
970     case PROP_MIN_Y_POSITION:
971       g_value_set_int (value, xpaned->min_position.y);
972       break;
973
974     case PROP_MAX_X_POSITION:
975       g_value_set_int (value, xpaned->max_position.x);
976       break;
977
978     case PROP_MAX_Y_POSITION:
979       g_value_set_int (value, xpaned->max_position.y);
980       break;
981
982     default:
983       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
984       break;
985     }
986 }
987
988 static void
989 gtk_xpaned_set_child_property (GtkContainer * container,
990                                GtkWidget * child,
991                                guint property_id,
992                                const GValue * value, GParamSpec * pspec)
993 {
994   GtkXPaned *xpaned = GTK_XPANED (container);
995   gboolean old_value = FALSE;
996   gboolean new_value = FALSE;
997
998   g_assert (child == xpaned->top_left_child ||
999             child == xpaned->top_right_child ||
1000             child == xpaned->bottom_left_child ||
1001             child == xpaned->bottom_right_child);
1002
1003   new_value = g_value_get_boolean (value);
1004
1005   switch (property_id)
1006     {
1007     case CHILD_PROP_RESIZE:
1008       if (child == xpaned->top_left_child)
1009         {
1010           old_value = xpaned->top_left_child_resize;
1011           xpaned->top_left_child_resize = new_value;
1012         }
1013       else if (child == xpaned->top_right_child)
1014         {
1015           old_value = xpaned->top_right_child_resize;
1016           xpaned->top_right_child_resize = new_value;
1017         }
1018       else if (child == xpaned->bottom_left_child)
1019         {
1020           old_value = xpaned->bottom_left_child_resize;
1021           xpaned->bottom_left_child_resize = new_value;
1022         }
1023       else if (child == xpaned->bottom_right_child)
1024         {
1025           old_value = xpaned->bottom_right_child_resize;
1026           xpaned->bottom_right_child_resize = new_value;
1027         }
1028       break;
1029
1030     case CHILD_PROP_SHRINK:
1031       if (child == xpaned->top_left_child)
1032         {
1033           old_value = xpaned->top_left_child_shrink;
1034           xpaned->top_left_child_shrink = new_value;
1035         }
1036       else if (child == xpaned->top_right_child)
1037         {
1038           old_value = xpaned->top_right_child_shrink;
1039           xpaned->top_right_child_shrink = new_value;
1040         }
1041       else if (child == xpaned->bottom_left_child)
1042         {
1043           old_value = xpaned->bottom_left_child_shrink;
1044           xpaned->bottom_left_child_shrink = new_value;
1045         }
1046       else if (child == xpaned->bottom_right_child)
1047         {
1048           old_value = xpaned->bottom_right_child_shrink;
1049           xpaned->bottom_right_child_shrink = new_value;
1050         }
1051       break;
1052
1053     default:
1054       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container,
1055                                                     property_id, pspec);
1056       old_value = -1;           /* quiet gcc */
1057       break;
1058     }
1059
1060   if (old_value != new_value)
1061     gtk_widget_queue_resize (GTK_WIDGET (container));
1062 }
1063
1064 static void
1065 gtk_xpaned_get_child_property (GtkContainer * container,
1066                                GtkWidget * child,
1067                                guint property_id,
1068                                GValue * value, GParamSpec * pspec)
1069 {
1070   GtkXPaned *xpaned = GTK_XPANED (container);
1071
1072   g_assert (child == xpaned->top_left_child ||
1073             child == xpaned->top_right_child ||
1074             child == xpaned->bottom_left_child ||
1075             child == xpaned->bottom_right_child);
1076
1077   switch (property_id)
1078     {
1079     case CHILD_PROP_RESIZE:
1080       if (child == xpaned->top_left_child)
1081         g_value_set_boolean (value, xpaned->top_left_child_resize);
1082       else if (child == xpaned->top_right_child)
1083         g_value_set_boolean (value, xpaned->top_right_child_resize);
1084       else if (child == xpaned->bottom_left_child)
1085         g_value_set_boolean (value, xpaned->bottom_left_child_resize);
1086       else if (child == xpaned->bottom_right_child)
1087         g_value_set_boolean (value, xpaned->bottom_right_child_resize);
1088       break;
1089
1090     case CHILD_PROP_SHRINK:
1091       if (child == xpaned->top_left_child)
1092         g_value_set_boolean (value, xpaned->top_left_child_shrink);
1093       else if (child == xpaned->top_right_child)
1094         g_value_set_boolean (value, xpaned->top_right_child_shrink);
1095       else if (child == xpaned->bottom_left_child)
1096         g_value_set_boolean (value, xpaned->bottom_left_child_shrink);
1097       else if (child == xpaned->bottom_right_child)
1098         g_value_set_boolean (value, xpaned->bottom_right_child_shrink);
1099       break;
1100
1101     default:
1102       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container,
1103                                                     property_id, pspec);
1104       break;
1105     }
1106 }
1107
1108 static void
1109 gtk_xpaned_finalize (GObject * object)
1110 {
1111   GtkXPaned *xpaned = GTK_XPANED (object);
1112
1113   gtk_xpaned_set_saved_focus (xpaned, NULL);
1114   gtk_xpaned_set_first_xpaned (xpaned, NULL);
1115
1116   g_free (xpaned->priv);
1117
1118   G_OBJECT_CLASS (parent_class)->finalize (object);
1119 }
1120
1121 static void
1122 gtk_xpaned_realize (GtkWidget * widget)
1123 {
1124   GtkXPaned *xpaned;
1125   GdkWindowAttr attributes_east;
1126   GdkWindowAttr attributes_west;
1127   GdkWindowAttr attributes_north;
1128   GdkWindowAttr attributes_south;
1129   GdkWindowAttr attributes_middle;
1130   gint attributes_mask_east;
1131   gint attributes_mask_west;
1132   gint attributes_mask_north;
1133   gint attributes_mask_south;
1134   gint attributes_mask_middle;
1135
1136   gtk_widget_set_realized (widget, TRUE);
1137   xpaned = GTK_XPANED (widget);
1138
1139   widget->window = gtk_widget_get_parent_window (widget);
1140   g_object_ref (widget->window);
1141
1142   attributes_east.window_type = GDK_WINDOW_CHILD;
1143   attributes_west.window_type = GDK_WINDOW_CHILD;
1144   attributes_north.window_type = GDK_WINDOW_CHILD;
1145   attributes_south.window_type = GDK_WINDOW_CHILD;
1146   attributes_middle.window_type = GDK_WINDOW_CHILD;
1147
1148   attributes_east.wclass = GDK_INPUT_ONLY;
1149   attributes_west.wclass = GDK_INPUT_ONLY;
1150   attributes_north.wclass = GDK_INPUT_ONLY;
1151   attributes_south.wclass = GDK_INPUT_ONLY;
1152   attributes_middle.wclass = GDK_INPUT_ONLY;
1153
1154   attributes_east.x = xpaned->handle_pos_east.x;
1155   attributes_east.y = xpaned->handle_pos_east.y;
1156   attributes_east.width = xpaned->handle_pos_east.width;
1157   attributes_east.height = xpaned->handle_pos_east.height;
1158
1159   attributes_west.x = xpaned->handle_pos_west.x;
1160   attributes_west.y = xpaned->handle_pos_west.y;
1161   attributes_west.width = xpaned->handle_pos_west.width;
1162   attributes_west.height = xpaned->handle_pos_west.height;
1163
1164   attributes_north.x = xpaned->handle_pos_north.x;
1165   attributes_north.y = xpaned->handle_pos_north.y;
1166   attributes_north.width = xpaned->handle_pos_north.width;
1167   attributes_north.height = xpaned->handle_pos_north.height;
1168
1169   attributes_south.x = xpaned->handle_pos_south.x;
1170   attributes_south.y = xpaned->handle_pos_south.y;
1171   attributes_south.width = xpaned->handle_pos_south.width;
1172   attributes_south.height = xpaned->handle_pos_south.height;
1173
1174   attributes_middle.x = xpaned->handle_pos_middle.x;
1175   attributes_middle.y = xpaned->handle_pos_middle.y;
1176   attributes_middle.width = xpaned->handle_pos_middle.width;
1177   attributes_middle.height = xpaned->handle_pos_middle.height;
1178
1179   attributes_east.cursor =
1180     gdk_cursor_new_for_display (gtk_widget_get_display (widget),
1181                                 xpaned->cursor_type_east);
1182   attributes_west.cursor =
1183     gdk_cursor_new_for_display (gtk_widget_get_display (widget),
1184                                 xpaned->cursor_type_west);
1185   attributes_north.cursor =
1186     gdk_cursor_new_for_display (gtk_widget_get_display (widget),
1187                                 xpaned->cursor_type_north);
1188   attributes_south.cursor =
1189     gdk_cursor_new_for_display (gtk_widget_get_display (widget),
1190                                 xpaned->cursor_type_south);
1191   attributes_middle.cursor =
1192     gdk_cursor_new_for_display (gtk_widget_get_display (widget),
1193                                 xpaned->cursor_type_middle);
1194
1195   attributes_east.event_mask = gtk_widget_get_events (widget);
1196   attributes_west.event_mask = gtk_widget_get_events (widget);
1197   attributes_north.event_mask = gtk_widget_get_events (widget);
1198   attributes_south.event_mask = gtk_widget_get_events (widget);
1199   attributes_middle.event_mask = gtk_widget_get_events (widget);
1200
1201   attributes_east.event_mask |= (GDK_BUTTON_PRESS_MASK |
1202                                  GDK_BUTTON_RELEASE_MASK |
1203                                  GDK_ENTER_NOTIFY_MASK |
1204                                  GDK_LEAVE_NOTIFY_MASK |
1205                                  GDK_POINTER_MOTION_MASK |
1206                                  GDK_POINTER_MOTION_HINT_MASK);
1207   attributes_west.event_mask |= (GDK_BUTTON_PRESS_MASK |
1208                                  GDK_BUTTON_RELEASE_MASK |
1209                                  GDK_ENTER_NOTIFY_MASK |
1210                                  GDK_LEAVE_NOTIFY_MASK |
1211                                  GDK_POINTER_MOTION_MASK |
1212                                  GDK_POINTER_MOTION_HINT_MASK);
1213   attributes_north.event_mask |= (GDK_BUTTON_PRESS_MASK |
1214                                   GDK_BUTTON_RELEASE_MASK |
1215                                   GDK_ENTER_NOTIFY_MASK |
1216                                   GDK_LEAVE_NOTIFY_MASK |
1217                                   GDK_POINTER_MOTION_MASK |
1218                                   GDK_POINTER_MOTION_HINT_MASK);
1219   attributes_south.event_mask |= (GDK_BUTTON_PRESS_MASK |
1220                                   GDK_BUTTON_RELEASE_MASK |
1221                                   GDK_ENTER_NOTIFY_MASK |
1222                                   GDK_LEAVE_NOTIFY_MASK |
1223                                   GDK_POINTER_MOTION_MASK |
1224                                   GDK_POINTER_MOTION_HINT_MASK);
1225   attributes_middle.event_mask |= (GDK_BUTTON_PRESS_MASK |
1226                                    GDK_BUTTON_RELEASE_MASK |
1227                                    GDK_ENTER_NOTIFY_MASK |
1228                                    GDK_LEAVE_NOTIFY_MASK |
1229                                    GDK_POINTER_MOTION_MASK |
1230                                    GDK_POINTER_MOTION_HINT_MASK);
1231
1232   attributes_mask_east = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
1233   attributes_mask_west = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
1234   attributes_mask_north = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
1235   attributes_mask_south = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
1236   attributes_mask_middle = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
1237
1238   xpaned->handle_east = gdk_window_new (widget->window,
1239                                         &attributes_east,
1240                                         attributes_mask_east);
1241   xpaned->handle_west = gdk_window_new (widget->window,
1242                                         &attributes_west,
1243                                         attributes_mask_west);
1244   xpaned->handle_north = gdk_window_new (widget->window,
1245                                          &attributes_north,
1246                                          attributes_mask_north);
1247   xpaned->handle_south = gdk_window_new (widget->window,
1248                                          &attributes_south,
1249                                          attributes_mask_south);
1250   xpaned->handle_middle = gdk_window_new (widget->window,
1251                                           &attributes_middle,
1252                                           attributes_mask_middle);
1253
1254   gdk_window_set_user_data (xpaned->handle_east, xpaned);
1255   gdk_window_set_user_data (xpaned->handle_west, xpaned);
1256   gdk_window_set_user_data (xpaned->handle_north, xpaned);
1257   gdk_window_set_user_data (xpaned->handle_south, xpaned);
1258   gdk_window_set_user_data (xpaned->handle_middle, xpaned);
1259
1260   gdk_cursor_unref (attributes_east.cursor);
1261   gdk_cursor_unref (attributes_west.cursor);
1262   gdk_cursor_unref (attributes_north.cursor);
1263   gdk_cursor_unref (attributes_south.cursor);
1264   gdk_cursor_unref (attributes_middle.cursor);
1265
1266   widget->style = gtk_style_attach (widget->style, widget->window);
1267
1268   if (xpaned->top_left_child
1269       && gtk_widget_get_visible (xpaned->top_left_child)
1270       && xpaned->top_right_child
1271       && gtk_widget_get_visible (xpaned->top_right_child)
1272       && xpaned->bottom_left_child
1273       && gtk_widget_get_visible (xpaned->bottom_left_child)
1274       && xpaned->bottom_right_child
1275       && gtk_widget_get_visible (xpaned->bottom_right_child))
1276     {
1277       gdk_window_show (xpaned->handle_east);
1278       gdk_window_show (xpaned->handle_west);
1279       gdk_window_show (xpaned->handle_north);
1280       gdk_window_show (xpaned->handle_south);
1281       gdk_window_show (xpaned->handle_middle);
1282     }
1283 }
1284
1285 static void
1286 gtk_xpaned_unrealize (GtkWidget * widget)
1287 {
1288   GtkXPaned *xpaned = GTK_XPANED (widget);
1289
1290   if (xpaned->xor_gc)
1291     {
1292       g_object_unref (xpaned->xor_gc);
1293       xpaned->xor_gc = NULL;
1294     }
1295
1296   if (xpaned->handle_east)
1297     {
1298       gdk_window_set_user_data (xpaned->handle_east, NULL);
1299       gdk_window_destroy (xpaned->handle_east);
1300       xpaned->handle_east = NULL;
1301     }
1302
1303   if (xpaned->handle_west)
1304     {
1305       gdk_window_set_user_data (xpaned->handle_west, NULL);
1306       gdk_window_destroy (xpaned->handle_west);
1307       xpaned->handle_west = NULL;
1308     }
1309
1310   if (xpaned->handle_north)
1311     {
1312       gdk_window_set_user_data (xpaned->handle_north, NULL);
1313       gdk_window_destroy (xpaned->handle_north);
1314       xpaned->handle_north = NULL;
1315     }
1316
1317   if (xpaned->handle_south)
1318     {
1319       gdk_window_set_user_data (xpaned->handle_south, NULL);
1320       gdk_window_destroy (xpaned->handle_south);
1321       xpaned->handle_south = NULL;
1322     }
1323
1324   if (xpaned->handle_middle)
1325     {
1326       gdk_window_set_user_data (xpaned->handle_middle, NULL);
1327       gdk_window_destroy (xpaned->handle_middle);
1328       xpaned->handle_middle = NULL;
1329     }
1330
1331   gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL);
1332   gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL);
1333   gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL);
1334   gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL);
1335   gtk_xpaned_set_saved_focus (xpaned, NULL);
1336   gtk_xpaned_set_first_xpaned (xpaned, NULL);
1337
1338   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1339     (*GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1340 }
1341
1342 static void
1343 gtk_xpaned_map (GtkWidget * widget)
1344 {
1345   GtkXPaned *xpaned = GTK_XPANED (widget);
1346
1347   gdk_window_show (xpaned->handle_east);
1348   gdk_window_show (xpaned->handle_west);
1349   gdk_window_show (xpaned->handle_north);
1350   gdk_window_show (xpaned->handle_south);
1351   gdk_window_show (xpaned->handle_middle);
1352
1353   GTK_WIDGET_CLASS (parent_class)->map (widget);
1354 }
1355
1356 static void
1357 gtk_xpaned_unmap (GtkWidget * widget)
1358 {
1359   GtkXPaned *xpaned = GTK_XPANED (widget);
1360
1361   gdk_window_hide (xpaned->handle_east);
1362   gdk_window_hide (xpaned->handle_west);
1363   gdk_window_hide (xpaned->handle_north);
1364   gdk_window_hide (xpaned->handle_south);
1365   gdk_window_hide (xpaned->handle_middle);
1366
1367   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
1368 }
1369
1370 static gboolean
1371 gtk_xpaned_expose (GtkWidget * widget, GdkEventExpose * event)
1372 {
1373   GtkXPaned *xpaned = GTK_XPANED (widget);
1374   gint handle_size;
1375   GdkRectangle horizontalClipArea;
1376   GdkRectangle verticalClipArea;
1377
1378   /* determine size of handle(s) */
1379   gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
1380
1381   /* I want the handle-"thickness" to be at least 3 */
1382   g_assert (handle_size >= 3);
1383
1384   if (gtk_widget_get_visible (widget) && gtk_widget_get_mapped (widget) &&
1385       xpaned->top_left_child
1386       && gtk_widget_get_visible (xpaned->top_left_child)
1387       && xpaned->top_right_child
1388       && gtk_widget_get_visible (xpaned->top_right_child)
1389       && xpaned->bottom_left_child
1390       && gtk_widget_get_visible (xpaned->bottom_left_child)
1391       && xpaned->bottom_right_child
1392       && gtk_widget_get_visible (xpaned->bottom_right_child))
1393     {
1394       GtkStateType state;
1395
1396       if (gtk_widget_is_focus (widget))
1397         state = GTK_STATE_SELECTED;
1398       else if (xpaned->handle_prelit)
1399         state = GTK_STATE_PRELIGHT;
1400       else
1401         state = gtk_widget_get_state (widget);
1402
1403       horizontalClipArea.x = xpaned->handle_pos_west.x;
1404       horizontalClipArea.y = xpaned->handle_pos_west.y;
1405       horizontalClipArea.width =
1406         xpaned->handle_pos_west.width + handle_size +
1407         xpaned->handle_pos_east.width;
1408       horizontalClipArea.height = handle_size;
1409
1410       verticalClipArea.x = xpaned->handle_pos_north.x;
1411       verticalClipArea.y = xpaned->handle_pos_north.y;
1412       verticalClipArea.width = handle_size;
1413       verticalClipArea.height =
1414         xpaned->handle_pos_north.height + handle_size +
1415         xpaned->handle_pos_south.height;
1416
1417       gtk_paint_handle (widget->style,
1418                         widget->window,
1419                         state,
1420                         GTK_SHADOW_NONE,
1421                         &horizontalClipArea,
1422                         widget,
1423                         "paned",
1424                         xpaned->handle_pos_east.x - handle_size - 256 / 2,
1425                         xpaned->handle_pos_west.y + 1,
1426                         256 + handle_size, handle_size - 2,
1427                         /*xpaned->handle_pos_west.x,
1428                           xpaned->handle_pos_west.y + 1,
1429                           xpaned->handle_pos_west.width + handle_size + xpaned->handle_pos_east.width,
1430                           handle_size - 2, */
1431                         GTK_ORIENTATION_HORIZONTAL);
1432       gtk_paint_handle (widget->style,
1433                         widget->window,
1434                         state,
1435                         GTK_SHADOW_NONE,
1436                         &verticalClipArea,
1437                         widget,
1438                         "paned",
1439                         xpaned->handle_pos_north.x + 1,
1440                         xpaned->handle_pos_south.y - handle_size - 256 / 2,
1441                         handle_size - 2, 256 + handle_size,
1442                         /*xpaned->handle_pos_north.x + 1,
1443                           xpaned->handle_pos_north.y,
1444                           handle_size - 2,
1445                           xpaned->handle_pos_north.height + handle_size + xpaned->handle_pos_south.height, */
1446                         GTK_ORIENTATION_VERTICAL);
1447     }
1448
1449   /* Chain up to draw children */
1450   GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
1451
1452   return FALSE;
1453 }
1454
1455 static gboolean
1456 is_rtl (GtkXPaned * xpaned)
1457 {
1458   if (gtk_widget_get_direction (GTK_WIDGET (xpaned)) == GTK_TEXT_DIR_RTL)
1459     return TRUE;
1460
1461   return FALSE;
1462 }
1463
1464 static void
1465 update_drag (GtkXPaned * xpaned)
1466 {
1467   GdkPoint pos;
1468   gint handle_size;
1469   GtkRequisition size;
1470
1471   gtk_widget_get_pointer (GTK_WIDGET (xpaned), &pos.x, &pos.y);
1472
1473   if (xpaned->in_drag_vert)
1474     {
1475       pos.y -= xpaned->drag_pos.y;
1476
1477       if (is_rtl (xpaned))
1478         {
1479           gtk_widget_style_get (GTK_WIDGET (xpaned),
1480                                 "handle-size", &handle_size, NULL);
1481
1482           size.height =
1483             GTK_WIDGET (xpaned)->allocation.height - pos.y - handle_size;
1484         }
1485       else
1486         {
1487           size.height = pos.y;
1488         }
1489
1490       size.height -= GTK_CONTAINER (xpaned)->border_width;
1491
1492       size.height =
1493         CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y);
1494
1495       if (size.height != xpaned->top_left_child_size.height)
1496         gtk_xpaned_set_position_y (xpaned, size.height);
1497     }
1498
1499   if (xpaned->in_drag_horiz)
1500     {
1501       pos.x -= xpaned->drag_pos.x;
1502
1503       if (is_rtl (xpaned))
1504         {
1505           gtk_widget_style_get (GTK_WIDGET (xpaned),
1506                                 "handle-size", &handle_size, NULL);
1507
1508           size.width =
1509             GTK_WIDGET (xpaned)->allocation.width - pos.x - handle_size;
1510         }
1511       else
1512         {
1513           size.width = pos.x;
1514         }
1515
1516       size.width -= GTK_CONTAINER (xpaned)->border_width;
1517
1518       size.width =
1519         CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x);
1520
1521       if (size.width != xpaned->top_left_child_size.width)
1522         gtk_xpaned_set_position_x (xpaned, size.width);
1523     }
1524
1525   if (xpaned->in_drag_vert_and_horiz)
1526     {
1527       pos.x -= xpaned->drag_pos.x;
1528       pos.y -= xpaned->drag_pos.y;
1529
1530       if (is_rtl (xpaned))
1531         {
1532           gtk_widget_style_get (GTK_WIDGET (xpaned),
1533                                 "handle-size", &handle_size, NULL);
1534
1535           size.width =
1536             GTK_WIDGET (xpaned)->allocation.width - pos.x - handle_size;
1537           size.height =
1538             GTK_WIDGET (xpaned)->allocation.height - pos.y - handle_size;
1539         }
1540       else
1541         {
1542           size.width = pos.x;
1543           size.height = pos.y;
1544         }
1545
1546       size.width -= GTK_CONTAINER (xpaned)->border_width;
1547       size.height -= GTK_CONTAINER (xpaned)->border_width;
1548
1549       size.width =
1550         CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x);
1551       size.height =
1552         CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y);
1553
1554       if (size.width != xpaned->top_left_child_size.width)
1555         gtk_xpaned_set_position_x (xpaned, size.width);
1556
1557       if (size.height != xpaned->top_left_child_size.height)
1558         gtk_xpaned_set_position_y (xpaned, size.height);
1559     }
1560 }
1561
1562 static gboolean
1563 gtk_xpaned_enter (GtkWidget * widget, GdkEventCrossing * event)
1564 {
1565   GtkXPaned *xpaned = GTK_XPANED (widget);
1566
1567   if (xpaned->in_drag_vert ||
1568       xpaned->in_drag_horiz || xpaned->in_drag_vert_and_horiz)
1569     update_drag (xpaned);
1570   else
1571     {
1572       xpaned->handle_prelit = TRUE;
1573
1574       gtk_widget_queue_draw_area (widget,
1575                                   xpaned->handle_pos_east.x,
1576                                   xpaned->handle_pos_east.y,
1577                                   xpaned->handle_pos_east.width,
1578                                   xpaned->handle_pos_east.height);
1579
1580       gtk_widget_queue_draw_area (widget,
1581                                   xpaned->handle_pos_west.x,
1582                                   xpaned->handle_pos_west.y,
1583                                   xpaned->handle_pos_west.width,
1584                                   xpaned->handle_pos_west.height);
1585
1586       gtk_widget_queue_draw_area (widget,
1587                                   xpaned->handle_pos_north.x,
1588                                   xpaned->handle_pos_north.y,
1589                                   xpaned->handle_pos_north.width,
1590                                   xpaned->handle_pos_north.height);
1591
1592       gtk_widget_queue_draw_area (widget,
1593                                   xpaned->handle_pos_south.x,
1594                                   xpaned->handle_pos_south.y,
1595                                   xpaned->handle_pos_south.width,
1596                                   xpaned->handle_pos_south.height);
1597
1598       gtk_widget_queue_draw_area (widget,
1599                                   xpaned->handle_pos_middle.x,
1600                                   xpaned->handle_pos_middle.y,
1601                                   xpaned->handle_pos_middle.width,
1602                                   xpaned->handle_pos_middle.height);
1603     }
1604
1605   return TRUE;
1606 }
1607
1608 static gboolean
1609 gtk_xpaned_leave (GtkWidget * widget, GdkEventCrossing * event)
1610 {
1611   GtkXPaned *xpaned = GTK_XPANED (widget);
1612
1613   if (xpaned->in_drag_vert ||
1614       xpaned->in_drag_horiz || xpaned->in_drag_vert_and_horiz)
1615     update_drag (xpaned);
1616   else
1617     {
1618       xpaned->handle_prelit = FALSE;
1619
1620       gtk_widget_queue_draw_area (widget,
1621                                   xpaned->handle_pos_east.x,
1622                                   xpaned->handle_pos_east.y,
1623                                   xpaned->handle_pos_east.width,
1624                                   xpaned->handle_pos_east.height);
1625
1626       gtk_widget_queue_draw_area (widget,
1627                                   xpaned->handle_pos_west.x,
1628                                   xpaned->handle_pos_west.y,
1629                                   xpaned->handle_pos_west.width,
1630                                   xpaned->handle_pos_west.height);
1631
1632       gtk_widget_queue_draw_area (widget,
1633                                   xpaned->handle_pos_north.x,
1634                                   xpaned->handle_pos_north.y,
1635                                   xpaned->handle_pos_north.width,
1636                                   xpaned->handle_pos_north.height);
1637
1638       gtk_widget_queue_draw_area (widget,
1639                                   xpaned->handle_pos_south.x,
1640                                   xpaned->handle_pos_south.y,
1641                                   xpaned->handle_pos_south.width,
1642                                   xpaned->handle_pos_south.height);
1643
1644       gtk_widget_queue_draw_area (widget,
1645                                   xpaned->handle_pos_middle.x,
1646                                   xpaned->handle_pos_middle.y,
1647                                   xpaned->handle_pos_middle.width,
1648                                   xpaned->handle_pos_middle.height);
1649     }
1650
1651   return TRUE;
1652 }
1653
1654 static gboolean
1655 gtk_xpaned_focus (GtkWidget * widget, GtkDirectionType direction)
1656 {
1657   gboolean retval;
1658
1659   /* This is a hack, but how can this be done without
1660    * excessive cut-and-paste from gtkcontainer.c?
1661    */
1662
1663   gtk_widget_set_can_focus (GTK_WIDGET (widget), FALSE);
1664   retval = (*GTK_WIDGET_CLASS (parent_class)->focus) (widget, direction);
1665   gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE);
1666
1667   return retval;
1668 }
1669
1670 static gboolean
1671 gtk_xpaned_button_press (GtkWidget * widget, GdkEventButton * event)
1672 {
1673   GtkXPaned *xpaned = GTK_XPANED (widget);
1674
1675   /* if any child is currently maximized, jump right back */
1676   if (xpaned->maximized[GTK_XPANED_TOP_LEFT] ||
1677       xpaned->maximized[GTK_XPANED_TOP_RIGHT] ||
1678       xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] ||
1679       xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
1680     return FALSE;
1681
1682   /* if user is dragging the handles around */
1683   if (!xpaned->in_drag_vert_and_horiz &&
1684       event->window != xpaned->handle_east &&
1685       event->window != xpaned->handle_west &&
1686       event->window != xpaned->handle_north &&
1687       event->window != xpaned->handle_south &&
1688       event->window == xpaned->handle_middle && event->button == 1)
1689     {
1690       xpaned->in_drag_vert_and_horiz = TRUE;
1691
1692       /* We need a server grab here, not gtk_grab_add(), since
1693        * we don't want to pass events on to the widget's children */
1694       if (gdk_pointer_grab (xpaned->handle_middle,
1695                             FALSE,
1696                             GDK_POINTER_MOTION_HINT_MASK
1697                             | GDK_BUTTON1_MOTION_MASK
1698                             | GDK_BUTTON_RELEASE_MASK
1699                             | GDK_ENTER_NOTIFY_MASK
1700                             | GDK_LEAVE_NOTIFY_MASK,
1701                             NULL, NULL, event->time) == GDK_GRAB_SUCCESS)
1702         {
1703         }
1704
1705       xpaned->drag_pos.x = event->x;
1706       xpaned->drag_pos.y = event->y;
1707
1708       return TRUE;
1709     }
1710   else if (!xpaned->in_drag_vert &&
1711            event->window == xpaned->handle_east &&
1712            event->window != xpaned->handle_west &&
1713            event->window != xpaned->handle_north &&
1714            event->window != xpaned->handle_south &&
1715            event->window != xpaned->handle_middle && event->button == 1)
1716     {
1717       xpaned->in_drag_vert = TRUE;
1718
1719       /* We need a server grab here, not gtk_grab_add(), since
1720        * we don't want to pass events on to the widget's children */
1721       if (gdk_pointer_grab (xpaned->handle_east,
1722                             FALSE,
1723                             GDK_POINTER_MOTION_HINT_MASK
1724                             | GDK_BUTTON1_MOTION_MASK
1725                             | GDK_BUTTON_RELEASE_MASK
1726                             | GDK_ENTER_NOTIFY_MASK
1727                             | GDK_LEAVE_NOTIFY_MASK,
1728                             NULL, NULL, event->time) == GDK_GRAB_SUCCESS)
1729         {
1730         }
1731
1732       xpaned->drag_pos.y = event->y;
1733
1734       return TRUE;
1735     }
1736   else if (!xpaned->in_drag_vert &&
1737            event->window != xpaned->handle_east &&
1738            event->window == xpaned->handle_west &&
1739            event->window != xpaned->handle_north &&
1740            event->window != xpaned->handle_south &&
1741            event->window != xpaned->handle_middle && event->button == 1)
1742     {
1743       xpaned->in_drag_vert = TRUE;
1744
1745       /* We need a server grab here, not gtk_grab_add(), since
1746        * we don't want to pass events on to the widget's children */
1747       if (gdk_pointer_grab (xpaned->handle_west,
1748                             FALSE,
1749                             GDK_POINTER_MOTION_HINT_MASK
1750                             | GDK_BUTTON1_MOTION_MASK
1751                             | GDK_BUTTON_RELEASE_MASK
1752                             | GDK_ENTER_NOTIFY_MASK
1753                             | GDK_LEAVE_NOTIFY_MASK,
1754                             NULL, NULL, event->time) == GDK_GRAB_SUCCESS)
1755         {
1756         }
1757
1758       xpaned->drag_pos.y = event->y;
1759
1760       return TRUE;
1761     }
1762   else if (!xpaned->in_drag_horiz &&
1763            event->window != xpaned->handle_east &&
1764            event->window != xpaned->handle_west &&
1765            event->window == xpaned->handle_north &&
1766            event->window != xpaned->handle_south &&
1767            event->window != xpaned->handle_middle && event->button == 1)
1768     {
1769       xpaned->in_drag_horiz = TRUE;
1770
1771       /* We need a server grab here, not gtk_grab_add(), since
1772        * we don't want to pass events on to the widget's children */
1773       if (gdk_pointer_grab (xpaned->handle_north,
1774                             FALSE,
1775                             GDK_POINTER_MOTION_HINT_MASK
1776                             | GDK_BUTTON1_MOTION_MASK
1777                             | GDK_BUTTON_RELEASE_MASK
1778                             | GDK_ENTER_NOTIFY_MASK
1779                             | GDK_LEAVE_NOTIFY_MASK,
1780                             NULL, NULL, event->time) == GDK_GRAB_SUCCESS)
1781         {
1782         }
1783
1784       xpaned->drag_pos.x = event->x;
1785
1786       return TRUE;
1787     }
1788   else if (!xpaned->in_drag_horiz &&
1789            event->window != xpaned->handle_east &&
1790            event->window != xpaned->handle_west &&
1791            event->window != xpaned->handle_north &&
1792            event->window == xpaned->handle_south &&
1793            event->window != xpaned->handle_middle && event->button == 1)
1794     {
1795       xpaned->in_drag_horiz = TRUE;
1796
1797       /* We need a server grab here, not gtk_grab_add(), since
1798        * we don't want to pass events on to the widget's children */
1799       if (gdk_pointer_grab (xpaned->handle_south,
1800                             FALSE,
1801                             GDK_POINTER_MOTION_HINT_MASK
1802                             | GDK_BUTTON1_MOTION_MASK
1803                             | GDK_BUTTON_RELEASE_MASK
1804                             | GDK_ENTER_NOTIFY_MASK
1805                             | GDK_LEAVE_NOTIFY_MASK,
1806                             NULL, NULL, event->time) == GDK_GRAB_SUCCESS)
1807         {
1808         }
1809
1810       xpaned->drag_pos.x = event->x;
1811
1812       return TRUE;
1813     }
1814   return FALSE;
1815 }
1816
1817 static gboolean
1818 gtk_xpaned_button_release (GtkWidget * widget, GdkEventButton * event)
1819 {
1820   GtkXPaned *xpaned = GTK_XPANED (widget);
1821
1822   if (xpaned->in_drag_vert && (event->button == 1))
1823     {
1824       xpaned->in_drag_vert = FALSE;
1825       xpaned->drag_pos.y = -1;
1826       xpaned->position_set = TRUE;
1827       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
1828                                   event->time);
1829       return TRUE;
1830     }
1831   else if (xpaned->in_drag_horiz && (event->button == 1))
1832     {
1833       xpaned->in_drag_horiz = FALSE;
1834       xpaned->drag_pos.x = -1;
1835       xpaned->position_set = TRUE;
1836       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
1837                                   event->time);
1838       return TRUE;
1839     }
1840   else if (xpaned->in_drag_vert_and_horiz && (event->button == 1))
1841     {
1842       xpaned->in_drag_vert_and_horiz = FALSE;
1843       xpaned->drag_pos.x = -1;
1844       xpaned->drag_pos.y = -1;
1845       xpaned->position_set = TRUE;
1846       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
1847                                   event->time);
1848       return TRUE;
1849     }
1850
1851   return FALSE;
1852 }
1853
1854 static gboolean
1855 gtk_xpaned_motion (GtkWidget * widget, GdkEventMotion * event)
1856 {
1857   GtkXPaned *xpaned = GTK_XPANED (widget);
1858
1859   if (xpaned->in_drag_vert ||
1860       xpaned->in_drag_horiz || xpaned->in_drag_vert_and_horiz)
1861
1862     {
1863       update_drag (xpaned);
1864       return TRUE;
1865     }
1866
1867   return FALSE;
1868 }
1869
1870 void
1871 gtk_xpaned_add_top_left (GtkXPaned * xpaned, GtkWidget * widget)
1872 {
1873   gtk_xpaned_pack_top_left (xpaned, widget, FALSE, TRUE);
1874 }
1875
1876 void
1877 gtk_xpaned_add_top_right (GtkXPaned * xpaned, GtkWidget * widget)
1878 {
1879   gtk_xpaned_pack_top_right (xpaned, widget, FALSE, TRUE);
1880 }
1881
1882 void
1883 gtk_xpaned_add_bottom_left (GtkXPaned * xpaned, GtkWidget * widget)
1884 {
1885   gtk_xpaned_pack_bottom_left (xpaned, widget, FALSE, TRUE);
1886 }
1887
1888 void
1889 gtk_xpaned_add_bottom_right (GtkXPaned * xpaned, GtkWidget * widget)
1890 {
1891   gtk_xpaned_pack_bottom_right (xpaned, widget, FALSE, TRUE);
1892 }
1893
1894 void
1895 gtk_xpaned_pack_top_left (GtkXPaned * xpaned,
1896                           GtkWidget * child, gboolean resize, gboolean shrink)
1897 {
1898   g_return_if_fail (GTK_IS_XPANED (xpaned));
1899   g_return_if_fail (GTK_IS_WIDGET (child));
1900
1901   if (!xpaned->top_left_child)
1902     {
1903       xpaned->top_left_child = child;
1904       xpaned->top_left_child_resize = resize;
1905       xpaned->top_left_child_shrink = shrink;
1906
1907       gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
1908     }
1909 }
1910
1911 void
1912 gtk_xpaned_pack_top_right (GtkXPaned * xpaned,
1913                            GtkWidget * child,
1914                            gboolean resize, gboolean shrink)
1915 {
1916   g_return_if_fail (GTK_IS_XPANED (xpaned));
1917   g_return_if_fail (GTK_IS_WIDGET (child));
1918
1919   if (!xpaned->top_right_child)
1920     {
1921       xpaned->top_right_child = child;
1922       xpaned->top_right_child_resize = resize;
1923       xpaned->top_right_child_shrink = shrink;
1924
1925       gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
1926     }
1927 }
1928
1929 void
1930 gtk_xpaned_pack_bottom_left (GtkXPaned * xpaned,
1931                              GtkWidget * child,
1932                              gboolean resize, gboolean shrink)
1933 {
1934   g_return_if_fail (GTK_IS_XPANED (xpaned));
1935   g_return_if_fail (GTK_IS_WIDGET (child));
1936
1937   if (!xpaned->bottom_left_child)
1938     {
1939       xpaned->bottom_left_child = child;
1940       xpaned->bottom_left_child_resize = resize;
1941       xpaned->bottom_left_child_shrink = shrink;
1942
1943       gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
1944     }
1945 }
1946
1947 void
1948 gtk_xpaned_pack_bottom_right (GtkXPaned * xpaned,
1949                               GtkWidget * child,
1950                               gboolean resize, gboolean shrink)
1951 {
1952   g_return_if_fail (GTK_IS_XPANED (xpaned));
1953   g_return_if_fail (GTK_IS_WIDGET (child));
1954
1955   if (!xpaned->bottom_right_child)
1956     {
1957       xpaned->bottom_right_child = child;
1958       xpaned->bottom_right_child_resize = resize;
1959       xpaned->bottom_right_child_shrink = shrink;
1960
1961       gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
1962     }
1963 }
1964
1965 static void
1966 gtk_xpaned_add (GtkContainer * container, GtkWidget * widget)
1967 {
1968   GtkXPaned *xpaned;
1969
1970   g_return_if_fail (GTK_IS_XPANED (container));
1971
1972   xpaned = GTK_XPANED (container);
1973
1974   if (!xpaned->top_left_child)
1975     gtk_xpaned_add_top_left (xpaned, widget);
1976   else if (!xpaned->top_right_child)
1977     gtk_xpaned_add_top_right (xpaned, widget);
1978   else if (!xpaned->bottom_left_child)
1979     gtk_xpaned_add_bottom_left (xpaned, widget);
1980   else if (!xpaned->bottom_right_child)
1981     gtk_xpaned_add_bottom_right (xpaned, widget);
1982   else
1983     g_warning ("GtkXPaned cannot have more than 4 children\n");
1984 }
1985
1986 static void
1987 gtk_xpaned_remove (GtkContainer * container, GtkWidget * widget)
1988 {
1989   GtkXPaned *xpaned;
1990   gboolean was_visible;
1991
1992   xpaned = GTK_XPANED (container);
1993   was_visible = gtk_widget_get_visible (widget);
1994
1995   if (xpaned->top_left_child == widget)
1996     {
1997       gtk_widget_unparent (widget);
1998
1999       xpaned->top_left_child = NULL;
2000
2001       if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
2002         gtk_widget_queue_resize (GTK_WIDGET (container));
2003     }
2004   else if (xpaned->top_right_child == widget)
2005     {
2006       gtk_widget_unparent (widget);
2007
2008       xpaned->top_right_child = NULL;
2009
2010       if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
2011         gtk_widget_queue_resize (GTK_WIDGET (container));
2012     }
2013   else if (xpaned->bottom_left_child == widget)
2014     {
2015       gtk_widget_unparent (widget);
2016
2017       xpaned->bottom_left_child = NULL;
2018
2019       if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
2020         gtk_widget_queue_resize (GTK_WIDGET (container));
2021     }
2022   else if (xpaned->bottom_right_child == widget)
2023     {
2024       gtk_widget_unparent (widget);
2025
2026       xpaned->bottom_right_child = NULL;
2027
2028       if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
2029         gtk_widget_queue_resize (GTK_WIDGET (container));
2030     }
2031   else
2032     g_warning ("GtkXPaned has no more children attached\n");
2033
2034 }
2035
2036 static void
2037 gtk_xpaned_forall (GtkContainer * container,
2038                    gboolean include_internals,
2039                    GtkCallback callback, gpointer callback_data)
2040 {
2041   GtkXPaned *xpaned;
2042
2043   g_return_if_fail (callback != NULL);
2044
2045   xpaned = GTK_XPANED (container);
2046
2047   if (xpaned->top_left_child)
2048     (*callback) (xpaned->top_left_child, callback_data);
2049   if (xpaned->top_right_child)
2050     (*callback) (xpaned->top_right_child, callback_data);
2051   if (xpaned->bottom_left_child)
2052     (*callback) (xpaned->bottom_left_child, callback_data);
2053   if (xpaned->bottom_right_child)
2054     (*callback) (xpaned->bottom_right_child, callback_data);
2055 }
2056
2057 /**
2058  * gtk_xpaned_get_position_x:
2059  * @paned: a #GtkXPaned widget
2060  * 
2061  * Obtains the x-position of the divider.
2062  * 
2063  * Return value: x-position of the divider
2064  **/
2065 gint
2066 gtk_xpaned_get_position_x (GtkXPaned * xpaned)
2067 {
2068   g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0);
2069
2070   return xpaned->top_left_child_size.width;
2071 }
2072
2073 /**
2074  * gtk_xpaned_get_position_y:
2075  * @paned: a #GtkXPaned widget
2076  * 
2077  * Obtains the y-position of the divider.
2078  * 
2079  * Return value: y-position of the divider
2080  **/
2081 gint
2082 gtk_xpaned_get_position_y (GtkXPaned * xpaned)
2083 {
2084   g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0);
2085
2086   return xpaned->top_left_child_size.height;
2087 }
2088
2089 /**
2090  * gtk_xpaned_set_position_x:
2091  * @paned: a #GtkXPaned widget
2092  * @xposition: pixel x-position of divider, a negative values
2093  *                         of a component mean that the position is unset.
2094  * 
2095  * Sets the x-position of the divider between the four panes.
2096  **/
2097 void
2098 gtk_xpaned_set_position_x (GtkXPaned * xpaned, gint xposition)
2099 {
2100   GObject *object;
2101
2102   g_return_if_fail (GTK_IS_XPANED (xpaned));
2103
2104   /* if any child is currently maximized, jump right back */
2105   if (xpaned->maximized[GTK_XPANED_TOP_LEFT] ||
2106       xpaned->maximized[GTK_XPANED_TOP_RIGHT] ||
2107       xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] ||
2108       xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2109     return;
2110
2111   object = G_OBJECT (xpaned);
2112
2113   if (xposition >= 0)
2114     {
2115       /* We don't clamp here - the assumption is that
2116        * if the total allocation changes at the same time
2117        * as the position, the position set is with reference
2118        * to the new total size. If only the position changes,
2119        * then clamping will occur in gtk_paned_compute_position()
2120        */
2121
2122       xpaned->top_left_child_size.width = xposition;
2123       xpaned->position_set = TRUE;
2124     }
2125   else
2126     {
2127       xpaned->position_set = FALSE;
2128     }
2129
2130   g_object_freeze_notify (object);
2131   g_object_notify (object, "x-position");
2132   g_object_notify (object, "position-set");
2133   g_object_thaw_notify (object);
2134
2135   gtk_widget_queue_resize (GTK_WIDGET (xpaned));
2136 }
2137
2138 /**
2139  * gtk_xpaned_set_position_y:
2140  * @paned: a #GtkXPaned widget
2141  * @yposition: pixel y-position of divider, a negative values
2142  *                         of a component mean that the position is unset.
2143  * 
2144  * Sets the y-position of the divider between the four panes.
2145  **/
2146 void
2147 gtk_xpaned_set_position_y (GtkXPaned * xpaned, gint yposition)
2148 {
2149   GObject *object;
2150
2151   g_return_if_fail (GTK_IS_XPANED (xpaned));
2152
2153   /* if any child is currently maximized, jump right back */
2154   if (xpaned->maximized[GTK_XPANED_TOP_LEFT] ||
2155       xpaned->maximized[GTK_XPANED_TOP_RIGHT] ||
2156       xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] ||
2157       xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2158     return;
2159
2160   object = G_OBJECT (xpaned);
2161
2162   if (yposition >= 0)
2163     {
2164       /* We don't clamp here - the assumption is that
2165        * if the total allocation changes at the same time
2166        * as the position, the position set is with reference
2167        * to the new total size. If only the position changes,
2168        * then clamping will occur in gtk_paned_compute_position()
2169        */
2170
2171       xpaned->top_left_child_size.height = yposition;
2172       xpaned->position_set = TRUE;
2173     }
2174   else
2175     {
2176       xpaned->position_set = FALSE;
2177     }
2178
2179   g_object_freeze_notify (object);
2180   g_object_notify (object, "y-position");
2181   g_object_notify (object, "position-set");
2182   g_object_thaw_notify (object);
2183
2184   gtk_widget_queue_resize (GTK_WIDGET (xpaned));
2185 }
2186
2187 /* this call is private and only intended for internal use! */
2188 void
2189 gtk_xpaned_save_unmaximized_x (GtkXPaned * xpaned)
2190 {
2191   xpaned->unmaximized_position.x = gtk_xpaned_get_position_x (xpaned);
2192 }
2193
2194 /* this call is private and only intended for internal use! */
2195 void
2196 gtk_xpaned_save_unmaximized_y (GtkXPaned * xpaned)
2197 {
2198   xpaned->unmaximized_position.y = gtk_xpaned_get_position_y (xpaned);
2199 }
2200
2201 /* this call is private and only intended for internal use! */
2202 gint
2203 gtk_xpaned_fetch_unmaximized_x (GtkXPaned * xpaned)
2204 {
2205   return xpaned->unmaximized_position.x;
2206 }
2207
2208 /* this call is private and only intended for internal use! */
2209 gint
2210 gtk_xpaned_fetch_unmaximized_y (GtkXPaned * xpaned)
2211 {
2212   return xpaned->unmaximized_position.y;
2213 }
2214
2215 /**
2216  * gtk_xpaned_get_top_left_child:
2217  * @xpaned: a #GtkXPaned widget
2218  * 
2219  * Obtains the top-left child of the xpaned widget.
2220  * 
2221  * Return value: top-left child, or %NULL if it is not set.
2222  *
2223  * Since: 2.4
2224  **/
2225 GtkWidget *
2226 gtk_xpaned_get_top_left_child (GtkXPaned * xpaned)
2227 {
2228   g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
2229
2230   return xpaned->top_left_child;
2231 }
2232
2233 /**
2234  * gtk_xpaned_get_top_right_child:
2235  * @xpaned: a #GtkXPaned widget
2236  * 
2237  * Obtains the top-right child of the xpaned widget.
2238  * 
2239  * Return value: top-right child, or %NULL if it is not set.
2240  *
2241  * Since: 2.4
2242  **/
2243 GtkWidget *
2244 gtk_xpaned_get_top_right_child (GtkXPaned * xpaned)
2245 {
2246   g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
2247
2248   return xpaned->top_right_child;
2249 }
2250
2251 /**
2252  * gtk_xpaned_get_bottom_left_child:
2253  * @xpaned: a #GtkXPaned widget
2254  * 
2255  * Obtains the bottom-left child of the xpaned widget.
2256  * 
2257  * Return value: bottom-left child, or %NULL if it is not set.
2258  *
2259  * Since: 2.4
2260  **/
2261 GtkWidget *
2262 gtk_xpaned_get_bottom_left_child (GtkXPaned * xpaned)
2263 {
2264   g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
2265
2266   return xpaned->bottom_left_child;
2267 }
2268
2269 /**
2270  * gtk_xpaned_get_bottom_right_child:
2271  * @xpaned: a #GtkXPaned widget
2272  * 
2273  * Obtains the bottom-right child of the xpaned widget.
2274  * 
2275  * Return value: bottom-right child, or %NULL if it is not set.
2276  *
2277  * Since: 2.4
2278  **/
2279 GtkWidget *
2280 gtk_xpaned_get_bottom_right_child (GtkXPaned * xpaned)
2281 {
2282   g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
2283
2284   return xpaned->bottom_right_child;
2285 }
2286
2287 gboolean
2288 gtk_xpaned_maximize_top_left (GtkXPaned * xpaned, gboolean maximize)
2289 {
2290   if (maximize)
2291     {
2292       /* see if any child is already maximized */
2293       if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
2294           !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
2295           !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
2296           !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2297         {
2298           /* save current position */
2299           gtk_xpaned_save_unmaximized_x (xpaned);
2300           gtk_xpaned_save_unmaximized_y (xpaned);
2301
2302           /* set new maximized position */
2303           gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x);
2304           gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y);
2305
2306           /* mark maximized flag for top-left child */
2307           xpaned->maximized[GTK_XPANED_TOP_LEFT] = TRUE;
2308
2309           return TRUE;
2310         }
2311       /* already one child maximized, report error */
2312       else
2313         return FALSE;
2314     }
2315   else
2316     {
2317       /* verify that top-left child is really currently maximized */
2318       if (xpaned->maximized[GTK_XPANED_TOP_LEFT])
2319         {
2320           /* clear maximized flat for top-left child */
2321           xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE;
2322
2323           /* restore unmaximized position */
2324           gtk_xpaned_set_position_x (xpaned,
2325                                      gtk_xpaned_fetch_unmaximized_x (xpaned));
2326           gtk_xpaned_set_position_y (xpaned,
2327                                      gtk_xpaned_fetch_unmaximized_y (xpaned));
2328
2329           return TRUE;
2330         }
2331       /* top-left child is currently not maximized, report error */
2332       else
2333         return FALSE;
2334     }
2335 }
2336
2337 gboolean
2338 gtk_xpaned_maximize_top_right (GtkXPaned * xpaned, gboolean maximize)
2339 {
2340   if (maximize)
2341     {
2342       /* see if any child is already maximized */
2343       if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
2344           !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
2345           !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
2346           !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2347         {
2348           /* save current position */
2349           gtk_xpaned_save_unmaximized_x (xpaned);
2350           gtk_xpaned_save_unmaximized_y (xpaned);
2351
2352           /* set new maximized position */
2353           gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x);
2354           gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y);
2355
2356           /* mark maximized flag for top-right child */
2357           xpaned->maximized[GTK_XPANED_TOP_RIGHT] = TRUE;
2358
2359           return TRUE;
2360         }
2361       /* already one child maximized, report error */
2362       else
2363         return FALSE;
2364     }
2365   else
2366     {
2367       /* verify that top-right child is really currently maximized */
2368       if (xpaned->maximized[GTK_XPANED_TOP_RIGHT])
2369         {
2370           /* clear maximized flat for top-right child */
2371           xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE;
2372
2373           /* restore unmaximized position */
2374           gtk_xpaned_set_position_x (xpaned,
2375                                      gtk_xpaned_fetch_unmaximized_x (xpaned));
2376           gtk_xpaned_set_position_y (xpaned,
2377                                      gtk_xpaned_fetch_unmaximized_y (xpaned));
2378
2379           return TRUE;
2380         }
2381       /* top-right child is currently not maximized, report error */
2382       else
2383         return FALSE;
2384     }
2385 }
2386
2387 gboolean
2388 gtk_xpaned_maximize_bottom_left (GtkXPaned * xpaned, gboolean maximize)
2389 {
2390   if (maximize)
2391     {
2392       /* see if any child is already maximized */
2393       if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
2394           !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
2395           !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
2396           !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2397         {
2398           /* save current position */
2399           gtk_xpaned_save_unmaximized_x (xpaned);
2400           gtk_xpaned_save_unmaximized_y (xpaned);
2401
2402           /* set new maximized position */
2403           gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x);
2404           gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y);
2405
2406           /* mark maximized flag for bottom-left child */
2407           xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = TRUE;
2408
2409           return TRUE;
2410         }
2411       /* already one child maximized, report error */
2412       else
2413         return FALSE;
2414     }
2415   else
2416     {
2417       /* verify that bottom-left child is really currently maximized */
2418       if (xpaned->maximized[GTK_XPANED_BOTTOM_LEFT])
2419         {
2420           /* clear maximized flat for bottom-left child */
2421           xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE;
2422
2423           /* restore unmaximized position */
2424           gtk_xpaned_set_position_x (xpaned,
2425                                      gtk_xpaned_fetch_unmaximized_x (xpaned));
2426           gtk_xpaned_set_position_y (xpaned,
2427                                      gtk_xpaned_fetch_unmaximized_y (xpaned));
2428
2429           return TRUE;
2430         }
2431       /* bottom-left child is currently not maximized, report error */
2432       else
2433         return FALSE;
2434     }
2435 }
2436
2437 gboolean
2438 gtk_xpaned_maximize_bottom_right (GtkXPaned * xpaned, gboolean maximize)
2439 {
2440   if (maximize)
2441     {
2442       /* see if any child is already maximized */
2443       if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
2444           !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
2445           !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
2446           !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2447         {
2448           /* save current position */
2449           gtk_xpaned_save_unmaximized_x (xpaned);
2450           gtk_xpaned_save_unmaximized_y (xpaned);
2451
2452           /* set new maximized position */
2453           gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x);
2454           gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y);
2455
2456           /* mark maximized flag for bottom-right child */
2457           xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = TRUE;
2458
2459           return TRUE;
2460         }
2461       /* already one child maximized, report error */
2462       else
2463         return FALSE;
2464     }
2465   else
2466     {
2467       /* verify that bottom-right child is really currently maximized */
2468       if (xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2469         {
2470           /* clear maximized flat for bottom-right child */
2471           xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE;
2472
2473           /* restore unmaximized position */
2474           gtk_xpaned_set_position_x (xpaned,
2475                                      gtk_xpaned_fetch_unmaximized_x (xpaned));
2476           gtk_xpaned_set_position_y (xpaned,
2477                                      gtk_xpaned_fetch_unmaximized_y (xpaned));
2478
2479           return TRUE;
2480         }
2481       /* bottom-right child is currently not maximized, report error */
2482       else
2483         return FALSE;
2484     }
2485 }
2486
2487 void
2488 gtk_xpaned_compute_position (GtkXPaned * xpaned,
2489                              const GtkAllocation * allocation,
2490                              GtkRequisition * top_left_child_req,
2491                              GtkRequisition * top_right_child_req,
2492                              GtkRequisition * bottom_left_child_req,
2493                              GtkRequisition * bottom_right_child_req)
2494 {
2495   GdkPoint old_position;
2496   GdkPoint old_min_position;
2497   GdkPoint old_max_position;
2498   gint handle_size;
2499   gint border_width = GTK_CONTAINER (xpaned)->border_width;
2500
2501   g_return_if_fail (GTK_IS_XPANED (xpaned));
2502
2503   old_position.x = xpaned->top_left_child_size.width;
2504   old_position.y = xpaned->top_left_child_size.height;
2505   old_min_position.x = xpaned->min_position.x;
2506   old_min_position.y = xpaned->min_position.y;
2507   old_max_position.x = xpaned->max_position.x;
2508   old_max_position.y = xpaned->max_position.y;
2509
2510   xpaned->min_position.x =
2511     xpaned->top_left_child_shrink ? 0 : top_left_child_req->width;
2512   xpaned->min_position.y =
2513     xpaned->top_left_child_shrink ? 0 : top_left_child_req->height;
2514
2515   gtk_widget_style_get (GTK_WIDGET (xpaned), "handle-size", &handle_size,
2516                         NULL);
2517
2518   xpaned->max_position.x = allocation->width - 2 * border_width - handle_size;
2519   xpaned->max_position.y =
2520     allocation->height - 2 * border_width - handle_size;
2521   if (!xpaned->top_left_child_shrink)
2522     xpaned->max_position.x =
2523       MAX (1, xpaned->max_position.x - top_left_child_req->width);
2524   xpaned->max_position.x =
2525     MAX (xpaned->min_position.x, xpaned->max_position.x);
2526
2527   if (!xpaned->position_set)
2528     {
2529       if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize)
2530         {
2531           xpaned->top_left_child_size.width =
2532             MAX (0, allocation->width - top_right_child_req->width);
2533           xpaned->top_left_child_size.height =
2534             MAX (0, allocation->height - top_right_child_req->height);
2535         }
2536       else if (!xpaned->top_left_child_resize
2537                && xpaned->top_right_child_resize)
2538         {
2539           xpaned->top_left_child_size.width = top_left_child_req->width;
2540           xpaned->top_left_child_size.height = top_left_child_req->height;
2541         }
2542       else
2543         {
2544           xpaned->top_left_child_size.width = allocation->width * 0.5 + 0.5;
2545           xpaned->top_left_child_size.height = allocation->height * 0.5 + 0.5;
2546         }
2547     }
2548   else
2549     {
2550       /* If the position was set before the initial allocation.
2551       ** (paned->last_allocation <= 0) just clamp it and leave it. */
2552       if (xpaned->last_allocation.width > 0)
2553         {
2554           if (xpaned->top_left_child_resize
2555               && !xpaned->top_right_child_resize)
2556             {
2557               xpaned->top_left_child_size.width += allocation->width
2558                 - xpaned->last_allocation.width;
2559
2560               xpaned->top_left_child_size.height += allocation->height
2561                 - xpaned->last_allocation.height;
2562             }
2563           else
2564             if (!
2565                 (!xpaned->top_left_child_resize
2566                  && xpaned->top_right_child_resize))
2567               {
2568                 xpaned->top_left_child_size.width = allocation->width
2569                   * ((gdouble) xpaned->top_left_child_size.width /
2570                      (xpaned->last_allocation.width)) + 0.5;
2571
2572                 xpaned->top_left_child_size.height = allocation->height
2573                   * ((gdouble) xpaned->top_left_child_size.height /
2574                      (xpaned->last_allocation.height)) + 0.5;
2575               }
2576         }
2577       if (xpaned->last_allocation.height > 0)
2578         {
2579           if (xpaned->top_left_child_resize
2580               && !xpaned->top_right_child_resize)
2581             {
2582               xpaned->top_left_child_size.width +=
2583                 allocation->width - xpaned->last_allocation.width;
2584               xpaned->top_left_child_size.height +=
2585                 allocation->height - xpaned->last_allocation.height;
2586             }
2587           else
2588             if (!
2589                 (!xpaned->top_left_child_resize
2590                  && xpaned->top_right_child_resize))
2591               {
2592                 xpaned->top_left_child_size.width =
2593                   allocation->width *
2594                   ((gdouble) xpaned->top_left_child_size.width /
2595                    (xpaned->last_allocation.width)) + 0.5;
2596                 xpaned->top_left_child_size.height =
2597                   allocation->height *
2598                   ((gdouble) xpaned->top_left_child_size.height /
2599                    (xpaned->last_allocation.height)) + 0.5;
2600               }
2601         }
2602
2603     }
2604
2605   xpaned->top_left_child_size.width =
2606     CLAMP (xpaned->top_left_child_size.width, xpaned->min_position.x,
2607            xpaned->max_position.x);
2608   xpaned->top_left_child_size.height =
2609     CLAMP (xpaned->top_left_child_size.height, xpaned->min_position.y,
2610            xpaned->max_position.y);
2611
2612   xpaned->top_right_child_size.width =
2613     CLAMP (xpaned->top_right_child_size.width, xpaned->min_position.x,
2614            xpaned->max_position.x);
2615   xpaned->top_right_child_size.height =
2616     CLAMP (xpaned->top_right_child_size.height, xpaned->min_position.y,
2617            xpaned->max_position.y);
2618
2619   xpaned->bottom_left_child_size.width =
2620     CLAMP (xpaned->bottom_left_child_size.width, xpaned->min_position.x,
2621            xpaned->max_position.x);
2622   xpaned->bottom_left_child_size.height =
2623     CLAMP (xpaned->bottom_left_child_size.height, xpaned->min_position.y,
2624            xpaned->max_position.y);
2625
2626   xpaned->bottom_right_child_size.width =
2627     CLAMP (xpaned->bottom_right_child_size.width, xpaned->min_position.x,
2628            xpaned->max_position.x);
2629   xpaned->bottom_right_child_size.height =
2630     CLAMP (xpaned->bottom_right_child_size.height, xpaned->min_position.y,
2631            xpaned->max_position.y);
2632
2633   gtk_widget_set_child_visible (xpaned->top_left_child, TRUE);
2634   gtk_widget_set_child_visible (xpaned->top_right_child, TRUE);
2635   gtk_widget_set_child_visible (xpaned->bottom_left_child, TRUE);
2636   gtk_widget_set_child_visible (xpaned->bottom_right_child, TRUE);
2637
2638   g_object_freeze_notify (G_OBJECT (xpaned));
2639
2640   if (xpaned->top_left_child_size.width != old_position.x)
2641     g_object_notify (G_OBJECT (xpaned), "x-position");
2642   if (xpaned->top_left_child_size.height != old_position.y)
2643     g_object_notify (G_OBJECT (xpaned), "y-position");
2644
2645   if (xpaned->top_right_child_size.width != old_position.x)
2646     g_object_notify (G_OBJECT (xpaned), "x-position");
2647   if (xpaned->top_right_child_size.height != old_position.y)
2648     g_object_notify (G_OBJECT (xpaned), "y-position");
2649
2650   if (xpaned->bottom_left_child_size.width != old_position.x)
2651     g_object_notify (G_OBJECT (xpaned), "x-position");
2652   if (xpaned->bottom_left_child_size.height != old_position.y)
2653     g_object_notify (G_OBJECT (xpaned), "y-position");
2654
2655   if (xpaned->bottom_right_child_size.width != old_position.x)
2656     g_object_notify (G_OBJECT (xpaned), "x-position");
2657   if (xpaned->bottom_right_child_size.height != old_position.y)
2658     g_object_notify (G_OBJECT (xpaned), "y-position");
2659
2660   if (xpaned->min_position.x != old_min_position.x)
2661     g_object_notify (G_OBJECT (xpaned), "min-x-position");
2662   if (xpaned->min_position.y != old_min_position.y)
2663     g_object_notify (G_OBJECT (xpaned), "min-y-position");
2664
2665   if (xpaned->max_position.x != old_max_position.x)
2666     g_object_notify (G_OBJECT (xpaned), "max-y-position");
2667   if (xpaned->max_position.y != old_max_position.y)
2668     g_object_notify (G_OBJECT (xpaned), "max-y-position");
2669
2670   g_object_thaw_notify (G_OBJECT (xpaned));
2671
2672   xpaned->last_allocation.width = allocation->width;
2673   xpaned->last_allocation.height = allocation->height;
2674 }
2675
2676 static void
2677 gtk_xpaned_set_saved_focus (GtkXPaned * xpaned, GtkWidget * widget)
2678 {
2679   if (xpaned->priv->saved_focus)
2680     g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->saved_focus),
2681                                   (gpointer *) & (xpaned->priv->saved_focus));
2682
2683   xpaned->priv->saved_focus = widget;
2684
2685   if (xpaned->priv->saved_focus)
2686     g_object_add_weak_pointer (G_OBJECT (xpaned->priv->saved_focus),
2687                                (gpointer *) & (xpaned->priv->saved_focus));
2688 }
2689
2690 static void
2691 gtk_xpaned_set_first_xpaned (GtkXPaned * xpaned, GtkXPaned * first_xpaned)
2692 {
2693   if (xpaned->priv->first_xpaned)
2694     g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned),
2695                                   (gpointer *) & (xpaned->priv->
2696                                                   first_xpaned));
2697
2698   xpaned->priv->first_xpaned = first_xpaned;
2699
2700   if (xpaned->priv->first_xpaned)
2701     g_object_add_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned),
2702                                (gpointer *) & (xpaned->priv->first_xpaned));
2703 }
2704
2705 static void
2706 gtk_xpaned_set_last_top_left_child_focus (GtkXPaned * xpaned,
2707                                           GtkWidget * widget)
2708 {
2709   if (xpaned->last_top_left_child_focus)
2710     g_object_remove_weak_pointer (G_OBJECT
2711                                   (xpaned->last_top_left_child_focus),
2712                                   (gpointer *) & (xpaned->
2713                                                   last_top_left_child_focus));
2714
2715   xpaned->last_top_left_child_focus = widget;
2716
2717   if (xpaned->last_top_left_child_focus)
2718     g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_left_child_focus),
2719                                (gpointer *) & (xpaned->
2720                                                last_top_left_child_focus));
2721 }
2722
2723 static void
2724 gtk_xpaned_set_last_top_right_child_focus (GtkXPaned * xpaned,
2725                                            GtkWidget * widget)
2726 {
2727   if (xpaned->last_top_right_child_focus)
2728     g_object_remove_weak_pointer (G_OBJECT
2729                                   (xpaned->last_top_right_child_focus),
2730                                   (gpointer *) & (xpaned->
2731                                                   last_top_right_child_focus));
2732
2733   xpaned->last_top_right_child_focus = widget;
2734
2735   if (xpaned->last_top_right_child_focus)
2736     g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_right_child_focus),
2737                                (gpointer *) & (xpaned->
2738                                                last_top_right_child_focus));
2739 }
2740
2741 static void
2742 gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned * xpaned,
2743                                              GtkWidget * widget)
2744 {
2745   if (xpaned->last_bottom_left_child_focus)
2746     g_object_remove_weak_pointer (G_OBJECT
2747                                   (xpaned->last_bottom_left_child_focus),
2748                                   (gpointer *) & (xpaned->
2749                                                   last_bottom_left_child_focus));
2750
2751   xpaned->last_bottom_left_child_focus = widget;
2752
2753   if (xpaned->last_bottom_left_child_focus)
2754     g_object_add_weak_pointer (G_OBJECT
2755                                (xpaned->last_bottom_left_child_focus),
2756                                (gpointer *) & (xpaned->
2757                                                last_bottom_left_child_focus));
2758 }
2759
2760 static void
2761 gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned * xpaned,
2762                                               GtkWidget * widget)
2763 {
2764   if (xpaned->last_bottom_right_child_focus)
2765     g_object_remove_weak_pointer (G_OBJECT
2766                                   (xpaned->last_bottom_right_child_focus),
2767                                   (gpointer *) & (xpaned->
2768                                                   last_bottom_right_child_focus));
2769
2770   xpaned->last_bottom_right_child_focus = widget;
2771
2772   if (xpaned->last_bottom_right_child_focus)
2773     g_object_add_weak_pointer (G_OBJECT
2774                                (xpaned->last_bottom_right_child_focus),
2775                                (gpointer *) & (xpaned->
2776                                                last_bottom_right_child_focus));
2777 }
2778
2779 static GtkWidget *
2780 xpaned_get_focus_widget (GtkXPaned * xpaned)
2781 {
2782   GtkWidget *toplevel;
2783
2784   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned));
2785   if (gtk_widget_is_toplevel (toplevel))
2786     return GTK_WINDOW (toplevel)->focus_widget;
2787
2788   return NULL;
2789 }
2790
2791 static void
2792 gtk_xpaned_set_focus_child (GtkContainer * container, GtkWidget * focus_child)
2793 {
2794   GtkXPaned *xpaned;
2795
2796   g_return_if_fail (GTK_IS_XPANED (container));
2797
2798   xpaned = GTK_XPANED (container);
2799
2800   if (focus_child == NULL)
2801     {
2802       GtkWidget *last_focus;
2803       GtkWidget *w;
2804
2805       last_focus = xpaned_get_focus_widget (xpaned);
2806
2807       if (last_focus)
2808         {
2809           /* If there is one or more paned widgets between us and the
2810            * focus widget, we want the topmost of those as last_focus
2811            */
2812           for (w = last_focus; w != GTK_WIDGET (xpaned); w = w->parent)
2813             if (GTK_IS_XPANED (w))
2814               last_focus = w;
2815
2816           if (container->focus_child == xpaned->top_left_child)
2817             gtk_xpaned_set_last_top_left_child_focus (xpaned, last_focus);
2818           else if (container->focus_child == xpaned->top_right_child)
2819             gtk_xpaned_set_last_top_right_child_focus (xpaned, last_focus);
2820           else if (container->focus_child == xpaned->bottom_left_child)
2821             gtk_xpaned_set_last_bottom_left_child_focus (xpaned, last_focus);
2822           else if (container->focus_child == xpaned->bottom_right_child)
2823             gtk_xpaned_set_last_bottom_right_child_focus (xpaned, last_focus);
2824         }
2825     }
2826
2827   if (parent_class->set_focus_child)
2828     (*parent_class->set_focus_child) (container, focus_child);
2829 }
2830
2831 static void
2832 gtk_xpaned_get_cycle_chain (GtkXPaned * xpaned,
2833                             GtkDirectionType direction, GList ** widgets)
2834 {
2835   GtkContainer *container = GTK_CONTAINER (xpaned);
2836   GtkWidget *ancestor = NULL;
2837   GList *temp_list = NULL;
2838   GList *list;
2839
2840   if (xpaned->in_recursion)
2841     return;
2842
2843   g_assert (widgets != NULL);
2844
2845   if (xpaned->last_top_left_child_focus &&
2846       !gtk_widget_is_ancestor (xpaned->last_top_left_child_focus,
2847                                GTK_WIDGET (xpaned)))
2848     {
2849       gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL);
2850     }
2851
2852   if (xpaned->last_top_right_child_focus &&
2853       !gtk_widget_is_ancestor (xpaned->last_top_right_child_focus,
2854                                GTK_WIDGET (xpaned)))
2855     {
2856       gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL);
2857     }
2858
2859   if (xpaned->last_bottom_left_child_focus &&
2860       !gtk_widget_is_ancestor (xpaned->last_bottom_left_child_focus,
2861                                GTK_WIDGET (xpaned)))
2862     {
2863       gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL);
2864     }
2865
2866   if (xpaned->last_bottom_right_child_focus &&
2867       !gtk_widget_is_ancestor (xpaned->last_bottom_right_child_focus,
2868                                GTK_WIDGET (xpaned)))
2869     {
2870       gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL);
2871     }
2872
2873   if (GTK_WIDGET (xpaned)->parent)
2874     ancestor = gtk_widget_get_ancestor (GTK_WIDGET (xpaned)->parent,
2875                                         GTK_TYPE_XPANED);
2876
2877   /* The idea here is that temp_list is a list of widgets we want to cycle
2878    * to. The list is prioritized so that the first element is our first
2879    * choice, the next our second, and so on.
2880    *
2881    * We can't just use g_list_reverse(), because we want to try
2882    * paned->last_child?_focus before paned->child?, both when we
2883    * are going forward and backward.
2884    */
2885   if (direction == GTK_DIR_TAB_FORWARD)
2886     {
2887       if (container->focus_child == xpaned->top_left_child)
2888         {
2889           temp_list =
2890             g_list_append (temp_list, xpaned->last_top_right_child_focus);
2891           temp_list = g_list_append (temp_list, xpaned->top_right_child);
2892           temp_list = g_list_append (temp_list, ancestor);
2893         }
2894       else if (container->focus_child == xpaned->top_right_child)
2895         {
2896           temp_list = g_list_append (temp_list, ancestor);
2897           temp_list =
2898             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2899           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2900         }
2901       else if (container->focus_child == xpaned->bottom_left_child)
2902         {
2903           temp_list = g_list_append (temp_list, ancestor);
2904           temp_list =
2905             g_list_append (temp_list, xpaned->last_bottom_right_child_focus);
2906           temp_list = g_list_append (temp_list, xpaned->bottom_right_child);
2907         }
2908       else if (container->focus_child == xpaned->bottom_right_child)
2909         {
2910           temp_list = g_list_append (temp_list, ancestor);
2911           temp_list =
2912             g_list_append (temp_list, xpaned->last_top_left_child_focus);
2913           temp_list = g_list_append (temp_list, xpaned->top_left_child);
2914         }
2915       else
2916         {
2917           temp_list =
2918             g_list_append (temp_list, xpaned->last_top_left_child_focus);
2919           temp_list = g_list_append (temp_list, xpaned->top_left_child);
2920           temp_list =
2921             g_list_append (temp_list, xpaned->last_top_right_child_focus);
2922           temp_list = g_list_append (temp_list, xpaned->top_right_child);
2923           temp_list =
2924             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2925           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2926           temp_list =
2927             g_list_append (temp_list, xpaned->last_bottom_right_child_focus);
2928           temp_list = g_list_append (temp_list, xpaned->bottom_right_child);
2929           temp_list = g_list_append (temp_list, ancestor);
2930         }
2931     }
2932   else
2933     {
2934       if (container->focus_child == xpaned->top_left_child)
2935         {
2936           temp_list = g_list_append (temp_list, ancestor);
2937           temp_list =
2938             g_list_append (temp_list, xpaned->last_top_right_child_focus);
2939           temp_list = g_list_append (temp_list, xpaned->top_right_child);
2940         }
2941       else if (container->focus_child == xpaned->top_right_child)
2942         {
2943           temp_list =
2944             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2945           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2946           temp_list = g_list_append (temp_list, ancestor);
2947         }
2948       else if (container->focus_child == xpaned->bottom_right_child)
2949         {
2950           temp_list =
2951             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2952           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2953           temp_list = g_list_append (temp_list, ancestor);
2954         }
2955       else if (container->focus_child == xpaned->top_right_child)
2956         {
2957           temp_list =
2958             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2959           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2960           temp_list = g_list_append (temp_list, ancestor);
2961         }
2962       else
2963         {
2964           temp_list =
2965             g_list_append (temp_list, xpaned->last_bottom_right_child_focus);
2966           temp_list = g_list_append (temp_list, xpaned->bottom_right_child);
2967           temp_list =
2968             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2969           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2970           temp_list =
2971             g_list_append (temp_list, xpaned->last_top_right_child_focus);
2972           temp_list = g_list_append (temp_list, xpaned->top_right_child);
2973           temp_list =
2974             g_list_append (temp_list, xpaned->last_top_left_child_focus);
2975           temp_list = g_list_append (temp_list, xpaned->top_left_child);
2976           temp_list = g_list_append (temp_list, ancestor);
2977         }
2978     }
2979
2980   /* Walk the list and expand all the paned widgets. */
2981   for (list = temp_list; list != NULL; list = list->next)
2982     {
2983       GtkWidget *widget = list->data;
2984
2985       if (widget)
2986         {
2987           if (GTK_IS_XPANED (widget))
2988             {
2989               xpaned->in_recursion = TRUE;
2990               gtk_xpaned_get_cycle_chain (GTK_XPANED (widget),
2991                                           direction, widgets);
2992               xpaned->in_recursion = FALSE;
2993             }
2994           else
2995             {
2996               *widgets = g_list_append (*widgets, widget);
2997             }
2998         }
2999     }
3000
3001   g_list_free (temp_list);
3002 }
3003
3004 static gboolean
3005 gtk_xpaned_cycle_child_focus (GtkXPaned * xpaned, gboolean reversed)
3006 {
3007   GList *cycle_chain = NULL;
3008   GList *list;
3009
3010   GtkDirectionType direction =
3011     reversed ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD;
3012
3013   /* ignore f6 if the handle is focused */
3014   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3015     return TRUE;
3016
3017   /* we can't just let the event propagate up the hierarchy,
3018    * because the paned will want to cycle focus _unless_ an
3019    * ancestor paned handles the event
3020    */
3021   gtk_xpaned_get_cycle_chain (xpaned, direction, &cycle_chain);
3022
3023   for (list = cycle_chain; list != NULL; list = list->next)
3024     if (gtk_widget_child_focus (GTK_WIDGET (list->data), direction))
3025       break;
3026
3027   g_list_free (cycle_chain);
3028
3029   return TRUE;
3030 }
3031
3032 static void
3033 get_child_xpanes (GtkWidget * widget, GList ** xpanes)
3034 {
3035   if (GTK_IS_XPANED (widget))
3036     {
3037       GtkXPaned *xpaned = GTK_XPANED (widget);
3038
3039       get_child_xpanes (xpaned->top_left_child, xpanes);
3040       *xpanes = g_list_prepend (*xpanes, widget);
3041       get_child_xpanes (xpaned->top_right_child, xpanes);
3042       *xpanes = g_list_prepend (*xpanes, widget);
3043       get_child_xpanes (xpaned->bottom_left_child, xpanes);
3044       *xpanes = g_list_prepend (*xpanes, widget);
3045       get_child_xpanes (xpaned->bottom_right_child, xpanes);
3046     }
3047   else if (GTK_IS_CONTAINER (widget))
3048     {
3049       gtk_container_foreach (GTK_CONTAINER (widget),
3050                              (GtkCallback) get_child_xpanes, xpanes);
3051     }
3052 }
3053
3054 static GList *
3055 get_all_xpanes (GtkXPaned * xpaned)
3056 {
3057   GtkXPaned *topmost = NULL;
3058   GList *result = NULL;
3059   GtkWidget *w;
3060
3061   for (w = GTK_WIDGET (xpaned); w != NULL; w = w->parent)
3062     {
3063       if (GTK_IS_XPANED (w))
3064         topmost = GTK_XPANED (w);
3065     }
3066
3067   g_assert (topmost);
3068
3069   get_child_xpanes (GTK_WIDGET (topmost), &result);
3070
3071   return g_list_reverse (result);
3072 }
3073
3074 static void
3075 gtk_xpaned_find_neighbours (GtkXPaned * xpaned,
3076                             GtkXPaned ** next, GtkXPaned ** prev)
3077 {
3078   GList *all_xpanes;
3079   GList *this_link;
3080
3081   all_xpanes = get_all_xpanes (xpaned);
3082   g_assert (all_xpanes);
3083
3084   this_link = g_list_find (all_xpanes, xpaned);
3085
3086   g_assert (this_link);
3087
3088   if (this_link->next)
3089     *next = this_link->next->data;
3090   else
3091     *next = all_xpanes->data;
3092
3093   if (this_link->prev)
3094     *prev = this_link->prev->data;
3095   else
3096     *prev = g_list_last (all_xpanes)->data;
3097
3098   g_list_free (all_xpanes);
3099 }
3100
3101 static gboolean
3102 gtk_xpaned_move_handle (GtkXPaned * xpaned, GtkScrollType scroll)
3103 {
3104   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3105     {
3106       GdkPoint old_position;
3107       GdkPoint new_position;
3108       gint increment;
3109
3110       enum
3111       {
3112         SINGLE_STEP_SIZE = 1,
3113         PAGE_STEP_SIZE = 75
3114       };
3115
3116       new_position.x = old_position.x = gtk_xpaned_get_position_x (xpaned);
3117       new_position.y = old_position.y = gtk_xpaned_get_position_y (xpaned);
3118       increment = 0;
3119
3120       switch (scroll)
3121         {
3122         case GTK_SCROLL_STEP_LEFT:
3123         case GTK_SCROLL_STEP_UP:
3124         case GTK_SCROLL_STEP_BACKWARD:
3125           increment = -SINGLE_STEP_SIZE;
3126           break;
3127
3128         case GTK_SCROLL_STEP_RIGHT:
3129         case GTK_SCROLL_STEP_DOWN:
3130         case GTK_SCROLL_STEP_FORWARD:
3131           increment = SINGLE_STEP_SIZE;
3132           break;
3133
3134         case GTK_SCROLL_PAGE_LEFT:
3135         case GTK_SCROLL_PAGE_UP:
3136         case GTK_SCROLL_PAGE_BACKWARD:
3137           increment = -PAGE_STEP_SIZE;
3138           break;
3139
3140         case GTK_SCROLL_PAGE_RIGHT:
3141         case GTK_SCROLL_PAGE_DOWN:
3142         case GTK_SCROLL_PAGE_FORWARD:
3143           increment = PAGE_STEP_SIZE;
3144           break;
3145
3146         case GTK_SCROLL_START:
3147           new_position.x = xpaned->min_position.x;
3148           new_position.y = xpaned->min_position.y;
3149           break;
3150
3151         case GTK_SCROLL_END:
3152           new_position.x = xpaned->max_position.x;
3153           new_position.y = xpaned->max_position.y;
3154           break;
3155
3156         default:
3157           break;
3158         }
3159
3160       if (increment)
3161         {
3162           if (is_rtl (xpaned))
3163             increment = -increment;
3164
3165           new_position.x = old_position.x + increment;
3166           new_position.y = old_position.y + increment;
3167         }
3168
3169       new_position.x = CLAMP (new_position.x,
3170                               xpaned->min_position.x, xpaned->max_position.x);
3171
3172       new_position.y = CLAMP (new_position.y,
3173                               xpaned->min_position.y, xpaned->max_position.y);
3174
3175       if (old_position.x != new_position.x)
3176         gtk_xpaned_set_position_x (xpaned, new_position.x);
3177
3178       if (old_position.y != new_position.y)
3179         gtk_xpaned_set_position_y (xpaned, new_position.y);
3180
3181       return TRUE;
3182     }
3183
3184   return FALSE;
3185 }
3186
3187 static void
3188 gtk_xpaned_restore_focus (GtkXPaned * xpaned)
3189 {
3190   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3191     {
3192       if (xpaned->priv->saved_focus &&
3193           gtk_widget_get_sensitive (xpaned->priv->saved_focus))
3194         {
3195           gtk_widget_grab_focus (xpaned->priv->saved_focus);
3196         }
3197       else
3198         {
3199           /* the saved focus is somehow not available for focusing,
3200            * try
3201            *   1) tabbing into the paned widget
3202            * if that didn't work,
3203            *   2) unset focus for the window if there is one
3204            */
3205
3206           if (!gtk_widget_child_focus
3207               (GTK_WIDGET (xpaned), GTK_DIR_TAB_FORWARD))
3208             {
3209               GtkWidget *toplevel =
3210                 gtk_widget_get_toplevel (GTK_WIDGET (xpaned));
3211
3212               if (GTK_IS_WINDOW (toplevel))
3213                 gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
3214             }
3215         }
3216
3217       gtk_xpaned_set_saved_focus (xpaned, NULL);
3218       gtk_xpaned_set_first_xpaned (xpaned, NULL);
3219     }
3220 }
3221
3222 static gboolean
3223 gtk_xpaned_accept_position (GtkXPaned * xpaned)
3224 {
3225   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3226     {
3227       xpaned->original_position.x = -1;
3228       xpaned->original_position.y = -1;
3229       gtk_xpaned_restore_focus (xpaned);
3230
3231       return TRUE;
3232     }
3233
3234   return FALSE;
3235 }
3236
3237 static gboolean
3238 gtk_xpaned_cancel_position (GtkXPaned * xpaned)
3239 {
3240   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3241     {
3242       if (xpaned->original_position.x != -1)
3243         {
3244           gtk_xpaned_set_position_x (xpaned, xpaned->original_position.x);
3245           xpaned->original_position.x = -1;
3246         }
3247
3248       if (xpaned->original_position.y != -1)
3249         {
3250           gtk_xpaned_set_position_y (xpaned, xpaned->original_position.y);
3251           xpaned->original_position.y = -1;
3252         }
3253
3254       gtk_xpaned_restore_focus (xpaned);
3255       return TRUE;
3256     }
3257
3258   return FALSE;
3259 }
3260
3261 static gboolean
3262 gtk_xpaned_cycle_handle_focus (GtkXPaned * xpaned, gboolean reversed)
3263 {
3264   GtkXPaned *next;
3265   GtkXPaned *prev;
3266
3267   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3268     {
3269       GtkXPaned *focus = NULL;
3270
3271       if (!xpaned->priv->first_xpaned)
3272         {
3273           /* The first_pane has disappeared. As an ad-hoc solution,
3274            * we make the currently focused paned the first_paned. To the
3275            * user this will seem like the paned cycling has been reset.
3276            */
3277           gtk_xpaned_set_first_xpaned (xpaned, xpaned);
3278         }
3279
3280       gtk_xpaned_find_neighbours (xpaned, &next, &prev);
3281
3282       if (reversed && prev &&
3283           prev != xpaned && xpaned != xpaned->priv->first_xpaned)
3284         {
3285           focus = prev;
3286         }
3287       else if (!reversed &&
3288                next && next != xpaned && next != xpaned->priv->first_xpaned)
3289         {
3290           focus = next;
3291         }
3292       else
3293         {
3294           gtk_xpaned_accept_position (xpaned);
3295           return TRUE;
3296         }
3297
3298       g_assert (focus);
3299
3300       gtk_xpaned_set_saved_focus (focus, xpaned->priv->saved_focus);
3301       gtk_xpaned_set_first_xpaned (focus, xpaned->priv->first_xpaned);
3302
3303       gtk_xpaned_set_saved_focus (xpaned, NULL);
3304       gtk_xpaned_set_first_xpaned (xpaned, NULL);
3305
3306       gtk_widget_grab_focus (GTK_WIDGET (focus));
3307
3308       if (!gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3309         {
3310           xpaned->original_position.x = -1;
3311           xpaned->original_position.y = -1;
3312           focus->original_position.x = gtk_xpaned_get_position_x (focus);
3313           focus->original_position.y = gtk_xpaned_get_position_y (focus);
3314         }
3315     }
3316   else
3317     {
3318       GtkContainer *container = GTK_CONTAINER (xpaned);
3319       GtkXPaned *focus;
3320       GtkXPaned *first;
3321       GtkXPaned *prev;
3322       GtkXPaned *next;
3323       GtkWidget *toplevel;
3324
3325       gtk_xpaned_find_neighbours (xpaned, &next, &prev);
3326
3327       if (container->focus_child == xpaned->top_left_child)
3328         {
3329           if (reversed)
3330             {
3331               focus = prev;
3332               first = xpaned;
3333             }
3334           else
3335             {
3336               focus = xpaned;
3337               first = xpaned;
3338             }
3339         }
3340       else if (container->focus_child == xpaned->top_right_child)
3341         {
3342           if (reversed)
3343             {
3344               focus = xpaned;
3345               first = next;
3346             }
3347           else
3348             {
3349               focus = next;
3350               first = next;
3351             }
3352         }
3353       else
3354         {
3355           /* Focus is not inside this xpaned, and we don't have focus.
3356            * Presumably this happened because the application wants us
3357            * to start keyboard navigating.
3358            */
3359           focus = xpaned;
3360
3361           if (reversed)
3362             first = xpaned;
3363           else
3364             first = next;
3365         }
3366
3367       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned));
3368
3369       if (GTK_IS_WINDOW (toplevel))
3370         gtk_xpaned_set_saved_focus (focus,
3371                                     GTK_WINDOW (toplevel)->focus_widget);
3372       gtk_xpaned_set_first_xpaned (focus, first);
3373       focus->original_position.x = gtk_xpaned_get_position_x (focus);
3374       focus->original_position.y = gtk_xpaned_get_position_y (focus);
3375
3376       gtk_widget_grab_focus (GTK_WIDGET (focus));
3377     }
3378
3379   return TRUE;
3380 }
3381
3382 static gboolean
3383 gtk_xpaned_toggle_handle_focus (GtkXPaned * xpaned)
3384 {
3385   /* This function/signal has the wrong name. It is called when you
3386    * press Tab or Shift-Tab and what we do is act as if
3387    * the user pressed Return and then Tab or Shift-Tab
3388    */
3389   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3390     gtk_xpaned_accept_position (xpaned);
3391
3392   return FALSE;
3393 }
3394
3395 /*#define __GTK_XPANED_C__*/
3396 /*#include "gtkaliasdef.c"*/