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