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