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