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