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