]> Pileus Git - ~andy/gtk/blob - gtk/gtkpaned.c
Define macros GTK_PARAM_READABLE, GTK_PARAM_WRITABLE, GTK_PARAM_READWRITE
[~andy/gtk] / gtk / gtkpaned.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <config.h>
28 #include "gtkintl.h"
29 #include "gtkpaned.h"
30 #include "gtkbindings.h"
31 #include "gtksignal.h"
32 #include "gdk/gdkkeysyms.h"
33 #include "gtkwindow.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkprivate.h"
37 #include "gtkalias.h"
38
39 enum {
40   PROP_0,
41   PROP_POSITION,
42   PROP_POSITION_SET,
43   PROP_MIN_POSITION,
44   PROP_MAX_POSITION
45 };
46
47 enum {
48   CHILD_PROP_0,
49   CHILD_PROP_RESIZE,
50   CHILD_PROP_SHRINK
51 };
52
53 enum {
54   CYCLE_CHILD_FOCUS,
55   TOGGLE_HANDLE_FOCUS,
56   MOVE_HANDLE,
57   CYCLE_HANDLE_FOCUS,
58   ACCEPT_POSITION,
59   CANCEL_POSITION,
60   LAST_SIGNAL
61 };
62
63 static void     gtk_paned_class_init            (GtkPanedClass    *klass);
64 static void     gtk_paned_init                  (GtkPaned         *paned);
65 static void     gtk_paned_set_property          (GObject          *object,
66                                                  guint             prop_id,
67                                                  const GValue     *value,
68                                                  GParamSpec       *pspec);
69 static void     gtk_paned_get_property          (GObject          *object,
70                                                  guint             prop_id,
71                                                  GValue           *value,
72                                                  GParamSpec       *pspec);
73 static void gtk_paned_set_child_property        (GtkContainer      *container,
74                                                  GtkWidget         *child,
75                                                  guint              property_id,
76                                                  const GValue      *value,
77                                                  GParamSpec        *pspec);
78 static void gtk_paned_get_child_property        (GtkContainer      *container,
79                                                  GtkWidget         *child,
80                                                  guint              property_id,
81                                                  GValue            *value,
82                                                  GParamSpec        *pspec);
83 static void     gtk_paned_finalize              (GObject          *object);
84 static void     gtk_paned_realize               (GtkWidget        *widget);
85 static void     gtk_paned_unrealize             (GtkWidget        *widget);
86 static void     gtk_paned_map                   (GtkWidget        *widget);
87 static void     gtk_paned_unmap                 (GtkWidget        *widget);
88 static gboolean gtk_paned_expose                (GtkWidget        *widget,
89                                                  GdkEventExpose   *event);
90 static gboolean gtk_paned_enter                 (GtkWidget        *widget,
91                                                  GdkEventCrossing *event);
92 static gboolean gtk_paned_leave                 (GtkWidget        *widget,
93                                                  GdkEventCrossing *event);
94 static gboolean gtk_paned_button_press          (GtkWidget        *widget,
95                                                  GdkEventButton   *event);
96 static gboolean gtk_paned_button_release        (GtkWidget        *widget,
97                                                  GdkEventButton   *event);
98 static gboolean gtk_paned_motion                (GtkWidget        *widget,
99                                                  GdkEventMotion   *event);
100 static gboolean gtk_paned_focus                 (GtkWidget        *widget,
101                                                  GtkDirectionType  direction);
102 static void     gtk_paned_add                   (GtkContainer     *container,
103                                                  GtkWidget        *widget);
104 static void     gtk_paned_remove                (GtkContainer     *container,
105                                                  GtkWidget        *widget);
106 static void     gtk_paned_forall                (GtkContainer     *container,
107                                                  gboolean          include_internals,
108                                                  GtkCallback       callback,
109                                                  gpointer          callback_data);
110 static void     gtk_paned_set_focus_child       (GtkContainer     *container,
111                                                  GtkWidget        *child);
112 static void     gtk_paned_set_saved_focus       (GtkPaned         *paned,
113                                                  GtkWidget        *widget);
114 static void     gtk_paned_set_first_paned       (GtkPaned         *paned,
115                                                  GtkPaned         *first_paned);
116 static void     gtk_paned_set_last_child1_focus (GtkPaned         *paned,
117                                                  GtkWidget        *widget);
118 static void     gtk_paned_set_last_child2_focus (GtkPaned         *paned,
119                                                  GtkWidget        *widget);
120 static gboolean gtk_paned_cycle_child_focus     (GtkPaned         *paned,
121                                                  gboolean          reverse);
122 static gboolean gtk_paned_cycle_handle_focus    (GtkPaned         *paned,
123                                                  gboolean          reverse);
124 static gboolean gtk_paned_move_handle           (GtkPaned         *paned,
125                                                  GtkScrollType     scroll);
126 static gboolean gtk_paned_accept_position       (GtkPaned         *paned);
127 static gboolean gtk_paned_cancel_position       (GtkPaned         *paned);
128 static gboolean gtk_paned_toggle_handle_focus   (GtkPaned         *paned);
129
130 static GType    gtk_paned_child_type             (GtkContainer     *container);
131
132 static GtkContainerClass *parent_class = NULL;
133
134 struct _GtkPanedPrivate
135 {
136   GtkWidget *saved_focus;
137   GtkPaned *first_paned;
138 };
139
140 GType
141 gtk_paned_get_type (void)
142 {
143   static GType paned_type = 0;
144   
145   if (!paned_type)
146     {
147       static const GTypeInfo paned_info =
148       {
149         sizeof (GtkPanedClass),
150         NULL,           /* base_init */
151         NULL,           /* base_finalize */
152         (GClassInitFunc) gtk_paned_class_init,
153         NULL,           /* class_finalize */
154         NULL,           /* class_data */
155         sizeof (GtkPaned),
156         0,              /* n_preallocs */
157         (GInstanceInitFunc) gtk_paned_init,
158         NULL,           /* value_table */
159       };
160
161       paned_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkPaned",
162                                            &paned_info, G_TYPE_FLAG_ABSTRACT);
163     }
164   
165   return paned_type;
166 }
167
168 static guint signals[LAST_SIGNAL] = { 0 };
169
170 static void
171 add_tab_bindings (GtkBindingSet    *binding_set,
172                   GdkModifierType   modifiers)
173 {
174   gtk_binding_entry_add_signal (binding_set, GDK_Tab, modifiers,
175                                 "toggle_handle_focus", 0);
176   gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, modifiers,
177                                 "toggle_handle_focus", 0);
178 }
179
180 static void
181 add_move_binding (GtkBindingSet   *binding_set,
182                   guint            keyval,
183                   GdkModifierType  mask,
184                   GtkScrollType    scroll)
185 {
186   gtk_binding_entry_add_signal (binding_set, keyval, mask,
187                                 "move_handle", 1,
188                                 GTK_TYPE_SCROLL_TYPE, scroll);
189 }
190
191 static void
192 gtk_paned_class_init (GtkPanedClass *class)
193 {
194   GObjectClass *object_class;
195   GtkWidgetClass *widget_class;
196   GtkContainerClass *container_class;
197   GtkPanedClass *paned_class;
198   GtkBindingSet *binding_set;
199
200   object_class = (GObjectClass *) class;
201   widget_class = (GtkWidgetClass *) class;
202   container_class = (GtkContainerClass *) class;
203   paned_class = (GtkPanedClass *) class;
204
205   parent_class = g_type_class_peek_parent (class);
206
207   object_class->set_property = gtk_paned_set_property;
208   object_class->get_property = gtk_paned_get_property;
209   object_class->finalize = gtk_paned_finalize;
210
211   widget_class->realize = gtk_paned_realize;
212   widget_class->unrealize = gtk_paned_unrealize;
213   widget_class->map = gtk_paned_map;
214   widget_class->unmap = gtk_paned_unmap;
215   widget_class->expose_event = gtk_paned_expose;
216   widget_class->focus = gtk_paned_focus;
217   widget_class->enter_notify_event = gtk_paned_enter;
218   widget_class->leave_notify_event = gtk_paned_leave;
219   widget_class->button_press_event = gtk_paned_button_press;
220   widget_class->button_release_event = gtk_paned_button_release;
221   widget_class->motion_notify_event = gtk_paned_motion;
222   
223   container_class->add = gtk_paned_add;
224   container_class->remove = gtk_paned_remove;
225   container_class->forall = gtk_paned_forall;
226   container_class->child_type = gtk_paned_child_type;
227   container_class->set_focus_child = gtk_paned_set_focus_child;
228   container_class->set_child_property = gtk_paned_set_child_property;
229   container_class->get_child_property = gtk_paned_get_child_property;
230
231   paned_class->cycle_child_focus = gtk_paned_cycle_child_focus;
232   paned_class->toggle_handle_focus = gtk_paned_toggle_handle_focus;
233   paned_class->move_handle = gtk_paned_move_handle;
234   paned_class->cycle_handle_focus = gtk_paned_cycle_handle_focus;
235   paned_class->accept_position = gtk_paned_accept_position;
236   paned_class->cancel_position = gtk_paned_cancel_position;
237
238   g_object_class_install_property (object_class,
239                                    PROP_POSITION,
240                                    g_param_spec_int ("position",
241                                                      P_("Position"),
242                                                      P_("Position of paned separator in pixels (0 means all the way to the left/top)"),
243                                                      0,
244                                                      G_MAXINT,
245                                                      0,
246                                                      GTK_PARAM_READWRITE));
247   g_object_class_install_property (object_class,
248                                    PROP_POSITION_SET,
249                                    g_param_spec_boolean ("position-set",
250                                                          P_("Position Set"),
251                                                          P_("TRUE if the Position property should be used"),
252                                                          FALSE,
253                                                          GTK_PARAM_READWRITE));
254                                    
255   gtk_widget_class_install_style_property (widget_class,
256                                            g_param_spec_int ("handle-size",
257                                                              P_("Handle Size"),
258                                                              P_("Width of handle"),
259                                                              0,
260                                                              G_MAXINT,
261                                                              5,
262                                                              GTK_PARAM_READABLE));
263   /**
264    * GtkPaned:min-position:
265    *
266    * The smallest possible value for the position property. This property is derived from the
267    * size and shrinkability of the widget's children.
268    *
269    * Since: 2.4
270    */
271   g_object_class_install_property (object_class,
272                                    PROP_MIN_POSITION,
273                                    g_param_spec_int ("min-position",
274                                                      P_("Minimal Position"),
275                                                      P_("Smallest possible value for the \"position\" property"),
276                                                      0,
277                                                      G_MAXINT,
278                                                      0,
279                                                      GTK_PARAM_READABLE));
280
281   /**
282    * GtkPaned:max-position:
283    *
284    * The largest possible value for the position property. This property is derived from the
285    * size and shrinkability of the widget's children.
286    *
287    * Since: 2.4
288    */
289   g_object_class_install_property (object_class,
290                                    PROP_MAX_POSITION,
291                                    g_param_spec_int ("max-position",
292                                                      P_("Maximal Position"),
293                                                      P_("Largest possible value for the \"position\" property"),
294                                                      0,
295                                                      G_MAXINT,
296                                                      G_MAXINT,
297                                                      GTK_PARAM_READABLE));
298
299 /**
300  * GtkPaned:resize:
301  *
302  * The "resize" child property determines whether the child expands and 
303  * shrinks along with the paned widget.
304  * 
305  * Since: 2.4 
306  */
307   gtk_container_class_install_child_property (container_class,
308                                               CHILD_PROP_RESIZE,
309                                               g_param_spec_boolean ("resize", 
310                                                                     P_("Resize"),
311                                                                     P_("If TRUE, the child expands and shrinks along with the paned widget"),
312                                                                     TRUE,
313                                                                     GTK_PARAM_READWRITE));
314
315 /**
316  * GtkPaned:shrink:
317  *
318  * The "shrink" child property determines whether the child can be made 
319  * smaller than its requisition.
320  * 
321  * Since: 2.4 
322  */
323   gtk_container_class_install_child_property (container_class,
324                                               CHILD_PROP_SHRINK,
325                                               g_param_spec_boolean ("shrink", 
326                                                                     P_("Shrink"),
327                                                                     P_("If TRUE, the child can be made smaller than its requisition"),
328                                                                     TRUE,
329                                                                     GTK_PARAM_READWRITE));
330
331   signals [CYCLE_CHILD_FOCUS] =
332     g_signal_new ("cycle_child_focus",
333                   G_TYPE_FROM_CLASS (object_class),
334                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
335                   G_STRUCT_OFFSET (GtkPanedClass, cycle_child_focus),
336                   NULL, NULL,
337                   _gtk_marshal_BOOLEAN__BOOLEAN,
338                   G_TYPE_BOOLEAN, 1,
339                   G_TYPE_BOOLEAN);
340
341   signals [TOGGLE_HANDLE_FOCUS] =
342     g_signal_new ("toggle_handle_focus",
343                   G_TYPE_FROM_CLASS (object_class),
344                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
345                   G_STRUCT_OFFSET (GtkPanedClass, toggle_handle_focus),
346                   NULL, NULL,
347                   _gtk_marshal_BOOLEAN__VOID,
348                   G_TYPE_BOOLEAN, 0);
349
350   signals[MOVE_HANDLE] =
351     g_signal_new ("move_handle",
352                   G_TYPE_FROM_CLASS (object_class),
353                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
354                   G_STRUCT_OFFSET (GtkPanedClass, move_handle),
355                   NULL, NULL,
356                   _gtk_marshal_BOOLEAN__ENUM,
357                   G_TYPE_BOOLEAN, 1,
358                   GTK_TYPE_SCROLL_TYPE);
359
360   signals [CYCLE_HANDLE_FOCUS] =
361     g_signal_new ("cycle_handle_focus",
362                   G_TYPE_FROM_CLASS (object_class),
363                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
364                   G_STRUCT_OFFSET (GtkPanedClass, cycle_handle_focus),
365                   NULL, NULL,
366                   _gtk_marshal_BOOLEAN__BOOLEAN,
367                   G_TYPE_BOOLEAN, 1,
368                   G_TYPE_BOOLEAN);
369
370   signals [ACCEPT_POSITION] =
371     g_signal_new ("accept_position",
372                   G_TYPE_FROM_CLASS (object_class),
373                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
374                   G_STRUCT_OFFSET (GtkPanedClass, accept_position),
375                   NULL, NULL,
376                   _gtk_marshal_BOOLEAN__VOID,
377                   G_TYPE_BOOLEAN, 0);
378
379   signals [CANCEL_POSITION] =
380     g_signal_new ("cancel_position",
381                   G_TYPE_FROM_CLASS (object_class),
382                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
383                   G_STRUCT_OFFSET (GtkPanedClass, cancel_position),
384                   NULL, NULL,
385                   _gtk_marshal_BOOLEAN__VOID,
386                   G_TYPE_BOOLEAN, 0);
387
388   binding_set = gtk_binding_set_by_class (class);
389
390   /* F6 and friends */
391   gtk_binding_entry_add_signal (binding_set,                            
392                                 GDK_F6, 0,
393                                 "cycle_child_focus", 1, 
394                                 G_TYPE_BOOLEAN, FALSE);
395   gtk_binding_entry_add_signal (binding_set,
396                                 GDK_F6, GDK_SHIFT_MASK,
397                                 "cycle_child_focus", 1,
398                                 G_TYPE_BOOLEAN, TRUE);
399
400   /* F8 and friends */
401   gtk_binding_entry_add_signal (binding_set,
402                                 GDK_F8, 0,
403                                 "cycle_handle_focus", 1,
404                                 G_TYPE_BOOLEAN, FALSE);
405  
406   gtk_binding_entry_add_signal (binding_set,
407                                 GDK_F8, GDK_SHIFT_MASK,
408                                 "cycle_handle_focus", 1,
409                                 G_TYPE_BOOLEAN, TRUE);
410  
411   add_tab_bindings (binding_set, 0);
412   add_tab_bindings (binding_set, GDK_CONTROL_MASK);
413   add_tab_bindings (binding_set, GDK_SHIFT_MASK);
414   add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK);
415
416   /* accept and cancel positions */
417   gtk_binding_entry_add_signal (binding_set,
418                                 GDK_Escape, 0,
419                                 "cancel_position", 0);
420
421   gtk_binding_entry_add_signal (binding_set,
422                                 GDK_Return, 0,
423                                 "accept_position", 0);
424   gtk_binding_entry_add_signal (binding_set,
425                                 GDK_KP_Enter, 0,
426                                 "accept_position", 0);
427   gtk_binding_entry_add_signal (binding_set,
428                                 GDK_space, 0,
429                                 "accept_position", 0);
430   gtk_binding_entry_add_signal (binding_set,
431                                 GDK_KP_Space, 0,
432                                 "accept_position", 0);
433
434   /* move handle */
435   add_move_binding (binding_set, GDK_Left, 0, GTK_SCROLL_STEP_LEFT);
436   add_move_binding (binding_set, GDK_KP_Left, 0, GTK_SCROLL_STEP_LEFT);
437   add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT);
438   add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT);
439
440   add_move_binding (binding_set, GDK_Right, 0, GTK_SCROLL_STEP_RIGHT);
441   add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT);
442   add_move_binding (binding_set, GDK_KP_Right, 0, GTK_SCROLL_STEP_RIGHT);
443   add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT);
444
445   add_move_binding (binding_set, GDK_Up, 0, GTK_SCROLL_STEP_UP);
446   add_move_binding (binding_set, GDK_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP);
447   add_move_binding (binding_set, GDK_KP_Up, 0, GTK_SCROLL_STEP_UP);
448   add_move_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP);
449   add_move_binding (binding_set, GDK_Page_Up, 0, GTK_SCROLL_PAGE_UP);
450   add_move_binding (binding_set, GDK_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP);
451
452   add_move_binding (binding_set, GDK_Down, 0, GTK_SCROLL_STEP_DOWN);
453   add_move_binding (binding_set, GDK_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN);
454   add_move_binding (binding_set, GDK_KP_Down, 0, GTK_SCROLL_STEP_DOWN);
455   add_move_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN);
456   add_move_binding (binding_set, GDK_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);
457   add_move_binding (binding_set, GDK_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);
458
459   add_move_binding (binding_set, GDK_Home, 0, GTK_SCROLL_START);
460   add_move_binding (binding_set, GDK_KP_Home, 0, GTK_SCROLL_START);
461   add_move_binding (binding_set, GDK_End, 0, GTK_SCROLL_END);
462   add_move_binding (binding_set, GDK_KP_End, 0, GTK_SCROLL_END);
463 }
464
465 static GType
466 gtk_paned_child_type (GtkContainer *container)
467 {
468   if (!GTK_PANED (container)->child1 || !GTK_PANED (container)->child2)
469     return GTK_TYPE_WIDGET;
470   else
471     return G_TYPE_NONE;
472 }
473
474 static void
475 gtk_paned_init (GtkPaned *paned)
476 {
477   GTK_WIDGET_SET_FLAGS (paned, GTK_NO_WINDOW | GTK_CAN_FOCUS);
478   
479   paned->child1 = NULL;
480   paned->child2 = NULL;
481   paned->handle = NULL;
482   paned->xor_gc = NULL;
483   paned->cursor_type = GDK_CROSS;
484   
485   paned->handle_pos.width = 5;
486   paned->handle_pos.height = 5;
487   paned->position_set = FALSE;
488   paned->last_allocation = -1;
489   paned->in_drag = FALSE;
490
491   paned->priv = g_new0 (GtkPanedPrivate, 1);
492   paned->last_child1_focus = NULL;
493   paned->last_child2_focus = NULL;
494   paned->in_recursion = FALSE;
495   paned->handle_prelit = FALSE;
496   paned->original_position = -1;
497   
498   paned->handle_pos.x = -1;
499   paned->handle_pos.y = -1;
500
501   paned->drag_pos = -1;
502 }
503
504 static void
505 gtk_paned_set_property (GObject        *object,
506                         guint           prop_id,
507                         const GValue   *value,
508                         GParamSpec     *pspec)
509 {
510   GtkPaned *paned = GTK_PANED (object);
511   
512   switch (prop_id)
513     {
514     case PROP_POSITION:
515       gtk_paned_set_position (paned, g_value_get_int (value));
516       break;
517     case PROP_POSITION_SET:
518       paned->position_set = g_value_get_boolean (value);
519       gtk_widget_queue_resize (GTK_WIDGET (paned));
520       break;
521     default:
522       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
523       break;
524     }
525 }
526
527 static void
528 gtk_paned_get_property (GObject        *object,
529                         guint           prop_id,
530                         GValue         *value,
531                         GParamSpec     *pspec)
532 {
533   GtkPaned *paned = GTK_PANED (object);
534   
535   switch (prop_id)
536     {
537     case PROP_POSITION:
538       g_value_set_int (value, paned->child1_size);
539       break;
540     case PROP_POSITION_SET:
541       g_value_set_boolean (value, paned->position_set);
542       break;
543     case PROP_MIN_POSITION:
544       g_value_set_int (value, paned->min_position);
545       break;
546     case PROP_MAX_POSITION:
547       g_value_set_int (value, paned->max_position);
548       break;
549     default:
550       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
551       break;
552     }
553 }
554
555 static void
556 gtk_paned_set_child_property (GtkContainer    *container,
557                               GtkWidget       *child,
558                               guint            property_id,
559                               const GValue    *value,
560                               GParamSpec      *pspec)
561 {
562   GtkPaned *paned = GTK_PANED (container);
563   gboolean old_value, new_value;
564
565   g_assert (child == paned->child1 || child == paned->child2);
566
567   new_value = g_value_get_boolean (value);
568   switch (property_id)
569     {
570     case CHILD_PROP_RESIZE:
571       if (child == paned->child1)
572         {
573           old_value = paned->child1_resize;
574           paned->child1_resize = new_value;
575         }
576       else
577         {
578           old_value = paned->child2_resize;
579           paned->child2_resize = new_value;
580         }
581       break;
582     case CHILD_PROP_SHRINK:
583       if (child == paned->child1)
584         {
585           old_value = paned->child1_shrink;
586           paned->child1_shrink = new_value;
587         }
588       else
589         {
590           old_value = paned->child2_shrink;
591           paned->child2_shrink = new_value;
592         }
593       break;
594     default:
595       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
596       old_value = -1; /* quiet gcc */
597       break;
598     }
599   if (old_value != new_value)
600     gtk_widget_queue_resize (GTK_WIDGET (container));
601 }
602
603 static void
604 gtk_paned_get_child_property (GtkContainer *container,
605                               GtkWidget    *child,
606                               guint         property_id,
607                               GValue       *value,
608                               GParamSpec   *pspec)
609 {
610   GtkPaned *paned = GTK_PANED (container);
611
612   g_assert (child == paned->child1 || child == paned->child2);
613   
614   switch (property_id)
615     {
616     case CHILD_PROP_RESIZE:
617       if (child == paned->child1)
618         g_value_set_boolean (value, paned->child1_resize);
619       else
620         g_value_set_boolean (value, paned->child2_resize);
621       break;
622     case CHILD_PROP_SHRINK:
623       if (child == paned->child1)
624         g_value_set_boolean (value, paned->child1_shrink);
625       else
626         g_value_set_boolean (value, paned->child2_shrink);
627       break;
628     default:
629       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
630       break;
631     }
632 }
633
634 static void
635 gtk_paned_finalize (GObject *object)
636 {
637   GtkPaned *paned = GTK_PANED (object);
638   
639   gtk_paned_set_saved_focus (paned, NULL);
640   gtk_paned_set_first_paned (paned, NULL);
641
642   g_free (paned->priv);
643
644   G_OBJECT_CLASS (parent_class)->finalize (object);
645 }
646
647 static void
648 gtk_paned_realize (GtkWidget *widget)
649 {
650   GtkPaned *paned;
651   GdkWindowAttr attributes;
652   gint attributes_mask;
653
654   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
655   paned = GTK_PANED (widget);
656
657   widget->window = gtk_widget_get_parent_window (widget);
658   g_object_ref (widget->window);
659   
660   attributes.window_type = GDK_WINDOW_CHILD;
661   attributes.wclass = GDK_INPUT_ONLY;
662   attributes.x = paned->handle_pos.x;
663   attributes.y = paned->handle_pos.y;
664   attributes.width = paned->handle_pos.width;
665   attributes.height = paned->handle_pos.height;
666   attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
667                                                   paned->cursor_type);
668   attributes.event_mask = gtk_widget_get_events (widget);
669   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
670                             GDK_BUTTON_RELEASE_MASK |
671                             GDK_ENTER_NOTIFY_MASK |
672                             GDK_LEAVE_NOTIFY_MASK |
673                             GDK_POINTER_MOTION_MASK |
674                             GDK_POINTER_MOTION_HINT_MASK);
675   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
676
677   paned->handle = gdk_window_new (widget->window,
678                                   &attributes, attributes_mask);
679   gdk_window_set_user_data (paned->handle, paned);
680   gdk_cursor_unref (attributes.cursor);
681
682   widget->style = gtk_style_attach (widget->style, widget->window);
683
684   if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1) &&
685       paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
686     gdk_window_show (paned->handle);
687 }
688
689 static void
690 gtk_paned_unrealize (GtkWidget *widget)
691 {
692   GtkPaned *paned = GTK_PANED (widget);
693
694   if (paned->xor_gc)
695     {
696       g_object_unref (paned->xor_gc);
697       paned->xor_gc = NULL;
698     }
699
700   if (paned->handle)
701     {
702       gdk_window_set_user_data (paned->handle, NULL);
703       gdk_window_destroy (paned->handle);
704       paned->handle = NULL;
705     }
706
707   gtk_paned_set_last_child1_focus (paned, NULL);
708   gtk_paned_set_last_child2_focus (paned, NULL);
709   gtk_paned_set_saved_focus (paned, NULL);
710   gtk_paned_set_first_paned (paned, NULL);
711   
712   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
713     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
714 }
715
716 static void
717 gtk_paned_map (GtkWidget *widget)
718 {
719   GtkPaned *paned = GTK_PANED (widget);
720
721   gdk_window_show (paned->handle);
722
723   GTK_WIDGET_CLASS (parent_class)->map (widget);
724 }
725
726 static void
727 gtk_paned_unmap (GtkWidget *widget)
728 {
729   GtkPaned *paned = GTK_PANED (widget);
730     
731   gdk_window_hide (paned->handle);
732
733   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
734 }
735
736 static gboolean
737 gtk_paned_expose (GtkWidget      *widget,
738                   GdkEventExpose *event)
739 {
740   GtkPaned *paned = GTK_PANED (widget);
741
742   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
743       paned->child1 && GTK_WIDGET_VISIBLE (paned->child1) &&
744       paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
745     {
746       GtkStateType state;
747       
748       if (gtk_widget_is_focus (widget))
749         state = GTK_STATE_SELECTED;
750       else if (paned->handle_prelit)
751         state = GTK_STATE_PRELIGHT;
752       else
753         state = GTK_WIDGET_STATE (widget);
754       
755       gtk_paint_handle (widget->style, widget->window,
756                         state, GTK_SHADOW_NONE,
757                         &paned->handle_pos, widget, "paned",
758                         paned->handle_pos.x, paned->handle_pos.y,
759                         paned->handle_pos.width, paned->handle_pos.height,
760                         paned->orientation);
761     }
762
763   /* Chain up to draw children */
764   GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
765   
766   return FALSE;
767 }
768
769 static gboolean
770 is_rtl (GtkPaned *paned)
771 {
772   if (paned->orientation == GTK_ORIENTATION_VERTICAL &&
773       gtk_widget_get_direction (GTK_WIDGET (paned)) == GTK_TEXT_DIR_RTL)
774     {
775       return TRUE;
776     }
777
778   return FALSE;
779 }
780
781 static void
782 update_drag (GtkPaned *paned)
783 {
784   gint pos;
785   gint handle_size;
786   gint size;
787   
788   if (paned->orientation == GTK_ORIENTATION_HORIZONTAL)
789     gtk_widget_get_pointer (GTK_WIDGET (paned), NULL, &pos);
790   else
791     gtk_widget_get_pointer (GTK_WIDGET (paned), &pos, NULL);
792
793   pos -= paned->drag_pos;
794
795   if (is_rtl (paned))
796     {
797       gtk_widget_style_get (GTK_WIDGET (paned),
798                             "handle_size", &handle_size,
799                             NULL);
800       
801       size = GTK_WIDGET (paned)->allocation.width - pos - handle_size;
802     }
803   else
804     {
805       size = pos;
806     }
807
808   size -= GTK_CONTAINER (paned)->border_width;
809   
810   size = CLAMP (size, paned->min_position, paned->max_position);
811
812   if (size != paned->child1_size)
813     gtk_paned_set_position (paned, size);
814 }
815
816 static gboolean
817 gtk_paned_enter (GtkWidget        *widget,
818                  GdkEventCrossing *event)
819 {
820   GtkPaned *paned = GTK_PANED (widget);
821   
822   if (paned->in_drag)
823     update_drag (paned);
824   else
825     {
826       paned->handle_prelit = TRUE;
827       gtk_widget_queue_draw_area (widget,
828                                   paned->handle_pos.x,
829                                   paned->handle_pos.y,
830                                   paned->handle_pos.width,
831                                   paned->handle_pos.height);
832     }
833   
834   return TRUE;
835 }
836
837 static gboolean
838 gtk_paned_leave (GtkWidget        *widget,
839                  GdkEventCrossing *event)
840 {
841   GtkPaned *paned = GTK_PANED (widget);
842   
843   if (paned->in_drag)
844     update_drag (paned);
845   else
846     {
847       paned->handle_prelit = FALSE;
848       gtk_widget_queue_draw_area (widget,
849                                   paned->handle_pos.x,
850                                   paned->handle_pos.y,
851                                   paned->handle_pos.width,
852                                   paned->handle_pos.height);
853     }
854
855   return TRUE;
856 }
857
858 static gboolean
859 gtk_paned_focus (GtkWidget        *widget,
860                  GtkDirectionType  direction)
861
862 {
863   gboolean retval;
864   
865   /* This is a hack, but how can this be done without
866    * excessive cut-and-paste from gtkcontainer.c?
867    */
868
869   GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS);
870   retval = (* GTK_WIDGET_CLASS (parent_class)->focus) (widget, direction);
871   GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
872
873   return retval;
874 }
875
876 static gboolean
877 gtk_paned_button_press (GtkWidget      *widget,
878                         GdkEventButton *event)
879 {
880   GtkPaned *paned = GTK_PANED (widget);
881
882   if (!paned->in_drag &&
883       (event->window == paned->handle) && (event->button == 1))
884     {
885       paned->in_drag = TRUE;
886
887       /* We need a server grab here, not gtk_grab_add(), since
888        * we don't want to pass events on to the widget's children */
889       gdk_pointer_grab (paned->handle, FALSE,
890                         GDK_POINTER_MOTION_HINT_MASK
891                         | GDK_BUTTON1_MOTION_MASK
892                         | GDK_BUTTON_RELEASE_MASK
893                         | GDK_ENTER_NOTIFY_MASK
894                         | GDK_LEAVE_NOTIFY_MASK,
895                         NULL, NULL,
896                         event->time);
897
898       if (paned->orientation == GTK_ORIENTATION_HORIZONTAL)
899         paned->drag_pos = event->y;
900       else
901         paned->drag_pos = event->x;
902       
903       return TRUE;
904     }
905
906   return FALSE;
907 }
908
909 static gboolean
910 gtk_paned_button_release (GtkWidget      *widget,
911                           GdkEventButton *event)
912 {
913   GtkPaned *paned = GTK_PANED (widget);
914
915   if (paned->in_drag && (event->button == 1))
916     {
917       paned->in_drag = FALSE;
918       paned->drag_pos = -1;
919       paned->position_set = TRUE;
920       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
921                                   event->time);
922       return TRUE;
923     }
924
925   return FALSE;
926 }
927
928 static gboolean
929 gtk_paned_motion (GtkWidget      *widget,
930                   GdkEventMotion *event)
931 {
932   GtkPaned *paned = GTK_PANED (widget);
933   
934   if (paned->in_drag)
935     {
936       update_drag (paned);
937       return TRUE;
938     }
939   
940   return FALSE;
941 }
942
943 void
944 gtk_paned_add1 (GtkPaned  *paned,
945                 GtkWidget *widget)
946 {
947   gtk_paned_pack1 (paned, widget, FALSE, TRUE);
948 }
949
950 void
951 gtk_paned_add2 (GtkPaned  *paned,
952                 GtkWidget *widget)
953 {
954   gtk_paned_pack2 (paned, widget, TRUE, TRUE);
955 }
956
957 void
958 gtk_paned_pack1 (GtkPaned  *paned,
959                  GtkWidget *child,
960                  gboolean   resize,
961                  gboolean   shrink)
962 {
963   g_return_if_fail (GTK_IS_PANED (paned));
964   g_return_if_fail (GTK_IS_WIDGET (child));
965
966   if (!paned->child1)
967     {
968       paned->child1 = child;
969       paned->child1_resize = resize;
970       paned->child1_shrink = shrink;
971
972       gtk_widget_set_parent (child, GTK_WIDGET (paned));
973     }
974 }
975
976 void
977 gtk_paned_pack2 (GtkPaned  *paned,
978                  GtkWidget *child,
979                  gboolean   resize,
980                  gboolean   shrink)
981 {
982   g_return_if_fail (GTK_IS_PANED (paned));
983   g_return_if_fail (GTK_IS_WIDGET (child));
984
985   if (!paned->child2)
986     {
987       paned->child2 = child;
988       paned->child2_resize = resize;
989       paned->child2_shrink = shrink;
990
991       gtk_widget_set_parent (child, GTK_WIDGET (paned));
992     }
993 }
994
995
996 static void
997 gtk_paned_add (GtkContainer *container,
998                GtkWidget    *widget)
999 {
1000   GtkPaned *paned;
1001
1002   g_return_if_fail (GTK_IS_PANED (container));
1003
1004   paned = GTK_PANED (container);
1005
1006   if (!paned->child1)
1007     gtk_paned_add1 (paned, widget);
1008   else if (!paned->child2)
1009     gtk_paned_add2 (paned, widget);
1010 }
1011
1012 static void
1013 gtk_paned_remove (GtkContainer *container,
1014                   GtkWidget    *widget)
1015 {
1016   GtkPaned *paned;
1017   gboolean was_visible;
1018
1019   paned = GTK_PANED (container);
1020   was_visible = GTK_WIDGET_VISIBLE (widget);
1021
1022   if (paned->child1 == widget)
1023     {
1024       gtk_widget_unparent (widget);
1025
1026       paned->child1 = NULL;
1027
1028       if (was_visible && GTK_WIDGET_VISIBLE (container))
1029         gtk_widget_queue_resize (GTK_WIDGET (container));
1030     }
1031   else if (paned->child2 == widget)
1032     {
1033       gtk_widget_unparent (widget);
1034
1035       paned->child2 = NULL;
1036
1037       if (was_visible && GTK_WIDGET_VISIBLE (container))
1038         gtk_widget_queue_resize (GTK_WIDGET (container));
1039     }
1040 }
1041
1042 static void
1043 gtk_paned_forall (GtkContainer *container,
1044                   gboolean      include_internals,
1045                   GtkCallback   callback,
1046                   gpointer      callback_data)
1047 {
1048   GtkPaned *paned;
1049
1050   g_return_if_fail (callback != NULL);
1051
1052   paned = GTK_PANED (container);
1053
1054   if (paned->child1)
1055     (*callback) (paned->child1, callback_data);
1056   if (paned->child2)
1057     (*callback) (paned->child2, callback_data);
1058 }
1059
1060 /**
1061  * gtk_paned_get_position:
1062  * @paned: a #GtkPaned widget
1063  * 
1064  * Obtains the position of the divider between the two panes.
1065  * 
1066  * Return value: position of the divider
1067  **/
1068 gint
1069 gtk_paned_get_position (GtkPaned  *paned)
1070 {
1071   g_return_val_if_fail (GTK_IS_PANED (paned), 0);
1072
1073   return paned->child1_size;
1074 }
1075
1076 /**
1077  * gtk_paned_set_position:
1078  * @paned: a #GtkPaned widget
1079  * @position: pixel position of divider, a negative value means that the position
1080  *            is unset.
1081  * 
1082  * Sets the position of the divider between the two panes.
1083  **/
1084 void
1085 gtk_paned_set_position (GtkPaned *paned,
1086                         gint      position)
1087 {
1088   GObject *object;
1089   
1090   g_return_if_fail (GTK_IS_PANED (paned));
1091
1092   object = G_OBJECT (paned);
1093   
1094   if (position >= 0)
1095     {
1096       /* We don't clamp here - the assumption is that
1097        * if the total allocation changes at the same time
1098        * as the position, the position set is with reference
1099        * to the new total size. If only the position changes,
1100        * then clamping will occur in gtk_paned_compute_position()
1101        */
1102
1103       paned->child1_size = position;
1104       paned->position_set = TRUE;
1105     }
1106   else
1107     {
1108       paned->position_set = FALSE;
1109     }
1110
1111   g_object_freeze_notify (object);
1112   g_object_notify (object, "position");
1113   g_object_notify (object, "position_set");
1114   g_object_thaw_notify (object);
1115
1116   gtk_widget_queue_resize (GTK_WIDGET (paned));
1117 }
1118
1119 /**
1120  * gtk_paned_get_child1:
1121  * @paned: a #GtkPaned widget
1122  * 
1123  * Obtains the first child of the paned widget.
1124  * 
1125  * Return value: first child, or %NULL if it is not set.
1126  *
1127  * Since: 2.4
1128  **/
1129 GtkWidget *
1130 gtk_paned_get_child1 (GtkPaned *paned)
1131 {
1132   g_return_val_if_fail (GTK_IS_PANED (paned), NULL);
1133
1134   return paned->child1;
1135 }
1136
1137 /**
1138  * gtk_paned_get_child2:
1139  * @paned: a #GtkPaned widget
1140  * 
1141  * Obtains the second child of the paned widget.
1142  * 
1143  * Return value: second child, or %NULL if it is not set.
1144  *
1145  * Since: 2.4
1146  **/
1147 GtkWidget *
1148 gtk_paned_get_child2 (GtkPaned *paned)
1149 {
1150   g_return_val_if_fail (GTK_IS_PANED (paned), NULL);
1151
1152   return paned->child2;
1153 }
1154
1155 void
1156 gtk_paned_compute_position (GtkPaned *paned,
1157                             gint      allocation,
1158                             gint      child1_req,
1159                             gint      child2_req)
1160 {
1161   gint old_position;
1162   gint old_min_position;
1163   gint old_max_position;
1164   
1165   g_return_if_fail (GTK_IS_PANED (paned));
1166
1167   old_position = paned->child1_size;
1168   old_min_position = paned->min_position;
1169   old_max_position = paned->max_position;
1170
1171   paned->min_position = paned->child1_shrink ? 0 : child1_req;
1172
1173   paned->max_position = allocation;
1174   if (!paned->child2_shrink)
1175     paned->max_position = MAX (1, paned->max_position - child2_req);
1176   paned->max_position = MAX (paned->min_position, paned->max_position);
1177
1178   if (!paned->position_set)
1179     {
1180       if (paned->child1_resize && !paned->child2_resize)
1181         paned->child1_size = MAX (0, allocation - child2_req);
1182       else if (!paned->child1_resize && paned->child2_resize)
1183         paned->child1_size = child1_req;
1184       else if (child1_req + child2_req != 0)
1185         paned->child1_size = allocation * ((gdouble)child1_req / (child1_req + child2_req)) + 0.5;
1186       else
1187         paned->child1_size = allocation * 0.5 + 0.5;
1188     }
1189   else
1190     {
1191       /* If the position was set before the initial allocation.
1192        * (paned->last_allocation <= 0) just clamp it and leave it.
1193        */
1194       if (paned->last_allocation > 0)
1195         {
1196           if (paned->child1_resize && !paned->child2_resize)
1197             paned->child1_size += allocation - paned->last_allocation;
1198           else if (!(!paned->child1_resize && paned->child2_resize))
1199             paned->child1_size = allocation * ((gdouble) paned->child1_size / (paned->last_allocation)) + 0.5;
1200         }
1201     }
1202
1203   paned->child1_size = CLAMP (paned->child1_size,
1204                               paned->min_position,
1205                               paned->max_position);
1206
1207   gtk_widget_set_child_visible (paned->child1, paned->child1_size != 0);
1208   gtk_widget_set_child_visible (paned->child2, paned->child1_size != allocation);
1209
1210   g_object_freeze_notify (G_OBJECT (paned));
1211   if (paned->child1_size != old_position)
1212     g_object_notify (G_OBJECT (paned), "position");
1213   if (paned->min_position != old_min_position)
1214     g_object_notify (G_OBJECT (paned), "min_position");
1215   if (paned->max_position != old_max_position)
1216     g_object_notify (G_OBJECT (paned), "max_position");
1217   g_object_thaw_notify (G_OBJECT (paned));
1218
1219   paned->last_allocation = allocation;
1220 }
1221
1222 static void
1223 gtk_paned_set_saved_focus (GtkPaned *paned, GtkWidget *widget)
1224 {
1225   if (paned->priv->saved_focus)
1226     g_object_remove_weak_pointer (G_OBJECT (paned->priv->saved_focus),
1227                                   (gpointer *)&(paned->priv->saved_focus));
1228
1229   paned->priv->saved_focus = widget;
1230
1231   if (paned->priv->saved_focus)
1232     g_object_add_weak_pointer (G_OBJECT (paned->priv->saved_focus),
1233                                (gpointer *)&(paned->priv->saved_focus));
1234 }
1235
1236 static void
1237 gtk_paned_set_first_paned (GtkPaned *paned, GtkPaned *first_paned)
1238 {
1239   if (paned->priv->first_paned)
1240     g_object_remove_weak_pointer (G_OBJECT (paned->priv->first_paned),
1241                                   (gpointer *)&(paned->priv->first_paned));
1242
1243   paned->priv->first_paned = first_paned;
1244
1245   if (paned->priv->first_paned)
1246     g_object_add_weak_pointer (G_OBJECT (paned->priv->first_paned),
1247                                (gpointer *)&(paned->priv->first_paned));
1248 }
1249
1250 static void
1251 gtk_paned_set_last_child1_focus (GtkPaned *paned, GtkWidget *widget)
1252 {
1253   if (paned->last_child1_focus)
1254     g_object_remove_weak_pointer (G_OBJECT (paned->last_child1_focus),
1255                                   (gpointer *)&(paned->last_child1_focus));
1256
1257   paned->last_child1_focus = widget;
1258
1259   if (paned->last_child1_focus)
1260     g_object_add_weak_pointer (G_OBJECT (paned->last_child1_focus),
1261                                (gpointer *)&(paned->last_child1_focus));
1262 }
1263
1264 static void
1265 gtk_paned_set_last_child2_focus (GtkPaned *paned, GtkWidget *widget)
1266 {
1267   if (paned->last_child2_focus)
1268     g_object_remove_weak_pointer (G_OBJECT (paned->last_child2_focus),
1269                                   (gpointer *)&(paned->last_child2_focus));
1270
1271   paned->last_child2_focus = widget;
1272
1273   if (paned->last_child2_focus)
1274     g_object_add_weak_pointer (G_OBJECT (paned->last_child2_focus),
1275                                (gpointer *)&(paned->last_child2_focus));
1276 }
1277
1278 static GtkWidget *
1279 paned_get_focus_widget (GtkPaned *paned)
1280 {
1281   GtkWidget *toplevel;
1282
1283   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned));
1284   if (GTK_WIDGET_TOPLEVEL (toplevel))
1285     return GTK_WINDOW (toplevel)->focus_widget;
1286
1287   return NULL;
1288 }
1289
1290 static void
1291 gtk_paned_set_focus_child (GtkContainer *container,
1292                            GtkWidget    *focus_child)
1293 {
1294   GtkPaned *paned;
1295   
1296   g_return_if_fail (GTK_IS_PANED (container));
1297
1298   paned = GTK_PANED (container);
1299  
1300   if (focus_child == NULL)
1301     {
1302       GtkWidget *last_focus;
1303       GtkWidget *w;
1304       
1305       last_focus = paned_get_focus_widget (paned);
1306
1307       if (last_focus)
1308         {
1309           /* If there is one or more paned widgets between us and the
1310            * focus widget, we want the topmost of those as last_focus
1311            */
1312           for (w = last_focus; w != GTK_WIDGET (paned); w = w->parent)
1313             if (GTK_IS_PANED (w))
1314               last_focus = w;
1315           
1316           if (container->focus_child == paned->child1)
1317             gtk_paned_set_last_child1_focus (paned, last_focus);
1318           else if (container->focus_child == paned->child2)
1319             gtk_paned_set_last_child2_focus (paned, last_focus);
1320         }
1321     }
1322
1323   if (parent_class->set_focus_child)
1324     (* parent_class->set_focus_child) (container, focus_child);
1325 }
1326
1327 static void
1328 gtk_paned_get_cycle_chain (GtkPaned          *paned,
1329                            GtkDirectionType   direction,
1330                            GList            **widgets)
1331 {
1332   GtkContainer *container = GTK_CONTAINER (paned);
1333   GtkWidget *ancestor = NULL;
1334   GList *temp_list = NULL;
1335   GList *list;
1336
1337   if (paned->in_recursion)
1338     return;
1339
1340   g_assert (widgets != NULL);
1341
1342   if (paned->last_child1_focus &&
1343       !gtk_widget_is_ancestor (paned->last_child1_focus, GTK_WIDGET (paned)))
1344     {
1345       gtk_paned_set_last_child1_focus (paned, NULL);
1346     }
1347
1348   if (paned->last_child2_focus &&
1349       !gtk_widget_is_ancestor (paned->last_child2_focus, GTK_WIDGET (paned)))
1350     {
1351       gtk_paned_set_last_child2_focus (paned, NULL);
1352     }
1353
1354   if (GTK_WIDGET (paned)->parent)
1355     ancestor = gtk_widget_get_ancestor (GTK_WIDGET (paned)->parent, GTK_TYPE_PANED);
1356
1357   /* The idea here is that temp_list is a list of widgets we want to cycle
1358    * to. The list is prioritized so that the first element is our first
1359    * choice, the next our second, and so on.
1360    *
1361    * We can't just use g_list_reverse(), because we want to try
1362    * paned->last_child?_focus before paned->child?, both when we
1363    * are going forward and backward.
1364    */
1365   if (direction == GTK_DIR_TAB_FORWARD)
1366     {
1367       if (container->focus_child == paned->child1)
1368         {
1369           temp_list = g_list_append (temp_list, paned->last_child2_focus);
1370           temp_list = g_list_append (temp_list, paned->child2);
1371           temp_list = g_list_append (temp_list, ancestor);
1372         }
1373       else if (container->focus_child == paned->child2)
1374         {
1375           temp_list = g_list_append (temp_list, ancestor);
1376           temp_list = g_list_append (temp_list, paned->last_child1_focus);
1377           temp_list = g_list_append (temp_list, paned->child1);
1378         }
1379       else
1380         {
1381           temp_list = g_list_append (temp_list, paned->last_child1_focus);
1382           temp_list = g_list_append (temp_list, paned->child1);
1383           temp_list = g_list_append (temp_list, paned->last_child2_focus);
1384           temp_list = g_list_append (temp_list, paned->child2);
1385           temp_list = g_list_append (temp_list, ancestor);
1386         }
1387     }
1388   else
1389     {
1390       if (container->focus_child == paned->child1)
1391         {
1392           temp_list = g_list_append (temp_list, ancestor);
1393           temp_list = g_list_append (temp_list, paned->last_child2_focus);
1394           temp_list = g_list_append (temp_list, paned->child2);
1395         }
1396       else if (container->focus_child == paned->child2)
1397         {
1398           temp_list = g_list_append (temp_list, paned->last_child1_focus);
1399           temp_list = g_list_append (temp_list, paned->child1);
1400           temp_list = g_list_append (temp_list, ancestor);
1401         }
1402       else
1403         {
1404           temp_list = g_list_append (temp_list, paned->last_child2_focus);
1405           temp_list = g_list_append (temp_list, paned->child2);
1406           temp_list = g_list_append (temp_list, paned->last_child1_focus);
1407           temp_list = g_list_append (temp_list, paned->child1);
1408           temp_list = g_list_append (temp_list, ancestor);
1409         }
1410     }
1411
1412   /* Walk the list and expand all the paned widgets. */
1413   for (list = temp_list; list != NULL; list = list->next)
1414     {
1415       GtkWidget *widget = list->data;
1416
1417       if (widget)
1418         {
1419           if (GTK_IS_PANED (widget))
1420             {
1421               paned->in_recursion = TRUE;
1422               gtk_paned_get_cycle_chain (GTK_PANED (widget), direction, widgets);
1423               paned->in_recursion = FALSE;
1424             }
1425           else
1426             {
1427               *widgets = g_list_append (*widgets, widget);
1428             }
1429         }
1430     }
1431
1432   g_list_free (temp_list);
1433 }
1434
1435 static gboolean
1436 gtk_paned_cycle_child_focus (GtkPaned *paned,
1437                              gboolean  reversed)
1438 {
1439   GList *cycle_chain = NULL;
1440   GList *list;
1441   
1442   GtkDirectionType direction = reversed? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD;
1443
1444   /* ignore f6 if the handle is focused */
1445   if (gtk_widget_is_focus (GTK_WIDGET (paned)))
1446     return TRUE;
1447   
1448   /* we can't just let the event propagate up the hierarchy,
1449    * because the paned will want to cycle focus _unless_ an
1450    * ancestor paned handles the event
1451    */
1452   gtk_paned_get_cycle_chain (paned, direction, &cycle_chain);
1453
1454   for (list = cycle_chain; list != NULL; list = list->next)
1455     if (gtk_widget_child_focus (GTK_WIDGET (list->data), direction))
1456       break;
1457
1458   g_list_free (cycle_chain);
1459   
1460   return TRUE;
1461 }
1462
1463 static void
1464 get_child_panes (GtkWidget  *widget,
1465                  GList     **panes)
1466 {
1467   if (GTK_IS_PANED (widget))
1468     {
1469       GtkPaned *paned = GTK_PANED (widget);
1470       
1471       get_child_panes (paned->child1, panes);
1472       *panes = g_list_prepend (*panes, widget);
1473       get_child_panes (paned->child2, panes);
1474     }
1475   else if (GTK_IS_CONTAINER (widget))
1476     {
1477       gtk_container_foreach (GTK_CONTAINER (widget),
1478                              (GtkCallback)get_child_panes, panes);
1479     }
1480 }
1481
1482 static GList *
1483 get_all_panes (GtkPaned *paned)
1484 {
1485   GtkPaned *topmost = NULL;
1486   GList *result = NULL;
1487   GtkWidget *w;
1488   
1489   for (w = GTK_WIDGET (paned); w != NULL; w = w->parent)
1490     {
1491       if (GTK_IS_PANED (w))
1492         topmost = GTK_PANED (w);
1493     }
1494
1495   g_assert (topmost);
1496
1497   get_child_panes (GTK_WIDGET (topmost), &result);
1498
1499   return g_list_reverse (result);
1500 }
1501
1502 static void
1503 gtk_paned_find_neighbours (GtkPaned  *paned,
1504                            GtkPaned **next,
1505                            GtkPaned **prev)
1506 {
1507   GList *all_panes;
1508   GList *this_link;
1509
1510   all_panes = get_all_panes (paned);
1511   g_assert (all_panes);
1512
1513   this_link = g_list_find (all_panes, paned);
1514
1515   g_assert (this_link);
1516   
1517   if (this_link->next)
1518     *next = this_link->next->data;
1519   else
1520     *next = all_panes->data;
1521
1522   if (this_link->prev)
1523     *prev = this_link->prev->data;
1524   else
1525     *prev = g_list_last (all_panes)->data;
1526
1527   g_list_free (all_panes);
1528 }
1529
1530 static gboolean
1531 gtk_paned_move_handle (GtkPaned      *paned,
1532                        GtkScrollType  scroll)
1533 {
1534   if (gtk_widget_is_focus (GTK_WIDGET (paned)))
1535     {
1536       gint old_position;
1537       gint new_position;
1538       gint increment;
1539       
1540       enum {
1541         SINGLE_STEP_SIZE = 1,
1542         PAGE_STEP_SIZE   = 75
1543       };
1544       
1545       new_position = old_position = gtk_paned_get_position (paned);
1546       increment = 0;
1547       
1548       switch (scroll)
1549         {
1550         case GTK_SCROLL_STEP_LEFT:
1551         case GTK_SCROLL_STEP_UP:
1552         case GTK_SCROLL_STEP_BACKWARD:
1553           increment = - SINGLE_STEP_SIZE;
1554           break;
1555           
1556         case GTK_SCROLL_STEP_RIGHT:
1557         case GTK_SCROLL_STEP_DOWN:
1558         case GTK_SCROLL_STEP_FORWARD:
1559           increment = SINGLE_STEP_SIZE;
1560           break;
1561           
1562         case GTK_SCROLL_PAGE_LEFT:
1563         case GTK_SCROLL_PAGE_UP:
1564         case GTK_SCROLL_PAGE_BACKWARD:
1565           increment = - PAGE_STEP_SIZE;
1566           break;
1567           
1568         case GTK_SCROLL_PAGE_RIGHT:
1569         case GTK_SCROLL_PAGE_DOWN:
1570         case GTK_SCROLL_PAGE_FORWARD:
1571           increment = PAGE_STEP_SIZE;
1572           break;
1573           
1574         case GTK_SCROLL_START:
1575           new_position = paned->min_position;
1576           break;
1577           
1578         case GTK_SCROLL_END:
1579           new_position = paned->max_position;
1580           break;
1581
1582         default:
1583           break;
1584         }
1585
1586       if (increment)
1587         {
1588           if (is_rtl (paned))
1589             increment = -increment;
1590           
1591           new_position = old_position + increment;
1592         }
1593       
1594       new_position = CLAMP (new_position, paned->min_position, paned->max_position);
1595       
1596       if (old_position != new_position)
1597         gtk_paned_set_position (paned, new_position);
1598
1599       return TRUE;
1600     }
1601
1602   return FALSE;
1603 }
1604
1605 static void
1606 gtk_paned_restore_focus (GtkPaned *paned)
1607 {
1608   if (gtk_widget_is_focus (GTK_WIDGET (paned)))
1609     {
1610       if (paned->priv->saved_focus &&
1611           GTK_WIDGET_SENSITIVE (paned->priv->saved_focus))
1612         {
1613           gtk_widget_grab_focus (paned->priv->saved_focus);
1614         }
1615       else
1616         {
1617           /* the saved focus is somehow not available for focusing,
1618            * try
1619            *   1) tabbing into the paned widget
1620            * if that didn't work,
1621            *   2) unset focus for the window if there is one
1622            */
1623           
1624           if (!gtk_widget_child_focus (GTK_WIDGET (paned), GTK_DIR_TAB_FORWARD))
1625             {
1626               GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned));
1627               
1628               if (GTK_IS_WINDOW (toplevel))
1629                 gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
1630             }
1631         }
1632       
1633       gtk_paned_set_saved_focus (paned, NULL);
1634       gtk_paned_set_first_paned (paned, NULL);
1635     }
1636 }
1637
1638 static gboolean
1639 gtk_paned_accept_position (GtkPaned *paned)
1640 {
1641   if (gtk_widget_is_focus (GTK_WIDGET (paned)))
1642     {
1643       paned->original_position = -1;
1644       gtk_paned_restore_focus (paned);
1645
1646       return TRUE;
1647     }
1648
1649   return FALSE;
1650 }
1651
1652
1653 static gboolean
1654 gtk_paned_cancel_position (GtkPaned *paned)
1655 {
1656   if (gtk_widget_is_focus (GTK_WIDGET (paned)))
1657     {
1658       if (paned->original_position != -1)
1659         {
1660           gtk_paned_set_position (paned, paned->original_position);
1661           paned->original_position = -1;
1662         }
1663
1664       gtk_paned_restore_focus (paned);
1665       return TRUE;
1666     }
1667
1668   return FALSE;
1669 }
1670
1671 static gboolean
1672 gtk_paned_cycle_handle_focus (GtkPaned *paned,
1673                               gboolean  reversed)
1674 {
1675   GtkPaned *next, *prev;
1676   
1677   if (gtk_widget_is_focus (GTK_WIDGET (paned)))
1678     {
1679       GtkPaned *focus = NULL;
1680
1681       if (!paned->priv->first_paned)
1682         {
1683           /* The first_pane has disappeared. As an ad-hoc solution,
1684            * we make the currently focused paned the first_paned. To the
1685            * user this will seem like the paned cycling has been reset.
1686            */
1687           
1688           gtk_paned_set_first_paned (paned, paned);
1689         }
1690       
1691       gtk_paned_find_neighbours (paned, &next, &prev);
1692
1693       if (reversed && prev &&
1694           prev != paned && paned != paned->priv->first_paned)
1695         {
1696           focus = prev;
1697         }
1698       else if (!reversed && next &&
1699                next != paned && next != paned->priv->first_paned)
1700         {
1701           focus = next;
1702         }
1703       else
1704         {
1705           gtk_paned_accept_position (paned);
1706           return TRUE;
1707         }
1708
1709       g_assert (focus);
1710       
1711       gtk_paned_set_saved_focus (focus, paned->priv->saved_focus);
1712       gtk_paned_set_first_paned (focus, paned->priv->first_paned);
1713       
1714       gtk_paned_set_saved_focus (paned, NULL);
1715       gtk_paned_set_first_paned (paned, NULL);
1716       
1717       gtk_widget_grab_focus (GTK_WIDGET (focus));
1718       
1719       if (!gtk_widget_is_focus (GTK_WIDGET (paned)))
1720         {
1721           paned->original_position = -1;
1722           focus->original_position = gtk_paned_get_position (focus);
1723         }
1724     }
1725   else
1726     {
1727       GtkContainer *container = GTK_CONTAINER (paned);
1728       GtkPaned *focus;
1729       GtkPaned *first;
1730       GtkPaned *prev, *next;
1731       GtkWidget *toplevel;
1732
1733       gtk_paned_find_neighbours (paned, &next, &prev);
1734
1735       if (container->focus_child == paned->child1)
1736         {
1737           if (reversed)
1738             {
1739               focus = prev;
1740               first = paned;
1741             }
1742           else
1743             {
1744               focus = paned;
1745               first = paned;
1746             }
1747         }
1748       else if (container->focus_child == paned->child2)
1749         {
1750           if (reversed)
1751             {
1752               focus = paned;
1753               first = next;
1754             }
1755           else
1756             {
1757               focus = next;
1758               first = next;
1759             }
1760         }
1761       else
1762         {
1763           /* Focus is not inside this paned, and we don't have focus.
1764            * Presumably this happened because the application wants us
1765            * to start keyboard navigating.
1766            */
1767           focus = paned;
1768
1769           if (reversed)
1770             first = paned;
1771           else
1772             first = next;
1773         }
1774
1775       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned));
1776
1777       if (GTK_IS_WINDOW (toplevel))
1778         gtk_paned_set_saved_focus (focus, GTK_WINDOW (toplevel)->focus_widget);
1779       gtk_paned_set_first_paned (focus, first);
1780       focus->original_position = gtk_paned_get_position (focus); 
1781
1782       gtk_widget_grab_focus (GTK_WIDGET (focus));
1783    }
1784   
1785   return TRUE;
1786 }
1787
1788 static gboolean
1789 gtk_paned_toggle_handle_focus (GtkPaned *paned)
1790 {
1791   /* This function/signal has the wrong name. It is called when you
1792    * press Tab or Shift-Tab and what we do is act as if
1793    * the user pressed Return and then Tab or Shift-Tab
1794    */
1795   if (gtk_widget_is_focus (GTK_WIDGET (paned)))
1796     gtk_paned_accept_position (paned);
1797
1798   return FALSE;
1799 }
1800
1801 #define __GTK_PANED_C__
1802 #include "gtkaliasdef.c"