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