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