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