GtkXPaned: Convert size_request to get_preferred_height/width
[pspp] / lib / gtk-contrib / gtkxpaned.c
1 /*******************************************************************************
2  **3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 
3  **      10        20        30        40        50        60        70        80
4  **
5  **  library for GtkXPaned-widget, a 2x2 grid-like variation of GtkPaned of gtk+
6  **  Copyright (C) 2012 Free Software Foundation, Inc.
7  **  Copyright (C) 2005-2006 Mirco "MacSlow" Müller <macslow@bangang.de>
8  **
9  **  This library is free software; you can redistribute it and/or
10  **  modify it under the terms of the GNU Lesser General Public
11  **  License as published by the Free Software Foundation; either
12  **  version 2.1 of the License, or (at your option) any later version.
13  **
14  **  This library is distributed in the hope that it will be useful,
15  **  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  **  Lesser General Public License for more details.
18  **
19  **  You should have received a copy of the GNU Lesser General Public
20  **  License along with this library; if not, write to the Free Software
21  **  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  **
23  **  GtkXPaned is based on GtkPaned which was done by...
24  **
25  **  "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald"
26  **
27  **  and later modified by...
28  **
29  **  "the GTK+ Team and others 1997-2000"
30  **
31  *******************************************************************************/
32
33 #include <config.h>
34 #include "gtkxpaned.h"
35
36 #include <gtk/gtk.h>
37 #include <ui/gui/psppire-marshal.h>
38 #include <gdk/gdkkeysyms.h>
39 #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   GdkRectangle horizontalClipArea;
1400   GdkRectangle verticalClipArea;
1401
1402   /* determine size of handle(s) */
1403   gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
1404
1405   /* I want the handle-"thickness" to be at least 3 */
1406   g_assert (handle_size >= 3);
1407
1408   if (gtk_widget_get_visible (widget) && gtk_widget_get_mapped (widget) &&
1409       xpaned->top_left_child
1410       && gtk_widget_get_visible (xpaned->top_left_child)
1411       && xpaned->top_right_child
1412       && gtk_widget_get_visible (xpaned->top_right_child)
1413       && xpaned->bottom_left_child
1414       && gtk_widget_get_visible (xpaned->bottom_left_child)
1415       && xpaned->bottom_right_child
1416       && gtk_widget_get_visible (xpaned->bottom_right_child))
1417     {
1418       GtkStateType state;
1419
1420       if (gtk_widget_is_focus (widget))
1421         state = GTK_STATE_SELECTED;
1422       else if (xpaned->handle_prelit)
1423         state = GTK_STATE_PRELIGHT;
1424       else
1425         state = gtk_widget_get_state (widget);
1426
1427       horizontalClipArea.x = xpaned->handle_pos_west.x;
1428       horizontalClipArea.y = xpaned->handle_pos_west.y;
1429       horizontalClipArea.width =
1430         xpaned->handle_pos_west.width + handle_size +
1431         xpaned->handle_pos_east.width;
1432       horizontalClipArea.height = handle_size;
1433
1434       verticalClipArea.x = xpaned->handle_pos_north.x;
1435       verticalClipArea.y = xpaned->handle_pos_north.y;
1436       verticalClipArea.width = handle_size;
1437       verticalClipArea.height =
1438         xpaned->handle_pos_north.height + handle_size +
1439         xpaned->handle_pos_south.height;
1440
1441       gtk_paint_handle (gtk_widget_get_style (widget),
1442                         cr,
1443                         state,
1444                         GTK_SHADOW_NONE,
1445                         widget,
1446                         "paned",
1447                         xpaned->handle_pos_east.x - handle_size - 256 / 2,
1448                         xpaned->handle_pos_west.y + 1,
1449                         256 + handle_size, handle_size - 2,
1450                         /*xpaned->handle_pos_west.x,
1451                           xpaned->handle_pos_west.y + 1,
1452                           xpaned->handle_pos_west.width + handle_size + xpaned->handle_pos_east.width,
1453                           handle_size - 2, */
1454                         GTK_ORIENTATION_HORIZONTAL);
1455
1456       gtk_paint_handle (gtk_widget_get_style (widget),
1457                         cr,
1458                         state,
1459                         GTK_SHADOW_NONE,
1460                         widget,
1461                         "paned",
1462                         xpaned->handle_pos_north.x + 1,
1463                         xpaned->handle_pos_south.y - handle_size - 256 / 2,
1464                         handle_size - 2, 256 + handle_size,
1465                         /*xpaned->handle_pos_north.x + 1,
1466                           xpaned->handle_pos_north.y,
1467                           handle_size - 2,
1468                           xpaned->handle_pos_north.height + handle_size + xpaned->handle_pos_south.height, */
1469                         GTK_ORIENTATION_VERTICAL);
1470     }
1471
1472   /* Chain up to draw children */
1473   GTK_WIDGET_CLASS (parent_class)->draw (widget, cr);
1474
1475   return FALSE;
1476 }
1477
1478 static gboolean
1479 is_rtl (GtkXPaned * xpaned)
1480 {
1481   if (gtk_widget_get_direction (GTK_WIDGET (xpaned)) == GTK_TEXT_DIR_RTL)
1482     return TRUE;
1483
1484   return FALSE;
1485 }
1486
1487 static void
1488 update_drag (GtkXPaned * xpaned)
1489 {
1490   GdkPoint pos;
1491   gint handle_size;
1492   GtkRequisition size;
1493   GtkAllocation allocation;
1494   gtk_widget_get_allocation (GTK_WIDGET (xpaned), &allocation);
1495
1496   gtk_widget_get_pointer (GTK_WIDGET (xpaned), &pos.x, &pos.y);
1497
1498   if (xpaned->in_drag_vert)
1499     {
1500       pos.y -= xpaned->drag_pos.y;
1501
1502       if (is_rtl (xpaned))
1503         {
1504           gtk_widget_style_get (GTK_WIDGET (xpaned),
1505                                 "handle-size", &handle_size, NULL);
1506
1507           size.height = allocation.height - pos.y - handle_size;
1508         }
1509       else
1510         {
1511           size.height = pos.y;
1512         }
1513
1514       size.height -= gtk_container_get_border_width (GTK_CONTAINER (xpaned));
1515
1516       size.height =
1517         CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y);
1518
1519       if (size.height != xpaned->top_left_child_size.height)
1520         gtk_xpaned_set_position_y (xpaned, size.height);
1521     }
1522
1523   if (xpaned->in_drag_horiz)
1524     {
1525       pos.x -= xpaned->drag_pos.x;
1526
1527       if (is_rtl (xpaned))
1528         {
1529           gtk_widget_style_get (GTK_WIDGET (xpaned),
1530                                 "handle-size", &handle_size, NULL);
1531
1532           size.width = allocation.width - pos.x - handle_size;
1533         }
1534       else
1535         {
1536           size.width = pos.x;
1537         }
1538
1539       size.width -= gtk_container_get_border_width (GTK_CONTAINER (xpaned));
1540
1541       size.width =
1542         CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x);
1543
1544       if (size.width != xpaned->top_left_child_size.width)
1545         gtk_xpaned_set_position_x (xpaned, size.width);
1546     }
1547
1548   if (xpaned->in_drag_vert_and_horiz)
1549     {
1550       pos.x -= xpaned->drag_pos.x;
1551       pos.y -= xpaned->drag_pos.y;
1552
1553       if (is_rtl (xpaned))
1554         {
1555           gtk_widget_style_get (GTK_WIDGET (xpaned),
1556                                 "handle-size", &handle_size, NULL);
1557
1558           size.width = allocation.width - pos.x - handle_size;
1559           size.height = allocation.height - pos.y - handle_size;
1560         }
1561       else
1562         {
1563           size.width = pos.x;
1564           size.height = pos.y;
1565         }
1566
1567       size.width -= gtk_container_get_border_width (GTK_CONTAINER (xpaned));
1568       size.height -= gtk_container_get_border_width (GTK_CONTAINER (xpaned));
1569
1570       size.width =
1571         CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x);
1572       size.height =
1573         CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y);
1574
1575       if (size.width != xpaned->top_left_child_size.width)
1576         gtk_xpaned_set_position_x (xpaned, size.width);
1577
1578       if (size.height != xpaned->top_left_child_size.height)
1579         gtk_xpaned_set_position_y (xpaned, size.height);
1580     }
1581 }
1582
1583 static gboolean
1584 gtk_xpaned_enter (GtkWidget * widget, GdkEventCrossing * event)
1585 {
1586   GtkXPaned *xpaned = GTK_XPANED (widget);
1587
1588   if (xpaned->in_drag_vert ||
1589       xpaned->in_drag_horiz || xpaned->in_drag_vert_and_horiz)
1590     update_drag (xpaned);
1591   else
1592     {
1593       xpaned->handle_prelit = TRUE;
1594
1595       gtk_widget_queue_draw_area (widget,
1596                                   xpaned->handle_pos_east.x,
1597                                   xpaned->handle_pos_east.y,
1598                                   xpaned->handle_pos_east.width,
1599                                   xpaned->handle_pos_east.height);
1600
1601       gtk_widget_queue_draw_area (widget,
1602                                   xpaned->handle_pos_west.x,
1603                                   xpaned->handle_pos_west.y,
1604                                   xpaned->handle_pos_west.width,
1605                                   xpaned->handle_pos_west.height);
1606
1607       gtk_widget_queue_draw_area (widget,
1608                                   xpaned->handle_pos_north.x,
1609                                   xpaned->handle_pos_north.y,
1610                                   xpaned->handle_pos_north.width,
1611                                   xpaned->handle_pos_north.height);
1612
1613       gtk_widget_queue_draw_area (widget,
1614                                   xpaned->handle_pos_south.x,
1615                                   xpaned->handle_pos_south.y,
1616                                   xpaned->handle_pos_south.width,
1617                                   xpaned->handle_pos_south.height);
1618
1619       gtk_widget_queue_draw_area (widget,
1620                                   xpaned->handle_pos_middle.x,
1621                                   xpaned->handle_pos_middle.y,
1622                                   xpaned->handle_pos_middle.width,
1623                                   xpaned->handle_pos_middle.height);
1624     }
1625
1626   return TRUE;
1627 }
1628
1629 static gboolean
1630 gtk_xpaned_leave (GtkWidget * widget, GdkEventCrossing * event)
1631 {
1632   GtkXPaned *xpaned = GTK_XPANED (widget);
1633
1634   if (xpaned->in_drag_vert ||
1635       xpaned->in_drag_horiz || xpaned->in_drag_vert_and_horiz)
1636     update_drag (xpaned);
1637   else
1638     {
1639       xpaned->handle_prelit = FALSE;
1640
1641       gtk_widget_queue_draw_area (widget,
1642                                   xpaned->handle_pos_east.x,
1643                                   xpaned->handle_pos_east.y,
1644                                   xpaned->handle_pos_east.width,
1645                                   xpaned->handle_pos_east.height);
1646
1647       gtk_widget_queue_draw_area (widget,
1648                                   xpaned->handle_pos_west.x,
1649                                   xpaned->handle_pos_west.y,
1650                                   xpaned->handle_pos_west.width,
1651                                   xpaned->handle_pos_west.height);
1652
1653       gtk_widget_queue_draw_area (widget,
1654                                   xpaned->handle_pos_north.x,
1655                                   xpaned->handle_pos_north.y,
1656                                   xpaned->handle_pos_north.width,
1657                                   xpaned->handle_pos_north.height);
1658
1659       gtk_widget_queue_draw_area (widget,
1660                                   xpaned->handle_pos_south.x,
1661                                   xpaned->handle_pos_south.y,
1662                                   xpaned->handle_pos_south.width,
1663                                   xpaned->handle_pos_south.height);
1664
1665       gtk_widget_queue_draw_area (widget,
1666                                   xpaned->handle_pos_middle.x,
1667                                   xpaned->handle_pos_middle.y,
1668                                   xpaned->handle_pos_middle.width,
1669                                   xpaned->handle_pos_middle.height);
1670     }
1671
1672   return TRUE;
1673 }
1674
1675 static gboolean
1676 gtk_xpaned_focus (GtkWidget * widget, GtkDirectionType direction)
1677 {
1678   gboolean retval;
1679
1680   /* This is a hack, but how can this be done without
1681    * excessive cut-and-paste from gtkcontainer.c?
1682    */
1683
1684   gtk_widget_set_can_focus (GTK_WIDGET (widget), FALSE);
1685   retval = (*GTK_WIDGET_CLASS (parent_class)->focus) (widget, direction);
1686   gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE);
1687
1688   return retval;
1689 }
1690
1691 static gboolean
1692 gtk_xpaned_button_press (GtkWidget * widget, GdkEventButton * event)
1693 {
1694   GtkXPaned *xpaned = GTK_XPANED (widget);
1695
1696   /* if any child is currently maximized, jump right back */
1697   if (xpaned->maximized[GTK_XPANED_TOP_LEFT] ||
1698       xpaned->maximized[GTK_XPANED_TOP_RIGHT] ||
1699       xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] ||
1700       xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
1701     return FALSE;
1702
1703   /* if user is dragging the handles around */
1704   if (!xpaned->in_drag_vert_and_horiz &&
1705       event->window != xpaned->handle_east &&
1706       event->window != xpaned->handle_west &&
1707       event->window != xpaned->handle_north &&
1708       event->window != xpaned->handle_south &&
1709       event->window == xpaned->handle_middle && event->button == 1)
1710     {
1711       xpaned->in_drag_vert_and_horiz = TRUE;
1712
1713       /* We need a server grab here, not gtk_grab_add(), since
1714        * we don't want to pass events on to the widget's children */
1715       if (gdk_pointer_grab (xpaned->handle_middle,
1716                             FALSE,
1717                             GDK_POINTER_MOTION_HINT_MASK
1718                             | GDK_BUTTON1_MOTION_MASK
1719                             | GDK_BUTTON_RELEASE_MASK
1720                             | GDK_ENTER_NOTIFY_MASK
1721                             | GDK_LEAVE_NOTIFY_MASK,
1722                             NULL, NULL, event->time) == GDK_GRAB_SUCCESS)
1723         {
1724         }
1725
1726       xpaned->drag_pos.x = event->x;
1727       xpaned->drag_pos.y = event->y;
1728
1729       return TRUE;
1730     }
1731   else if (!xpaned->in_drag_vert &&
1732            event->window == xpaned->handle_east &&
1733            event->window != xpaned->handle_west &&
1734            event->window != xpaned->handle_north &&
1735            event->window != xpaned->handle_south &&
1736            event->window != xpaned->handle_middle && event->button == 1)
1737     {
1738       xpaned->in_drag_vert = TRUE;
1739
1740       /* We need a server grab here, not gtk_grab_add(), since
1741        * we don't want to pass events on to the widget's children */
1742       if (gdk_pointer_grab (xpaned->handle_east,
1743                             FALSE,
1744                             GDK_POINTER_MOTION_HINT_MASK
1745                             | GDK_BUTTON1_MOTION_MASK
1746                             | GDK_BUTTON_RELEASE_MASK
1747                             | GDK_ENTER_NOTIFY_MASK
1748                             | GDK_LEAVE_NOTIFY_MASK,
1749                             NULL, NULL, event->time) == GDK_GRAB_SUCCESS)
1750         {
1751         }
1752
1753       xpaned->drag_pos.y = event->y;
1754
1755       return TRUE;
1756     }
1757   else if (!xpaned->in_drag_vert &&
1758            event->window != xpaned->handle_east &&
1759            event->window == xpaned->handle_west &&
1760            event->window != xpaned->handle_north &&
1761            event->window != xpaned->handle_south &&
1762            event->window != xpaned->handle_middle && event->button == 1)
1763     {
1764       xpaned->in_drag_vert = TRUE;
1765
1766       /* We need a server grab here, not gtk_grab_add(), since
1767        * we don't want to pass events on to the widget's children */
1768       if (gdk_pointer_grab (xpaned->handle_west,
1769                             FALSE,
1770                             GDK_POINTER_MOTION_HINT_MASK
1771                             | GDK_BUTTON1_MOTION_MASK
1772                             | GDK_BUTTON_RELEASE_MASK
1773                             | GDK_ENTER_NOTIFY_MASK
1774                             | GDK_LEAVE_NOTIFY_MASK,
1775                             NULL, NULL, event->time) == GDK_GRAB_SUCCESS)
1776         {
1777         }
1778
1779       xpaned->drag_pos.y = event->y;
1780
1781       return TRUE;
1782     }
1783   else if (!xpaned->in_drag_horiz &&
1784            event->window != xpaned->handle_east &&
1785            event->window != xpaned->handle_west &&
1786            event->window == xpaned->handle_north &&
1787            event->window != xpaned->handle_south &&
1788            event->window != xpaned->handle_middle && event->button == 1)
1789     {
1790       xpaned->in_drag_horiz = TRUE;
1791
1792       /* We need a server grab here, not gtk_grab_add(), since
1793        * we don't want to pass events on to the widget's children */
1794       if (gdk_pointer_grab (xpaned->handle_north,
1795                             FALSE,
1796                             GDK_POINTER_MOTION_HINT_MASK
1797                             | GDK_BUTTON1_MOTION_MASK
1798                             | GDK_BUTTON_RELEASE_MASK
1799                             | GDK_ENTER_NOTIFY_MASK
1800                             | GDK_LEAVE_NOTIFY_MASK,
1801                             NULL, NULL, event->time) == GDK_GRAB_SUCCESS)
1802         {
1803         }
1804
1805       xpaned->drag_pos.x = event->x;
1806
1807       return TRUE;
1808     }
1809   else if (!xpaned->in_drag_horiz &&
1810            event->window != xpaned->handle_east &&
1811            event->window != xpaned->handle_west &&
1812            event->window != xpaned->handle_north &&
1813            event->window == xpaned->handle_south &&
1814            event->window != xpaned->handle_middle && event->button == 1)
1815     {
1816       xpaned->in_drag_horiz = TRUE;
1817
1818       /* We need a server grab here, not gtk_grab_add(), since
1819        * we don't want to pass events on to the widget's children */
1820       if (gdk_pointer_grab (xpaned->handle_south,
1821                             FALSE,
1822                             GDK_POINTER_MOTION_HINT_MASK
1823                             | GDK_BUTTON1_MOTION_MASK
1824                             | GDK_BUTTON_RELEASE_MASK
1825                             | GDK_ENTER_NOTIFY_MASK
1826                             | GDK_LEAVE_NOTIFY_MASK,
1827                             NULL, NULL, event->time) == GDK_GRAB_SUCCESS)
1828         {
1829         }
1830
1831       xpaned->drag_pos.x = event->x;
1832
1833       return TRUE;
1834     }
1835   return FALSE;
1836 }
1837
1838 static gboolean
1839 gtk_xpaned_button_release (GtkWidget * widget, GdkEventButton * event)
1840 {
1841   GtkXPaned *xpaned = GTK_XPANED (widget);
1842
1843   if (xpaned->in_drag_vert && (event->button == 1))
1844     {
1845       xpaned->in_drag_vert = FALSE;
1846       xpaned->drag_pos.y = -1;
1847       xpaned->position_set = TRUE;
1848       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
1849                                   event->time);
1850       return TRUE;
1851     }
1852   else if (xpaned->in_drag_horiz && (event->button == 1))
1853     {
1854       xpaned->in_drag_horiz = FALSE;
1855       xpaned->drag_pos.x = -1;
1856       xpaned->position_set = TRUE;
1857       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
1858                                   event->time);
1859       return TRUE;
1860     }
1861   else if (xpaned->in_drag_vert_and_horiz && (event->button == 1))
1862     {
1863       xpaned->in_drag_vert_and_horiz = FALSE;
1864       xpaned->drag_pos.x = -1;
1865       xpaned->drag_pos.y = -1;
1866       xpaned->position_set = TRUE;
1867       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
1868                                   event->time);
1869       return TRUE;
1870     }
1871
1872   return FALSE;
1873 }
1874
1875 static gboolean
1876 gtk_xpaned_motion (GtkWidget * widget, GdkEventMotion * event)
1877 {
1878   GtkXPaned *xpaned = GTK_XPANED (widget);
1879
1880   if (xpaned->in_drag_vert ||
1881       xpaned->in_drag_horiz || xpaned->in_drag_vert_and_horiz)
1882
1883     {
1884       update_drag (xpaned);
1885       return TRUE;
1886     }
1887
1888   return FALSE;
1889 }
1890
1891 void
1892 gtk_xpaned_add_top_left (GtkXPaned * xpaned, GtkWidget * widget)
1893 {
1894   gtk_xpaned_pack_top_left (xpaned, widget, FALSE, TRUE);
1895 }
1896
1897 void
1898 gtk_xpaned_add_top_right (GtkXPaned * xpaned, GtkWidget * widget)
1899 {
1900   gtk_xpaned_pack_top_right (xpaned, widget, FALSE, TRUE);
1901 }
1902
1903 void
1904 gtk_xpaned_add_bottom_left (GtkXPaned * xpaned, GtkWidget * widget)
1905 {
1906   gtk_xpaned_pack_bottom_left (xpaned, widget, FALSE, TRUE);
1907 }
1908
1909 void
1910 gtk_xpaned_add_bottom_right (GtkXPaned * xpaned, GtkWidget * widget)
1911 {
1912   gtk_xpaned_pack_bottom_right (xpaned, widget, FALSE, TRUE);
1913 }
1914
1915 void
1916 gtk_xpaned_pack_top_left (GtkXPaned * xpaned,
1917                           GtkWidget * child, gboolean resize, gboolean shrink)
1918 {
1919   g_return_if_fail (GTK_IS_XPANED (xpaned));
1920   g_return_if_fail (GTK_IS_WIDGET (child));
1921
1922   if (!xpaned->top_left_child)
1923     {
1924       xpaned->top_left_child = child;
1925       xpaned->top_left_child_resize = resize;
1926       xpaned->top_left_child_shrink = shrink;
1927
1928       gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
1929     }
1930 }
1931
1932 void
1933 gtk_xpaned_pack_top_right (GtkXPaned * xpaned,
1934                            GtkWidget * child,
1935                            gboolean resize, gboolean shrink)
1936 {
1937   g_return_if_fail (GTK_IS_XPANED (xpaned));
1938   g_return_if_fail (GTK_IS_WIDGET (child));
1939
1940   if (!xpaned->top_right_child)
1941     {
1942       xpaned->top_right_child = child;
1943       xpaned->top_right_child_resize = resize;
1944       xpaned->top_right_child_shrink = shrink;
1945
1946       gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
1947     }
1948 }
1949
1950 void
1951 gtk_xpaned_pack_bottom_left (GtkXPaned * xpaned,
1952                              GtkWidget * child,
1953                              gboolean resize, gboolean shrink)
1954 {
1955   g_return_if_fail (GTK_IS_XPANED (xpaned));
1956   g_return_if_fail (GTK_IS_WIDGET (child));
1957
1958   if (!xpaned->bottom_left_child)
1959     {
1960       xpaned->bottom_left_child = child;
1961       xpaned->bottom_left_child_resize = resize;
1962       xpaned->bottom_left_child_shrink = shrink;
1963
1964       gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
1965     }
1966 }
1967
1968 void
1969 gtk_xpaned_pack_bottom_right (GtkXPaned * xpaned,
1970                               GtkWidget * child,
1971                               gboolean resize, gboolean shrink)
1972 {
1973   g_return_if_fail (GTK_IS_XPANED (xpaned));
1974   g_return_if_fail (GTK_IS_WIDGET (child));
1975
1976   if (!xpaned->bottom_right_child)
1977     {
1978       xpaned->bottom_right_child = child;
1979       xpaned->bottom_right_child_resize = resize;
1980       xpaned->bottom_right_child_shrink = shrink;
1981
1982       gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
1983     }
1984 }
1985
1986 static void
1987 gtk_xpaned_add (GtkContainer * container, GtkWidget * widget)
1988 {
1989   GtkXPaned *xpaned;
1990
1991   g_return_if_fail (GTK_IS_XPANED (container));
1992
1993   xpaned = GTK_XPANED (container);
1994
1995   if (!xpaned->top_left_child)
1996     gtk_xpaned_add_top_left (xpaned, widget);
1997   else if (!xpaned->top_right_child)
1998     gtk_xpaned_add_top_right (xpaned, widget);
1999   else if (!xpaned->bottom_left_child)
2000     gtk_xpaned_add_bottom_left (xpaned, widget);
2001   else if (!xpaned->bottom_right_child)
2002     gtk_xpaned_add_bottom_right (xpaned, widget);
2003   else
2004     g_warning ("GtkXPaned cannot have more than 4 children\n");
2005 }
2006
2007 static void
2008 gtk_xpaned_remove (GtkContainer * container, GtkWidget * widget)
2009 {
2010   GtkXPaned *xpaned;
2011   gboolean was_visible;
2012
2013   xpaned = GTK_XPANED (container);
2014   was_visible = gtk_widget_get_visible (widget);
2015
2016   if (xpaned->top_left_child == widget)
2017     {
2018       gtk_widget_unparent (widget);
2019
2020       xpaned->top_left_child = NULL;
2021
2022       if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
2023         gtk_widget_queue_resize (GTK_WIDGET (container));
2024     }
2025   else if (xpaned->top_right_child == widget)
2026     {
2027       gtk_widget_unparent (widget);
2028
2029       xpaned->top_right_child = NULL;
2030
2031       if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
2032         gtk_widget_queue_resize (GTK_WIDGET (container));
2033     }
2034   else if (xpaned->bottom_left_child == widget)
2035     {
2036       gtk_widget_unparent (widget);
2037
2038       xpaned->bottom_left_child = NULL;
2039
2040       if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
2041         gtk_widget_queue_resize (GTK_WIDGET (container));
2042     }
2043   else if (xpaned->bottom_right_child == widget)
2044     {
2045       gtk_widget_unparent (widget);
2046
2047       xpaned->bottom_right_child = NULL;
2048
2049       if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
2050         gtk_widget_queue_resize (GTK_WIDGET (container));
2051     }
2052   else
2053     g_warning ("GtkXPaned has no more children attached\n");
2054
2055 }
2056
2057 static void
2058 gtk_xpaned_forall (GtkContainer * container,
2059                    gboolean include_internals,
2060                    GtkCallback callback, gpointer callback_data)
2061 {
2062   GtkXPaned *xpaned;
2063
2064   g_return_if_fail (callback != NULL);
2065
2066   xpaned = GTK_XPANED (container);
2067
2068   if (xpaned->top_left_child)
2069     (*callback) (xpaned->top_left_child, callback_data);
2070   if (xpaned->top_right_child)
2071     (*callback) (xpaned->top_right_child, callback_data);
2072   if (xpaned->bottom_left_child)
2073     (*callback) (xpaned->bottom_left_child, callback_data);
2074   if (xpaned->bottom_right_child)
2075     (*callback) (xpaned->bottom_right_child, callback_data);
2076 }
2077
2078 /**
2079  * gtk_xpaned_get_position_x:
2080  * @paned: a #GtkXPaned widget
2081  * 
2082  * Obtains the x-position of the divider.
2083  * 
2084  * Return value: x-position of the divider
2085  **/
2086 gint
2087 gtk_xpaned_get_position_x (GtkXPaned * xpaned)
2088 {
2089   g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0);
2090
2091   return xpaned->top_left_child_size.width;
2092 }
2093
2094 /**
2095  * gtk_xpaned_get_position_y:
2096  * @paned: a #GtkXPaned widget
2097  * 
2098  * Obtains the y-position of the divider.
2099  * 
2100  * Return value: y-position of the divider
2101  **/
2102 gint
2103 gtk_xpaned_get_position_y (GtkXPaned * xpaned)
2104 {
2105   g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0);
2106
2107   return xpaned->top_left_child_size.height;
2108 }
2109
2110 /**
2111  * gtk_xpaned_set_position_x:
2112  * @paned: a #GtkXPaned widget
2113  * @xposition: pixel x-position of divider, a negative values
2114  *                         of a component mean that the position is unset.
2115  * 
2116  * Sets the x-position of the divider between the four panes.
2117  **/
2118 void
2119 gtk_xpaned_set_position_x (GtkXPaned * xpaned, gint xposition)
2120 {
2121   GObject *object;
2122
2123   g_return_if_fail (GTK_IS_XPANED (xpaned));
2124
2125   /* if any child is currently maximized, jump right back */
2126   if (xpaned->maximized[GTK_XPANED_TOP_LEFT] ||
2127       xpaned->maximized[GTK_XPANED_TOP_RIGHT] ||
2128       xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] ||
2129       xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2130     return;
2131
2132   object = G_OBJECT (xpaned);
2133
2134   if (xposition >= 0)
2135     {
2136       /* We don't clamp here - the assumption is that
2137        * if the total allocation changes at the same time
2138        * as the position, the position set is with reference
2139        * to the new total size. If only the position changes,
2140        * then clamping will occur in gtk_paned_compute_position()
2141        */
2142
2143       xpaned->top_left_child_size.width = xposition;
2144       xpaned->position_set = TRUE;
2145     }
2146   else
2147     {
2148       xpaned->position_set = FALSE;
2149     }
2150
2151   g_object_freeze_notify (object);
2152   g_object_notify (object, "x-position");
2153   g_object_notify (object, "position-set");
2154   g_object_thaw_notify (object);
2155
2156   gtk_widget_queue_resize (GTK_WIDGET (xpaned));
2157 }
2158
2159 /**
2160  * gtk_xpaned_set_position_y:
2161  * @paned: a #GtkXPaned widget
2162  * @yposition: pixel y-position of divider, a negative values
2163  *                         of a component mean that the position is unset.
2164  * 
2165  * Sets the y-position of the divider between the four panes.
2166  **/
2167 void
2168 gtk_xpaned_set_position_y (GtkXPaned * xpaned, gint yposition)
2169 {
2170   GObject *object;
2171
2172   g_return_if_fail (GTK_IS_XPANED (xpaned));
2173
2174   /* if any child is currently maximized, jump right back */
2175   if (xpaned->maximized[GTK_XPANED_TOP_LEFT] ||
2176       xpaned->maximized[GTK_XPANED_TOP_RIGHT] ||
2177       xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] ||
2178       xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2179     return;
2180
2181   object = G_OBJECT (xpaned);
2182
2183   if (yposition >= 0)
2184     {
2185       /* We don't clamp here - the assumption is that
2186        * if the total allocation changes at the same time
2187        * as the position, the position set is with reference
2188        * to the new total size. If only the position changes,
2189        * then clamping will occur in gtk_paned_compute_position()
2190        */
2191
2192       xpaned->top_left_child_size.height = yposition;
2193       xpaned->position_set = TRUE;
2194     }
2195   else
2196     {
2197       xpaned->position_set = FALSE;
2198     }
2199
2200   g_object_freeze_notify (object);
2201   g_object_notify (object, "y-position");
2202   g_object_notify (object, "position-set");
2203   g_object_thaw_notify (object);
2204
2205   gtk_widget_queue_resize (GTK_WIDGET (xpaned));
2206 }
2207
2208 /* this call is private and only intended for internal use! */
2209 void
2210 gtk_xpaned_save_unmaximized_x (GtkXPaned * xpaned)
2211 {
2212   xpaned->unmaximized_position.x = gtk_xpaned_get_position_x (xpaned);
2213 }
2214
2215 /* this call is private and only intended for internal use! */
2216 void
2217 gtk_xpaned_save_unmaximized_y (GtkXPaned * xpaned)
2218 {
2219   xpaned->unmaximized_position.y = gtk_xpaned_get_position_y (xpaned);
2220 }
2221
2222 /* this call is private and only intended for internal use! */
2223 gint
2224 gtk_xpaned_fetch_unmaximized_x (GtkXPaned * xpaned)
2225 {
2226   return xpaned->unmaximized_position.x;
2227 }
2228
2229 /* this call is private and only intended for internal use! */
2230 gint
2231 gtk_xpaned_fetch_unmaximized_y (GtkXPaned * xpaned)
2232 {
2233   return xpaned->unmaximized_position.y;
2234 }
2235
2236 /**
2237  * gtk_xpaned_get_top_left_child:
2238  * @xpaned: a #GtkXPaned widget
2239  * 
2240  * Obtains the top-left child of the xpaned widget.
2241  * 
2242  * Return value: top-left child, or %NULL if it is not set.
2243  *
2244  * Since: 2.4
2245  **/
2246 GtkWidget *
2247 gtk_xpaned_get_top_left_child (GtkXPaned * xpaned)
2248 {
2249   g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
2250
2251   return xpaned->top_left_child;
2252 }
2253
2254 /**
2255  * gtk_xpaned_get_top_right_child:
2256  * @xpaned: a #GtkXPaned widget
2257  * 
2258  * Obtains the top-right child of the xpaned widget.
2259  * 
2260  * Return value: top-right child, or %NULL if it is not set.
2261  *
2262  * Since: 2.4
2263  **/
2264 GtkWidget *
2265 gtk_xpaned_get_top_right_child (GtkXPaned * xpaned)
2266 {
2267   g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
2268
2269   return xpaned->top_right_child;
2270 }
2271
2272 /**
2273  * gtk_xpaned_get_bottom_left_child:
2274  * @xpaned: a #GtkXPaned widget
2275  * 
2276  * Obtains the bottom-left child of the xpaned widget.
2277  * 
2278  * Return value: bottom-left child, or %NULL if it is not set.
2279  *
2280  * Since: 2.4
2281  **/
2282 GtkWidget *
2283 gtk_xpaned_get_bottom_left_child (GtkXPaned * xpaned)
2284 {
2285   g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
2286
2287   return xpaned->bottom_left_child;
2288 }
2289
2290 /**
2291  * gtk_xpaned_get_bottom_right_child:
2292  * @xpaned: a #GtkXPaned widget
2293  * 
2294  * Obtains the bottom-right child of the xpaned widget.
2295  * 
2296  * Return value: bottom-right child, or %NULL if it is not set.
2297  *
2298  * Since: 2.4
2299  **/
2300 GtkWidget *
2301 gtk_xpaned_get_bottom_right_child (GtkXPaned * xpaned)
2302 {
2303   g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
2304
2305   return xpaned->bottom_right_child;
2306 }
2307
2308 gboolean
2309 gtk_xpaned_maximize_top_left (GtkXPaned * xpaned, gboolean maximize)
2310 {
2311   if (maximize)
2312     {
2313       /* see if any child is already maximized */
2314       if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
2315           !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
2316           !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
2317           !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2318         {
2319           /* save current position */
2320           gtk_xpaned_save_unmaximized_x (xpaned);
2321           gtk_xpaned_save_unmaximized_y (xpaned);
2322
2323           /* set new maximized position */
2324           gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x);
2325           gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y);
2326
2327           /* mark maximized flag for top-left child */
2328           xpaned->maximized[GTK_XPANED_TOP_LEFT] = TRUE;
2329
2330           return TRUE;
2331         }
2332       /* already one child maximized, report error */
2333       else
2334         return FALSE;
2335     }
2336   else
2337     {
2338       /* verify that top-left child is really currently maximized */
2339       if (xpaned->maximized[GTK_XPANED_TOP_LEFT])
2340         {
2341           /* clear maximized flat for top-left child */
2342           xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE;
2343
2344           /* restore unmaximized position */
2345           gtk_xpaned_set_position_x (xpaned,
2346                                      gtk_xpaned_fetch_unmaximized_x (xpaned));
2347           gtk_xpaned_set_position_y (xpaned,
2348                                      gtk_xpaned_fetch_unmaximized_y (xpaned));
2349
2350           return TRUE;
2351         }
2352       /* top-left child is currently not maximized, report error */
2353       else
2354         return FALSE;
2355     }
2356 }
2357
2358 gboolean
2359 gtk_xpaned_maximize_top_right (GtkXPaned * xpaned, gboolean maximize)
2360 {
2361   if (maximize)
2362     {
2363       /* see if any child is already maximized */
2364       if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
2365           !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
2366           !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
2367           !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2368         {
2369           /* save current position */
2370           gtk_xpaned_save_unmaximized_x (xpaned);
2371           gtk_xpaned_save_unmaximized_y (xpaned);
2372
2373           /* set new maximized position */
2374           gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x);
2375           gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y);
2376
2377           /* mark maximized flag for top-right child */
2378           xpaned->maximized[GTK_XPANED_TOP_RIGHT] = TRUE;
2379
2380           return TRUE;
2381         }
2382       /* already one child maximized, report error */
2383       else
2384         return FALSE;
2385     }
2386   else
2387     {
2388       /* verify that top-right child is really currently maximized */
2389       if (xpaned->maximized[GTK_XPANED_TOP_RIGHT])
2390         {
2391           /* clear maximized flat for top-right child */
2392           xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE;
2393
2394           /* restore unmaximized position */
2395           gtk_xpaned_set_position_x (xpaned,
2396                                      gtk_xpaned_fetch_unmaximized_x (xpaned));
2397           gtk_xpaned_set_position_y (xpaned,
2398                                      gtk_xpaned_fetch_unmaximized_y (xpaned));
2399
2400           return TRUE;
2401         }
2402       /* top-right child is currently not maximized, report error */
2403       else
2404         return FALSE;
2405     }
2406 }
2407
2408 gboolean
2409 gtk_xpaned_maximize_bottom_left (GtkXPaned * xpaned, gboolean maximize)
2410 {
2411   if (maximize)
2412     {
2413       /* see if any child is already maximized */
2414       if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
2415           !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
2416           !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
2417           !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2418         {
2419           /* save current position */
2420           gtk_xpaned_save_unmaximized_x (xpaned);
2421           gtk_xpaned_save_unmaximized_y (xpaned);
2422
2423           /* set new maximized position */
2424           gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x);
2425           gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y);
2426
2427           /* mark maximized flag for bottom-left child */
2428           xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = TRUE;
2429
2430           return TRUE;
2431         }
2432       /* already one child maximized, report error */
2433       else
2434         return FALSE;
2435     }
2436   else
2437     {
2438       /* verify that bottom-left child is really currently maximized */
2439       if (xpaned->maximized[GTK_XPANED_BOTTOM_LEFT])
2440         {
2441           /* clear maximized flat for bottom-left child */
2442           xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE;
2443
2444           /* restore unmaximized position */
2445           gtk_xpaned_set_position_x (xpaned,
2446                                      gtk_xpaned_fetch_unmaximized_x (xpaned));
2447           gtk_xpaned_set_position_y (xpaned,
2448                                      gtk_xpaned_fetch_unmaximized_y (xpaned));
2449
2450           return TRUE;
2451         }
2452       /* bottom-left child is currently not maximized, report error */
2453       else
2454         return FALSE;
2455     }
2456 }
2457
2458 gboolean
2459 gtk_xpaned_maximize_bottom_right (GtkXPaned * xpaned, gboolean maximize)
2460 {
2461   if (maximize)
2462     {
2463       /* see if any child is already maximized */
2464       if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
2465           !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
2466           !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
2467           !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2468         {
2469           /* save current position */
2470           gtk_xpaned_save_unmaximized_x (xpaned);
2471           gtk_xpaned_save_unmaximized_y (xpaned);
2472
2473           /* set new maximized position */
2474           gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x);
2475           gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y);
2476
2477           /* mark maximized flag for bottom-right child */
2478           xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = TRUE;
2479
2480           return TRUE;
2481         }
2482       /* already one child maximized, report error */
2483       else
2484         return FALSE;
2485     }
2486   else
2487     {
2488       /* verify that bottom-right child is really currently maximized */
2489       if (xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
2490         {
2491           /* clear maximized flat for bottom-right child */
2492           xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE;
2493
2494           /* restore unmaximized position */
2495           gtk_xpaned_set_position_x (xpaned,
2496                                      gtk_xpaned_fetch_unmaximized_x (xpaned));
2497           gtk_xpaned_set_position_y (xpaned,
2498                                      gtk_xpaned_fetch_unmaximized_y (xpaned));
2499
2500           return TRUE;
2501         }
2502       /* bottom-right child is currently not maximized, report error */
2503       else
2504         return FALSE;
2505     }
2506 }
2507
2508 void
2509 gtk_xpaned_compute_position (GtkXPaned * xpaned,
2510                              const GtkAllocation * allocation,
2511                              GtkRequisition * top_left_child_req,
2512                              GtkRequisition * top_right_child_req,
2513                              GtkRequisition * bottom_left_child_req,
2514                              GtkRequisition * bottom_right_child_req)
2515 {
2516   GdkPoint old_position;
2517   GdkPoint old_min_position;
2518   GdkPoint old_max_position;
2519   gint handle_size;
2520   gint border_width = gtk_container_get_border_width (GTK_CONTAINER (xpaned));
2521
2522   g_return_if_fail (GTK_IS_XPANED (xpaned));
2523
2524   old_position.x = xpaned->top_left_child_size.width;
2525   old_position.y = xpaned->top_left_child_size.height;
2526   old_min_position.x = xpaned->min_position.x;
2527   old_min_position.y = xpaned->min_position.y;
2528   old_max_position.x = xpaned->max_position.x;
2529   old_max_position.y = xpaned->max_position.y;
2530
2531   xpaned->min_position.x =
2532     xpaned->top_left_child_shrink ? 0 : top_left_child_req->width;
2533   xpaned->min_position.y =
2534     xpaned->top_left_child_shrink ? 0 : top_left_child_req->height;
2535
2536   gtk_widget_style_get (GTK_WIDGET (xpaned), "handle-size", &handle_size,
2537                         NULL);
2538
2539   xpaned->max_position.x = allocation->width - 2 * border_width - handle_size;
2540   xpaned->max_position.y =
2541     allocation->height - 2 * border_width - handle_size;
2542   if (!xpaned->top_left_child_shrink)
2543     xpaned->max_position.x =
2544       MAX (1, xpaned->max_position.x - top_left_child_req->width);
2545   xpaned->max_position.x =
2546     MAX (xpaned->min_position.x, xpaned->max_position.x);
2547
2548   if (!xpaned->position_set)
2549     {
2550       if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize)
2551         {
2552           xpaned->top_left_child_size.width =
2553             MAX (0, allocation->width - top_right_child_req->width);
2554           xpaned->top_left_child_size.height =
2555             MAX (0, allocation->height - top_right_child_req->height);
2556         }
2557       else if (!xpaned->top_left_child_resize
2558                && xpaned->top_right_child_resize)
2559         {
2560           xpaned->top_left_child_size.width = top_left_child_req->width;
2561           xpaned->top_left_child_size.height = top_left_child_req->height;
2562         }
2563       else
2564         {
2565           xpaned->top_left_child_size.width = allocation->width * 0.5 + 0.5;
2566           xpaned->top_left_child_size.height = allocation->height * 0.5 + 0.5;
2567         }
2568     }
2569   else
2570     {
2571       /* If the position was set before the initial allocation.
2572       ** (paned->last_allocation <= 0) just clamp it and leave it. */
2573       if (xpaned->last_allocation.width > 0)
2574         {
2575           if (xpaned->top_left_child_resize
2576               && !xpaned->top_right_child_resize)
2577             {
2578               xpaned->top_left_child_size.width += allocation->width
2579                 - xpaned->last_allocation.width;
2580
2581               xpaned->top_left_child_size.height += allocation->height
2582                 - xpaned->last_allocation.height;
2583             }
2584           else
2585             if (!
2586                 (!xpaned->top_left_child_resize
2587                  && xpaned->top_right_child_resize))
2588               {
2589                 xpaned->top_left_child_size.width = allocation->width
2590                   * ((gdouble) xpaned->top_left_child_size.width /
2591                      (xpaned->last_allocation.width)) + 0.5;
2592
2593                 xpaned->top_left_child_size.height = allocation->height
2594                   * ((gdouble) xpaned->top_left_child_size.height /
2595                      (xpaned->last_allocation.height)) + 0.5;
2596               }
2597         }
2598       if (xpaned->last_allocation.height > 0)
2599         {
2600           if (xpaned->top_left_child_resize
2601               && !xpaned->top_right_child_resize)
2602             {
2603               xpaned->top_left_child_size.width +=
2604                 allocation->width - xpaned->last_allocation.width;
2605               xpaned->top_left_child_size.height +=
2606                 allocation->height - xpaned->last_allocation.height;
2607             }
2608           else
2609             if (!
2610                 (!xpaned->top_left_child_resize
2611                  && xpaned->top_right_child_resize))
2612               {
2613                 xpaned->top_left_child_size.width =
2614                   allocation->width *
2615                   ((gdouble) xpaned->top_left_child_size.width /
2616                    (xpaned->last_allocation.width)) + 0.5;
2617                 xpaned->top_left_child_size.height =
2618                   allocation->height *
2619                   ((gdouble) xpaned->top_left_child_size.height /
2620                    (xpaned->last_allocation.height)) + 0.5;
2621               }
2622         }
2623
2624     }
2625
2626   xpaned->top_left_child_size.width =
2627     CLAMP (xpaned->top_left_child_size.width, xpaned->min_position.x,
2628            xpaned->max_position.x);
2629   xpaned->top_left_child_size.height =
2630     CLAMP (xpaned->top_left_child_size.height, xpaned->min_position.y,
2631            xpaned->max_position.y);
2632
2633   xpaned->top_right_child_size.width =
2634     CLAMP (xpaned->top_right_child_size.width, xpaned->min_position.x,
2635            xpaned->max_position.x);
2636   xpaned->top_right_child_size.height =
2637     CLAMP (xpaned->top_right_child_size.height, xpaned->min_position.y,
2638            xpaned->max_position.y);
2639
2640   xpaned->bottom_left_child_size.width =
2641     CLAMP (xpaned->bottom_left_child_size.width, xpaned->min_position.x,
2642            xpaned->max_position.x);
2643   xpaned->bottom_left_child_size.height =
2644     CLAMP (xpaned->bottom_left_child_size.height, xpaned->min_position.y,
2645            xpaned->max_position.y);
2646
2647   xpaned->bottom_right_child_size.width =
2648     CLAMP (xpaned->bottom_right_child_size.width, xpaned->min_position.x,
2649            xpaned->max_position.x);
2650   xpaned->bottom_right_child_size.height =
2651     CLAMP (xpaned->bottom_right_child_size.height, xpaned->min_position.y,
2652            xpaned->max_position.y);
2653
2654   gtk_widget_set_child_visible (xpaned->top_left_child, TRUE);
2655   gtk_widget_set_child_visible (xpaned->top_right_child, TRUE);
2656   gtk_widget_set_child_visible (xpaned->bottom_left_child, TRUE);
2657   gtk_widget_set_child_visible (xpaned->bottom_right_child, TRUE);
2658
2659   g_object_freeze_notify (G_OBJECT (xpaned));
2660
2661   if (xpaned->top_left_child_size.width != old_position.x)
2662     g_object_notify (G_OBJECT (xpaned), "x-position");
2663   if (xpaned->top_left_child_size.height != old_position.y)
2664     g_object_notify (G_OBJECT (xpaned), "y-position");
2665
2666   if (xpaned->top_right_child_size.width != old_position.x)
2667     g_object_notify (G_OBJECT (xpaned), "x-position");
2668   if (xpaned->top_right_child_size.height != old_position.y)
2669     g_object_notify (G_OBJECT (xpaned), "y-position");
2670
2671   if (xpaned->bottom_left_child_size.width != old_position.x)
2672     g_object_notify (G_OBJECT (xpaned), "x-position");
2673   if (xpaned->bottom_left_child_size.height != old_position.y)
2674     g_object_notify (G_OBJECT (xpaned), "y-position");
2675
2676   if (xpaned->bottom_right_child_size.width != old_position.x)
2677     g_object_notify (G_OBJECT (xpaned), "x-position");
2678   if (xpaned->bottom_right_child_size.height != old_position.y)
2679     g_object_notify (G_OBJECT (xpaned), "y-position");
2680
2681   if (xpaned->min_position.x != old_min_position.x)
2682     g_object_notify (G_OBJECT (xpaned), "min-x-position");
2683   if (xpaned->min_position.y != old_min_position.y)
2684     g_object_notify (G_OBJECT (xpaned), "min-y-position");
2685
2686   if (xpaned->max_position.x != old_max_position.x)
2687     g_object_notify (G_OBJECT (xpaned), "max-y-position");
2688   if (xpaned->max_position.y != old_max_position.y)
2689     g_object_notify (G_OBJECT (xpaned), "max-y-position");
2690
2691   g_object_thaw_notify (G_OBJECT (xpaned));
2692
2693   xpaned->last_allocation.width = allocation->width;
2694   xpaned->last_allocation.height = allocation->height;
2695 }
2696
2697 static void
2698 gtk_xpaned_set_saved_focus (GtkXPaned * xpaned, GtkWidget * widget)
2699 {
2700   if (xpaned->priv->saved_focus)
2701     g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->saved_focus),
2702                                   (gpointer *) & (xpaned->priv->saved_focus));
2703
2704   xpaned->priv->saved_focus = widget;
2705
2706   if (xpaned->priv->saved_focus)
2707     g_object_add_weak_pointer (G_OBJECT (xpaned->priv->saved_focus),
2708                                (gpointer *) & (xpaned->priv->saved_focus));
2709 }
2710
2711 static void
2712 gtk_xpaned_set_first_xpaned (GtkXPaned * xpaned, GtkXPaned * first_xpaned)
2713 {
2714   if (xpaned->priv->first_xpaned)
2715     g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned),
2716                                   (gpointer *) & (xpaned->priv->
2717                                                   first_xpaned));
2718
2719   xpaned->priv->first_xpaned = first_xpaned;
2720
2721   if (xpaned->priv->first_xpaned)
2722     g_object_add_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned),
2723                                (gpointer *) & (xpaned->priv->first_xpaned));
2724 }
2725
2726 static void
2727 gtk_xpaned_set_last_top_left_child_focus (GtkXPaned * xpaned,
2728                                           GtkWidget * widget)
2729 {
2730   if (xpaned->last_top_left_child_focus)
2731     g_object_remove_weak_pointer (G_OBJECT
2732                                   (xpaned->last_top_left_child_focus),
2733                                   (gpointer *) & (xpaned->
2734                                                   last_top_left_child_focus));
2735
2736   xpaned->last_top_left_child_focus = widget;
2737
2738   if (xpaned->last_top_left_child_focus)
2739     g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_left_child_focus),
2740                                (gpointer *) & (xpaned->
2741                                                last_top_left_child_focus));
2742 }
2743
2744 static void
2745 gtk_xpaned_set_last_top_right_child_focus (GtkXPaned * xpaned,
2746                                            GtkWidget * widget)
2747 {
2748   if (xpaned->last_top_right_child_focus)
2749     g_object_remove_weak_pointer (G_OBJECT
2750                                   (xpaned->last_top_right_child_focus),
2751                                   (gpointer *) & (xpaned->
2752                                                   last_top_right_child_focus));
2753
2754   xpaned->last_top_right_child_focus = widget;
2755
2756   if (xpaned->last_top_right_child_focus)
2757     g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_right_child_focus),
2758                                (gpointer *) & (xpaned->
2759                                                last_top_right_child_focus));
2760 }
2761
2762 static void
2763 gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned * xpaned,
2764                                              GtkWidget * widget)
2765 {
2766   if (xpaned->last_bottom_left_child_focus)
2767     g_object_remove_weak_pointer (G_OBJECT
2768                                   (xpaned->last_bottom_left_child_focus),
2769                                   (gpointer *) & (xpaned->
2770                                                   last_bottom_left_child_focus));
2771
2772   xpaned->last_bottom_left_child_focus = widget;
2773
2774   if (xpaned->last_bottom_left_child_focus)
2775     g_object_add_weak_pointer (G_OBJECT
2776                                (xpaned->last_bottom_left_child_focus),
2777                                (gpointer *) & (xpaned->
2778                                                last_bottom_left_child_focus));
2779 }
2780
2781 static void
2782 gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned * xpaned,
2783                                               GtkWidget * widget)
2784 {
2785   if (xpaned->last_bottom_right_child_focus)
2786     g_object_remove_weak_pointer (G_OBJECT
2787                                   (xpaned->last_bottom_right_child_focus),
2788                                   (gpointer *) & (xpaned->
2789                                                   last_bottom_right_child_focus));
2790
2791   xpaned->last_bottom_right_child_focus = widget;
2792
2793   if (xpaned->last_bottom_right_child_focus)
2794     g_object_add_weak_pointer (G_OBJECT
2795                                (xpaned->last_bottom_right_child_focus),
2796                                (gpointer *) & (xpaned->
2797                                                last_bottom_right_child_focus));
2798 }
2799
2800 static GtkWidget *
2801 xpaned_get_focus_widget (GtkXPaned * xpaned)
2802 {
2803   GtkWidget *toplevel;
2804
2805   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned));
2806   if (gtk_widget_is_toplevel (toplevel))
2807     return gtk_window_get_focus (GTK_WINDOW (toplevel));
2808
2809   return NULL;
2810 }
2811
2812 static void
2813 gtk_xpaned_set_focus_child (GtkContainer * container, GtkWidget * focus_child)
2814 {
2815   GtkXPaned *xpaned;
2816
2817   g_return_if_fail (GTK_IS_XPANED (container));
2818
2819   xpaned = GTK_XPANED (container);
2820
2821   if (focus_child == NULL)
2822     {
2823       GtkWidget *last_focus;
2824       GtkWidget *w;
2825
2826       last_focus = xpaned_get_focus_widget (xpaned);
2827
2828       if (last_focus)
2829         {
2830           /* If there is one or more paned widgets between us and the
2831            * focus widget, we want the topmost of those as last_focus
2832            */
2833           for (w = last_focus; w != GTK_WIDGET (xpaned); w = gtk_widget_get_parent (w))
2834             if (GTK_IS_XPANED (w))
2835               last_focus = w;
2836
2837           if (gtk_container_get_focus_child (container) == xpaned->top_left_child)
2838             gtk_xpaned_set_last_top_left_child_focus (xpaned, last_focus);
2839           else if (gtk_container_get_focus_child (container) == xpaned->top_right_child)
2840             gtk_xpaned_set_last_top_right_child_focus (xpaned, last_focus);
2841           else if (gtk_container_get_focus_child (container) == xpaned->bottom_left_child)
2842             gtk_xpaned_set_last_bottom_left_child_focus (xpaned, last_focus);
2843           else if (gtk_container_get_focus_child (container) == xpaned->bottom_right_child)
2844             gtk_xpaned_set_last_bottom_right_child_focus (xpaned, last_focus);
2845         }
2846     }
2847
2848   if (parent_class->set_focus_child)
2849     (*parent_class->set_focus_child) (container, focus_child);
2850 }
2851
2852 static void
2853 gtk_xpaned_get_cycle_chain (GtkXPaned * xpaned,
2854                             GtkDirectionType direction, GList ** widgets)
2855 {
2856   GtkContainer *container = GTK_CONTAINER (xpaned);
2857   GtkWidget *ancestor = NULL;
2858   GList *temp_list = NULL;
2859   GList *list;
2860
2861   if (xpaned->in_recursion)
2862     return;
2863
2864   g_assert (widgets != NULL);
2865
2866   if (xpaned->last_top_left_child_focus &&
2867       !gtk_widget_is_ancestor (xpaned->last_top_left_child_focus,
2868                                GTK_WIDGET (xpaned)))
2869     {
2870       gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL);
2871     }
2872
2873   if (xpaned->last_top_right_child_focus &&
2874       !gtk_widget_is_ancestor (xpaned->last_top_right_child_focus,
2875                                GTK_WIDGET (xpaned)))
2876     {
2877       gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL);
2878     }
2879
2880   if (xpaned->last_bottom_left_child_focus &&
2881       !gtk_widget_is_ancestor (xpaned->last_bottom_left_child_focus,
2882                                GTK_WIDGET (xpaned)))
2883     {
2884       gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL);
2885     }
2886
2887   if (xpaned->last_bottom_right_child_focus &&
2888       !gtk_widget_is_ancestor (xpaned->last_bottom_right_child_focus,
2889                                GTK_WIDGET (xpaned)))
2890     {
2891       gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL);
2892     }
2893
2894   if (gtk_widget_get_parent (GTK_WIDGET (xpaned)))
2895     ancestor = gtk_widget_get_ancestor (gtk_widget_get_parent (GTK_WIDGET (xpaned)),
2896                                         GTK_TYPE_XPANED);
2897
2898   /* The idea here is that temp_list is a list of widgets we want to cycle
2899    * to. The list is prioritized so that the first element is our first
2900    * choice, the next our second, and so on.
2901    *
2902    * We can't just use g_list_reverse(), because we want to try
2903    * paned->last_child?_focus before paned->child?, both when we
2904    * are going forward and backward.
2905    */
2906   if (direction == GTK_DIR_TAB_FORWARD)
2907     {
2908       if (gtk_container_get_focus_child (container) == xpaned->top_left_child)
2909         {
2910           temp_list =
2911             g_list_append (temp_list, xpaned->last_top_right_child_focus);
2912           temp_list = g_list_append (temp_list, xpaned->top_right_child);
2913           temp_list = g_list_append (temp_list, ancestor);
2914         }
2915       else if (gtk_container_get_focus_child (container) == xpaned->top_right_child)
2916         {
2917           temp_list = g_list_append (temp_list, ancestor);
2918           temp_list =
2919             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2920           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2921         }
2922       else if (gtk_container_get_focus_child (container) == xpaned->bottom_left_child)
2923         {
2924           temp_list = g_list_append (temp_list, ancestor);
2925           temp_list =
2926             g_list_append (temp_list, xpaned->last_bottom_right_child_focus);
2927           temp_list = g_list_append (temp_list, xpaned->bottom_right_child);
2928         }
2929       else if (gtk_container_get_focus_child (container) == xpaned->bottom_right_child)
2930         {
2931           temp_list = g_list_append (temp_list, ancestor);
2932           temp_list =
2933             g_list_append (temp_list, xpaned->last_top_left_child_focus);
2934           temp_list = g_list_append (temp_list, xpaned->top_left_child);
2935         }
2936       else
2937         {
2938           temp_list =
2939             g_list_append (temp_list, xpaned->last_top_left_child_focus);
2940           temp_list = g_list_append (temp_list, xpaned->top_left_child);
2941           temp_list =
2942             g_list_append (temp_list, xpaned->last_top_right_child_focus);
2943           temp_list = g_list_append (temp_list, xpaned->top_right_child);
2944           temp_list =
2945             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2946           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2947           temp_list =
2948             g_list_append (temp_list, xpaned->last_bottom_right_child_focus);
2949           temp_list = g_list_append (temp_list, xpaned->bottom_right_child);
2950           temp_list = g_list_append (temp_list, ancestor);
2951         }
2952     }
2953   else
2954     {
2955       if (gtk_container_get_focus_child (container) == xpaned->top_left_child)
2956         {
2957           temp_list = g_list_append (temp_list, ancestor);
2958           temp_list =
2959             g_list_append (temp_list, xpaned->last_top_right_child_focus);
2960           temp_list = g_list_append (temp_list, xpaned->top_right_child);
2961         }
2962       else if (gtk_container_get_focus_child (container) == xpaned->top_right_child)
2963         {
2964           temp_list =
2965             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2966           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2967           temp_list = g_list_append (temp_list, ancestor);
2968         }
2969       else if (gtk_container_get_focus_child (container) == xpaned->bottom_right_child)
2970         {
2971           temp_list =
2972             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2973           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2974           temp_list = g_list_append (temp_list, ancestor);
2975         }
2976       else if (gtk_container_get_focus_child (container) == xpaned->top_right_child)
2977         {
2978           temp_list =
2979             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2980           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2981           temp_list = g_list_append (temp_list, ancestor);
2982         }
2983       else
2984         {
2985           temp_list =
2986             g_list_append (temp_list, xpaned->last_bottom_right_child_focus);
2987           temp_list = g_list_append (temp_list, xpaned->bottom_right_child);
2988           temp_list =
2989             g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
2990           temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
2991           temp_list =
2992             g_list_append (temp_list, xpaned->last_top_right_child_focus);
2993           temp_list = g_list_append (temp_list, xpaned->top_right_child);
2994           temp_list =
2995             g_list_append (temp_list, xpaned->last_top_left_child_focus);
2996           temp_list = g_list_append (temp_list, xpaned->top_left_child);
2997           temp_list = g_list_append (temp_list, ancestor);
2998         }
2999     }
3000
3001   /* Walk the list and expand all the paned widgets. */
3002   for (list = temp_list; list != NULL; list = list->next)
3003     {
3004       GtkWidget *widget = list->data;
3005
3006       if (widget)
3007         {
3008           if (GTK_IS_XPANED (widget))
3009             {
3010               xpaned->in_recursion = TRUE;
3011               gtk_xpaned_get_cycle_chain (GTK_XPANED (widget),
3012                                           direction, widgets);
3013               xpaned->in_recursion = FALSE;
3014             }
3015           else
3016             {
3017               *widgets = g_list_append (*widgets, widget);
3018             }
3019         }
3020     }
3021
3022   g_list_free (temp_list);
3023 }
3024
3025 static gboolean
3026 gtk_xpaned_cycle_child_focus (GtkXPaned * xpaned, gboolean reversed)
3027 {
3028   GList *cycle_chain = NULL;
3029   GList *list;
3030
3031   GtkDirectionType direction =
3032     reversed ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD;
3033
3034   /* ignore f6 if the handle is focused */
3035   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3036     return TRUE;
3037
3038   /* we can't just let the event propagate up the hierarchy,
3039    * because the paned will want to cycle focus _unless_ an
3040    * ancestor paned handles the event
3041    */
3042   gtk_xpaned_get_cycle_chain (xpaned, direction, &cycle_chain);
3043
3044   for (list = cycle_chain; list != NULL; list = list->next)
3045     if (gtk_widget_child_focus (GTK_WIDGET (list->data), direction))
3046       break;
3047
3048   g_list_free (cycle_chain);
3049
3050   return TRUE;
3051 }
3052
3053 static void
3054 get_child_xpanes (GtkWidget * widget, GList ** xpanes)
3055 {
3056   if (GTK_IS_XPANED (widget))
3057     {
3058       GtkXPaned *xpaned = GTK_XPANED (widget);
3059
3060       get_child_xpanes (xpaned->top_left_child, xpanes);
3061       *xpanes = g_list_prepend (*xpanes, widget);
3062       get_child_xpanes (xpaned->top_right_child, xpanes);
3063       *xpanes = g_list_prepend (*xpanes, widget);
3064       get_child_xpanes (xpaned->bottom_left_child, xpanes);
3065       *xpanes = g_list_prepend (*xpanes, widget);
3066       get_child_xpanes (xpaned->bottom_right_child, xpanes);
3067     }
3068   else if (GTK_IS_CONTAINER (widget))
3069     {
3070       gtk_container_foreach (GTK_CONTAINER (widget),
3071                              (GtkCallback) get_child_xpanes, xpanes);
3072     }
3073 }
3074
3075 static GList *
3076 get_all_xpanes (GtkXPaned * xpaned)
3077 {
3078   GtkXPaned *topmost = NULL;
3079   GList *result = NULL;
3080   GtkWidget *w;
3081
3082   for (w = GTK_WIDGET (xpaned); w != NULL; w = gtk_widget_get_parent (w))
3083     {
3084       if (GTK_IS_XPANED (w))
3085         topmost = GTK_XPANED (w);
3086     }
3087
3088   g_assert (topmost);
3089
3090   get_child_xpanes (GTK_WIDGET (topmost), &result);
3091
3092   return g_list_reverse (result);
3093 }
3094
3095 static void
3096 gtk_xpaned_find_neighbours (GtkXPaned * xpaned,
3097                             GtkXPaned ** next, GtkXPaned ** prev)
3098 {
3099   GList *all_xpanes;
3100   GList *this_link;
3101
3102   all_xpanes = get_all_xpanes (xpaned);
3103   g_assert (all_xpanes);
3104
3105   this_link = g_list_find (all_xpanes, xpaned);
3106
3107   g_assert (this_link);
3108
3109   if (this_link->next)
3110     *next = this_link->next->data;
3111   else
3112     *next = all_xpanes->data;
3113
3114   if (this_link->prev)
3115     *prev = this_link->prev->data;
3116   else
3117     *prev = g_list_last (all_xpanes)->data;
3118
3119   g_list_free (all_xpanes);
3120 }
3121
3122 static gboolean
3123 gtk_xpaned_move_handle (GtkXPaned * xpaned, GtkScrollType scroll)
3124 {
3125   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3126     {
3127       GdkPoint old_position;
3128       GdkPoint new_position;
3129       gint increment;
3130
3131       enum
3132       {
3133         SINGLE_STEP_SIZE = 1,
3134         PAGE_STEP_SIZE = 75
3135       };
3136
3137       new_position.x = old_position.x = gtk_xpaned_get_position_x (xpaned);
3138       new_position.y = old_position.y = gtk_xpaned_get_position_y (xpaned);
3139       increment = 0;
3140
3141       switch (scroll)
3142         {
3143         case GTK_SCROLL_STEP_LEFT:
3144         case GTK_SCROLL_STEP_UP:
3145         case GTK_SCROLL_STEP_BACKWARD:
3146           increment = -SINGLE_STEP_SIZE;
3147           break;
3148
3149         case GTK_SCROLL_STEP_RIGHT:
3150         case GTK_SCROLL_STEP_DOWN:
3151         case GTK_SCROLL_STEP_FORWARD:
3152           increment = SINGLE_STEP_SIZE;
3153           break;
3154
3155         case GTK_SCROLL_PAGE_LEFT:
3156         case GTK_SCROLL_PAGE_UP:
3157         case GTK_SCROLL_PAGE_BACKWARD:
3158           increment = -PAGE_STEP_SIZE;
3159           break;
3160
3161         case GTK_SCROLL_PAGE_RIGHT:
3162         case GTK_SCROLL_PAGE_DOWN:
3163         case GTK_SCROLL_PAGE_FORWARD:
3164           increment = PAGE_STEP_SIZE;
3165           break;
3166
3167         case GTK_SCROLL_START:
3168           new_position.x = xpaned->min_position.x;
3169           new_position.y = xpaned->min_position.y;
3170           break;
3171
3172         case GTK_SCROLL_END:
3173           new_position.x = xpaned->max_position.x;
3174           new_position.y = xpaned->max_position.y;
3175           break;
3176
3177         default:
3178           break;
3179         }
3180
3181       if (increment)
3182         {
3183           if (is_rtl (xpaned))
3184             increment = -increment;
3185
3186           new_position.x = old_position.x + increment;
3187           new_position.y = old_position.y + increment;
3188         }
3189
3190       new_position.x = CLAMP (new_position.x,
3191                               xpaned->min_position.x, xpaned->max_position.x);
3192
3193       new_position.y = CLAMP (new_position.y,
3194                               xpaned->min_position.y, xpaned->max_position.y);
3195
3196       if (old_position.x != new_position.x)
3197         gtk_xpaned_set_position_x (xpaned, new_position.x);
3198
3199       if (old_position.y != new_position.y)
3200         gtk_xpaned_set_position_y (xpaned, new_position.y);
3201
3202       return TRUE;
3203     }
3204
3205   return FALSE;
3206 }
3207
3208 static void
3209 gtk_xpaned_restore_focus (GtkXPaned * xpaned)
3210 {
3211   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3212     {
3213       if (xpaned->priv->saved_focus &&
3214           gtk_widget_get_sensitive (xpaned->priv->saved_focus))
3215         {
3216           gtk_widget_grab_focus (xpaned->priv->saved_focus);
3217         }
3218       else
3219         {
3220           /* the saved focus is somehow not available for focusing,
3221            * try
3222            *   1) tabbing into the paned widget
3223            * if that didn't work,
3224            *   2) unset focus for the window if there is one
3225            */
3226
3227           if (!gtk_widget_child_focus
3228               (GTK_WIDGET (xpaned), GTK_DIR_TAB_FORWARD))
3229             {
3230               GtkWidget *toplevel =
3231                 gtk_widget_get_toplevel (GTK_WIDGET (xpaned));
3232
3233               if (GTK_IS_WINDOW (toplevel))
3234                 gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
3235             }
3236         }
3237
3238       gtk_xpaned_set_saved_focus (xpaned, NULL);
3239       gtk_xpaned_set_first_xpaned (xpaned, NULL);
3240     }
3241 }
3242
3243 static gboolean
3244 gtk_xpaned_accept_position (GtkXPaned * xpaned)
3245 {
3246   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3247     {
3248       xpaned->original_position.x = -1;
3249       xpaned->original_position.y = -1;
3250       gtk_xpaned_restore_focus (xpaned);
3251
3252       return TRUE;
3253     }
3254
3255   return FALSE;
3256 }
3257
3258 static gboolean
3259 gtk_xpaned_cancel_position (GtkXPaned * xpaned)
3260 {
3261   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3262     {
3263       if (xpaned->original_position.x != -1)
3264         {
3265           gtk_xpaned_set_position_x (xpaned, xpaned->original_position.x);
3266           xpaned->original_position.x = -1;
3267         }
3268
3269       if (xpaned->original_position.y != -1)
3270         {
3271           gtk_xpaned_set_position_y (xpaned, xpaned->original_position.y);
3272           xpaned->original_position.y = -1;
3273         }
3274
3275       gtk_xpaned_restore_focus (xpaned);
3276       return TRUE;
3277     }
3278
3279   return FALSE;
3280 }
3281
3282 static gboolean
3283 gtk_xpaned_cycle_handle_focus (GtkXPaned * xpaned, gboolean reversed)
3284 {
3285   GtkXPaned *next;
3286   GtkXPaned *prev;
3287
3288   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3289     {
3290       GtkXPaned *focus = NULL;
3291
3292       if (!xpaned->priv->first_xpaned)
3293         {
3294           /* The first_pane has disappeared. As an ad-hoc solution,
3295            * we make the currently focused paned the first_paned. To the
3296            * user this will seem like the paned cycling has been reset.
3297            */
3298           gtk_xpaned_set_first_xpaned (xpaned, xpaned);
3299         }
3300
3301       gtk_xpaned_find_neighbours (xpaned, &next, &prev);
3302
3303       if (reversed && prev &&
3304           prev != xpaned && xpaned != xpaned->priv->first_xpaned)
3305         {
3306           focus = prev;
3307         }
3308       else if (!reversed &&
3309                next && next != xpaned && next != xpaned->priv->first_xpaned)
3310         {
3311           focus = next;
3312         }
3313       else
3314         {
3315           gtk_xpaned_accept_position (xpaned);
3316           return TRUE;
3317         }
3318
3319       g_assert (focus);
3320
3321       gtk_xpaned_set_saved_focus (focus, xpaned->priv->saved_focus);
3322       gtk_xpaned_set_first_xpaned (focus, xpaned->priv->first_xpaned);
3323
3324       gtk_xpaned_set_saved_focus (xpaned, NULL);
3325       gtk_xpaned_set_first_xpaned (xpaned, NULL);
3326
3327       gtk_widget_grab_focus (GTK_WIDGET (focus));
3328
3329       if (!gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3330         {
3331           xpaned->original_position.x = -1;
3332           xpaned->original_position.y = -1;
3333           focus->original_position.x = gtk_xpaned_get_position_x (focus);
3334           focus->original_position.y = gtk_xpaned_get_position_y (focus);
3335         }
3336     }
3337   else
3338     {
3339       GtkContainer *container = GTK_CONTAINER (xpaned);
3340       GtkXPaned *focus;
3341       GtkXPaned *first;
3342       GtkXPaned *prev;
3343       GtkXPaned *next;
3344       GtkWidget *toplevel;
3345
3346       gtk_xpaned_find_neighbours (xpaned, &next, &prev);
3347
3348       if (gtk_container_get_focus_child (container) == xpaned->top_left_child)
3349         {
3350           if (reversed)
3351             {
3352               focus = prev;
3353               first = xpaned;
3354             }
3355           else
3356             {
3357               focus = xpaned;
3358               first = xpaned;
3359             }
3360         }
3361       else if (gtk_container_get_focus_child (container) == xpaned->top_right_child)
3362         {
3363           if (reversed)
3364             {
3365               focus = xpaned;
3366               first = next;
3367             }
3368           else
3369             {
3370               focus = next;
3371               first = next;
3372             }
3373         }
3374       else
3375         {
3376           /* Focus is not inside this xpaned, and we don't have focus.
3377            * Presumably this happened because the application wants us
3378            * to start keyboard navigating.
3379            */
3380           focus = xpaned;
3381
3382           if (reversed)
3383             first = xpaned;
3384           else
3385             first = next;
3386         }
3387
3388       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned));
3389
3390       if (GTK_IS_WINDOW (toplevel))
3391         gtk_xpaned_set_saved_focus (focus,
3392                                     gtk_window_get_focus (GTK_WINDOW (toplevel)));
3393       gtk_xpaned_set_first_xpaned (focus, first);
3394       focus->original_position.x = gtk_xpaned_get_position_x (focus);
3395       focus->original_position.y = gtk_xpaned_get_position_y (focus);
3396
3397       gtk_widget_grab_focus (GTK_WIDGET (focus));
3398     }
3399
3400   return TRUE;
3401 }
3402
3403 static gboolean
3404 gtk_xpaned_toggle_handle_focus (GtkXPaned * xpaned)
3405 {
3406   /* This function/signal has the wrong name. It is called when you
3407    * press Tab or Shift-Tab and what we do is act as if
3408    * the user pressed Return and then Tab or Shift-Tab
3409    */
3410   if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
3411     gtk_xpaned_accept_position (xpaned);
3412
3413   return FALSE;
3414 }
3415
3416 /*#define __GTK_XPANED_C__*/
3417 /*#include "gtkaliasdef.c"*/