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