gtkpaned.c 51.7 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

27
#include <config.h>
28
#include "gtkintl.h"
Elliot Lee's avatar
Elliot Lee committed
29
#include "gtkpaned.h"
30 31 32 33 34
#include "gtkbindings.h"
#include "gdk/gdkkeysyms.h"
#include "gtkwindow.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
35
#include "gtkprivate.h"
36
#include "gtkalias.h"
Elliot Lee's avatar
Elliot Lee committed
37

38
enum {
39 40
  PROP_0,
  PROP_POSITION,
41 42 43
  PROP_POSITION_SET,
  PROP_MIN_POSITION,
  PROP_MAX_POSITION
44
};
Elliot Lee's avatar
Elliot Lee committed
45

46 47 48 49 50 51
enum {
  CHILD_PROP_0,
  CHILD_PROP_RESIZE,
  CHILD_PROP_SHRINK
};

52 53 54 55 56 57
enum {
  CYCLE_CHILD_FOCUS,
  TOGGLE_HANDLE_FOCUS,
  MOVE_HANDLE,
  CYCLE_HANDLE_FOCUS,
  ACCEPT_POSITION,
58 59
  CANCEL_POSITION,
  LAST_SIGNAL
60 61 62 63 64 65 66 67 68 69
};

static void     gtk_paned_set_property          (GObject          *object,
						 guint             prop_id,
						 const GValue     *value,
						 GParamSpec       *pspec);
static void     gtk_paned_get_property          (GObject          *object,
						 guint             prop_id,
						 GValue           *value,
						 GParamSpec       *pspec);
70 71 72 73 74 75 76 77 78 79
static void gtk_paned_set_child_property        (GtkContainer      *container,
						 GtkWidget         *child,
						 guint              property_id,
						 const GValue      *value,
						 GParamSpec        *pspec);
static void gtk_paned_get_child_property        (GtkContainer      *container,
						 GtkWidget         *child,
						 guint              property_id,
						 GValue            *value,
						 GParamSpec        *pspec);
80
static void     gtk_paned_finalize              (GObject          *object);
81 82 83 84
static void     gtk_paned_realize               (GtkWidget        *widget);
static void     gtk_paned_unrealize             (GtkWidget        *widget);
static void     gtk_paned_map                   (GtkWidget        *widget);
static void     gtk_paned_unmap                 (GtkWidget        *widget);
85 86
static void     gtk_paned_state_changed         (GtkWidget        *widget,
                                                 GtkStateType      previous_state);
87
static gboolean gtk_paned_expose                (GtkWidget        *widget,
88
						 GdkEventExpose   *event);
89 90 91 92
static gboolean gtk_paned_enter                 (GtkWidget        *widget,
						 GdkEventCrossing *event);
static gboolean gtk_paned_leave                 (GtkWidget        *widget,
						 GdkEventCrossing *event);
93 94 95 96 97 98
static gboolean gtk_paned_button_press          (GtkWidget        *widget,
						 GdkEventButton   *event);
static gboolean gtk_paned_button_release        (GtkWidget        *widget,
						 GdkEventButton   *event);
static gboolean gtk_paned_motion                (GtkWidget        *widget,
						 GdkEventMotion   *event);
99 100
static gboolean gtk_paned_focus                 (GtkWidget        *widget,
						 GtkDirectionType  direction);
101 102
static gboolean gtk_paned_grab_broken           (GtkWidget          *widget,
						 GdkEventGrabBroken *event);
103 104 105 106 107 108 109 110 111 112 113 114
static void     gtk_paned_add                   (GtkContainer     *container,
						 GtkWidget        *widget);
static void     gtk_paned_remove                (GtkContainer     *container,
						 GtkWidget        *widget);
static void     gtk_paned_forall                (GtkContainer     *container,
						 gboolean          include_internals,
						 GtkCallback       callback,
						 gpointer          callback_data);
static void     gtk_paned_set_focus_child       (GtkContainer     *container,
						 GtkWidget        *child);
static void     gtk_paned_set_saved_focus       (GtkPaned         *paned,
						 GtkWidget        *widget);
115 116
static void     gtk_paned_set_first_paned       (GtkPaned         *paned,
						 GtkPaned         *first_paned);
117 118 119 120 121 122 123 124 125 126 127 128 129
static void     gtk_paned_set_last_child1_focus (GtkPaned         *paned,
						 GtkWidget        *widget);
static void     gtk_paned_set_last_child2_focus (GtkPaned         *paned,
						 GtkWidget        *widget);
static gboolean gtk_paned_cycle_child_focus     (GtkPaned         *paned,
						 gboolean          reverse);
static gboolean gtk_paned_cycle_handle_focus    (GtkPaned         *paned,
						 gboolean          reverse);
static gboolean gtk_paned_move_handle           (GtkPaned         *paned,
						 GtkScrollType     scroll);
static gboolean gtk_paned_accept_position       (GtkPaned         *paned);
static gboolean gtk_paned_cancel_position       (GtkPaned         *paned);
static gboolean gtk_paned_toggle_handle_focus   (GtkPaned         *paned);
130

131
static GType    gtk_paned_child_type            (GtkContainer     *container);
132 133
static void     gtk_paned_grab_notify           (GtkWidget        *widget,
		                                 gboolean          was_grabbed);
Elliot Lee's avatar
Elliot Lee committed
134

135 136 137
struct _GtkPanedPrivate
{
  GtkWidget *saved_focus;
138 139
  GtkPaned  *first_paned;
  guint32    grab_time;
140
};
Elliot Lee's avatar
Elliot Lee committed
141

Matthias Clasen's avatar
Matthias Clasen committed
142
G_DEFINE_ABSTRACT_TYPE (GtkPaned, gtk_paned, GTK_TYPE_CONTAINER)
Elliot Lee's avatar
Elliot Lee committed
143

144 145 146 147
static guint signals[LAST_SIGNAL] = { 0 };

static void
add_tab_bindings (GtkBindingSet    *binding_set,
148
		  GdkModifierType   modifiers)
149 150
{
  gtk_binding_entry_add_signal (binding_set, GDK_Tab, modifiers,
151
                                "toggle_handle_focus", 0);
152
  gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, modifiers,
153
				"toggle_handle_focus", 0);
154 155 156 157 158 159 160 161 162 163 164 165 166
}

static void
add_move_binding (GtkBindingSet   *binding_set,
		  guint            keyval,
		  GdkModifierType  mask,
		  GtkScrollType    scroll)
{
  gtk_binding_entry_add_signal (binding_set, keyval, mask,
				"move_handle", 1,
				GTK_TYPE_SCROLL_TYPE, scroll);
}

Elliot Lee's avatar
Elliot Lee committed
167 168 169
static void
gtk_paned_class_init (GtkPanedClass *class)
{
170
  GObjectClass *object_class;
Elliot Lee's avatar
Elliot Lee committed
171 172
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;
173 174
  GtkPanedClass *paned_class;
  GtkBindingSet *binding_set;
175

176
  object_class = (GObjectClass *) class;
177 178
  widget_class = (GtkWidgetClass *) class;
  container_class = (GtkContainerClass *) class;
179
  paned_class = (GtkPanedClass *) class;
180

181 182
  object_class->set_property = gtk_paned_set_property;
  object_class->get_property = gtk_paned_get_property;
183
  object_class->finalize = gtk_paned_finalize;
184

Elliot Lee's avatar
Elliot Lee committed
185 186
  widget_class->realize = gtk_paned_realize;
  widget_class->unrealize = gtk_paned_unrealize;
187 188
  widget_class->map = gtk_paned_map;
  widget_class->unmap = gtk_paned_unmap;
Elliot Lee's avatar
Elliot Lee committed
189
  widget_class->expose_event = gtk_paned_expose;
190
  widget_class->focus = gtk_paned_focus;
191 192 193 194 195
  widget_class->enter_notify_event = gtk_paned_enter;
  widget_class->leave_notify_event = gtk_paned_leave;
  widget_class->button_press_event = gtk_paned_button_press;
  widget_class->button_release_event = gtk_paned_button_release;
  widget_class->motion_notify_event = gtk_paned_motion;
196
  widget_class->grab_broken_event = gtk_paned_grab_broken;
197
  widget_class->grab_notify = gtk_paned_grab_notify;
198
  widget_class->state_changed = gtk_paned_state_changed;
199
  
Elliot Lee's avatar
Elliot Lee committed
200 201
  container_class->add = gtk_paned_add;
  container_class->remove = gtk_paned_remove;
202
  container_class->forall = gtk_paned_forall;
203
  container_class->child_type = gtk_paned_child_type;
204
  container_class->set_focus_child = gtk_paned_set_focus_child;
205 206
  container_class->set_child_property = gtk_paned_set_child_property;
  container_class->get_child_property = gtk_paned_get_child_property;
207 208 209 210 211 212 213

  paned_class->cycle_child_focus = gtk_paned_cycle_child_focus;
  paned_class->toggle_handle_focus = gtk_paned_toggle_handle_focus;
  paned_class->move_handle = gtk_paned_move_handle;
  paned_class->cycle_handle_focus = gtk_paned_cycle_handle_focus;
  paned_class->accept_position = gtk_paned_accept_position;
  paned_class->cancel_position = gtk_paned_cancel_position;
214

215 216 217
  g_object_class_install_property (object_class,
				   PROP_POSITION,
				   g_param_spec_int ("position",
218 219
						     P_("Position"),
						     P_("Position of paned separator in pixels (0 means all the way to the left/top)"),
220 221 222
						     0,
						     G_MAXINT,
						     0,
223
						     GTK_PARAM_READWRITE));
224 225
  g_object_class_install_property (object_class,
				   PROP_POSITION_SET,
226
				   g_param_spec_boolean ("position-set",
227 228
							 P_("Position Set"),
							 P_("TRUE if the Position property should be used"),
229
							 FALSE,
230
							 GTK_PARAM_READWRITE));
231
				   
232
  gtk_widget_class_install_style_property (widget_class,
233
					   g_param_spec_int ("handle-size",
234 235
							     P_("Handle Size"),
							     P_("Width of handle"),
236 237 238
							     0,
							     G_MAXINT,
							     5,
239
							     GTK_PARAM_READABLE));
240 241 242 243 244 245 246 247 248
  /**
   * GtkPaned:min-position:
   *
   * The smallest possible value for the position property. This property is derived from the
   * size and shrinkability of the widget's children.
   *
   * Since: 2.4
   */
  g_object_class_install_property (object_class,
Soeren Sandmann's avatar
Soeren Sandmann committed
249
				   PROP_MIN_POSITION,
250
				   g_param_spec_int ("min-position",
251 252
						     P_("Minimal Position"),
						     P_("Smallest possible value for the \"position\" property"),
253 254 255
						     0,
						     G_MAXINT,
						     0,
256
						     GTK_PARAM_READABLE));
257 258 259 260 261 262 263 264 265 266

  /**
   * GtkPaned:max-position:
   *
   * The largest possible value for the position property. This property is derived from the
   * size and shrinkability of the widget's children.
   *
   * Since: 2.4
   */
  g_object_class_install_property (object_class,
Soeren Sandmann's avatar
Soeren Sandmann committed
267
				   PROP_MAX_POSITION,
268
				   g_param_spec_int ("max-position",
269 270
						     P_("Maximal Position"),
						     P_("Largest possible value for the \"position\" property"),
271 272 273
						     0,
						     G_MAXINT,
						     G_MAXINT,
274
						     GTK_PARAM_READABLE));
275

276 277 278 279 280 281 282 283
/**
 * GtkPaned:resize:
 *
 * The "resize" child property determines whether the child expands and 
 * shrinks along with the paned widget.
 * 
 * Since: 2.4 
 */
284 285 286
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_RESIZE,
					      g_param_spec_boolean ("resize", 
287 288
								    P_("Resize"),
								    P_("If TRUE, the child expands and shrinks along with the paned widget"),
289
								    TRUE,
290
								    GTK_PARAM_READWRITE));
291

292 293 294 295 296 297 298 299
/**
 * GtkPaned:shrink:
 *
 * The "shrink" child property determines whether the child can be made 
 * smaller than its requisition.
 * 
 * Since: 2.4 
 */
300 301 302
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_SHRINK,
					      g_param_spec_boolean ("shrink", 
303 304
								    P_("Shrink"),
								    P_("If TRUE, the child can be made smaller than its requisition"),
305
								    TRUE,
306
								    GTK_PARAM_READWRITE));
307

308 309 310 311 312 313 314 315 316 317 318 319 320
  /**
   * GtkPaned::cycle-child-focus:
   * @widget: the object that received the signal
   * @reversed: whether cycling backward or forward
   *
   * The ::cycle-child-focus signal is a 
   * <link linkend="keybinding-signals">keybinding signal</link>
   * which gets emitted to cycle the focus between the children of the paned.
   *
   * The default binding is f6.
   *
   * Since: 2.0
   */
321
  signals [CYCLE_CHILD_FOCUS] =
Matthias Clasen's avatar
Matthias Clasen committed
322
    g_signal_new (I_("cycle_child_focus"),
323 324 325 326 327 328 329 330
		  G_TYPE_FROM_CLASS (object_class),
		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkPanedClass, cycle_child_focus),
		  NULL, NULL,
		  _gtk_marshal_BOOLEAN__BOOLEAN,
		  G_TYPE_BOOLEAN, 1,
		  G_TYPE_BOOLEAN);

331 332 333 334 335 336 337 338 339 340 341 342 343
  /**
   * GtkPaned::toggle-handle-focus:
   * @widget: the object that received the signal
   *
   * The ::toggle-handle-focus is a 
   * <link linkend="keybinding-signals">keybinding signal</link>
   * which gets emitted to accept the current position of the handle and then 
   * move focus to the next widget in the focus chain.
   *
   * The default binding is Tab.
   *
   * Since: 2.0
   */
344
  signals [TOGGLE_HANDLE_FOCUS] =
Matthias Clasen's avatar
Matthias Clasen committed
345
    g_signal_new (I_("toggle_handle_focus"),
346 347 348 349 350 351 352
		  G_TYPE_FROM_CLASS (object_class),
		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkPanedClass, toggle_handle_focus),
		  NULL, NULL,
		  _gtk_marshal_BOOLEAN__VOID,
		  G_TYPE_BOOLEAN, 0);

353 354 355 356 357 358 359 360 361 362 363 364
  /**
   * GtkPaned::move-handle:
   * @widget: the object that received the signal
   * @scroll_type: a #GtkScrollType
   *
   * The ::move-handle signal is a 
   * <link linkend="keybinding-signals">keybinding signal</link>
   * which gets emitted to move the handle when the user is using key bindings 
   * to move it.
   *
   * Since: 2.0
   */
365
  signals[MOVE_HANDLE] =
Matthias Clasen's avatar
Matthias Clasen committed
366
    g_signal_new (I_("move_handle"),
367 368 369 370 371 372 373 374
		  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkPanedClass, move_handle),
                  NULL, NULL,
                  _gtk_marshal_BOOLEAN__ENUM,
                  G_TYPE_BOOLEAN, 1,
                  GTK_TYPE_SCROLL_TYPE);

375 376 377 378 379 380 381 382 383 384 385 386 387 388
  /**
   * GtkPaned::cycle-handle-focus:
   * @widget: the object that received the signal
   * @reversed: whether cycling backward or forward
   *
   * The ::cycle-handle-focus signal is a 
   * <link linkend="keybinding-signals">keybinding signal</link>
   * which gets emitted to cycle whether the paned should grab focus to allow
   * the user to change position of the handle by using key bindings.
   *
   * The default binding for this signal is f8.
   *
   * Since: 2.0
   */
389
  signals [CYCLE_HANDLE_FOCUS] =
Matthias Clasen's avatar
Matthias Clasen committed
390
    g_signal_new (I_("cycle_handle_focus"),
391 392 393 394 395 396 397 398
		  G_TYPE_FROM_CLASS (object_class),
		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkPanedClass, cycle_handle_focus),
		  NULL, NULL,
		  _gtk_marshal_BOOLEAN__BOOLEAN,
		  G_TYPE_BOOLEAN, 1,
		  G_TYPE_BOOLEAN);

399 400 401 402 403 404 405 406 407 408 409 410 411
  /**
   * GtkPaned::accept-position:
   * @widget: the object that received the signal
   *
   * The ::accept-position signal is a 
   * <link linkend="keybinding-signals">keybinding signal</link>
   * which gets emitted to accept the current position of the handle when 
   * moving it using key bindings.
   *
   * The default binding for this signal is Return or Space.
   *
   * Since: 2.0
   */
412
  signals [ACCEPT_POSITION] =
Matthias Clasen's avatar
Matthias Clasen committed
413
    g_signal_new (I_("accept_position"),
414 415 416 417 418 419 420
		  G_TYPE_FROM_CLASS (object_class),
		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkPanedClass, accept_position),
		  NULL, NULL,
		  _gtk_marshal_BOOLEAN__VOID,
		  G_TYPE_BOOLEAN, 0);

421 422 423 424 425 426 427 428 429 430 431 432 433 434
  /**
   * GtkPaned::cancel-position:
   * @widget: the object that received the signal
   *
   * The ::cancel-position signal is a 
   * <link linkend="keybinding-signals">keybinding signal</link>
   * which gets emitted to cancel moving the position of the handle using key 
   * bindings. The position of the handle will be reset to the value prior to 
   * moving it.
   *
   * The default binding for this signal is Escape.
   *
   * Since: 2.0
   */
435
  signals [CANCEL_POSITION] =
Matthias Clasen's avatar
Matthias Clasen committed
436
    g_signal_new (I_("cancel_position"),
437 438 439 440 441 442 443
		  G_TYPE_FROM_CLASS (object_class),
		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkPanedClass, cancel_position),
		  NULL, NULL,
		  _gtk_marshal_BOOLEAN__VOID,
		  G_TYPE_BOOLEAN, 0);

Manish Singh's avatar
Manish Singh committed
444
  binding_set = gtk_binding_set_by_class (class);
445 446 447 448 449 450 451 452 453 454 455 456 457 458

  /* F6 and friends */
  gtk_binding_entry_add_signal (binding_set,				
                                GDK_F6, 0,
                                "cycle_child_focus", 1, 
                                G_TYPE_BOOLEAN, FALSE);
  gtk_binding_entry_add_signal (binding_set,
				GDK_F6, GDK_SHIFT_MASK,
				"cycle_child_focus", 1,
				G_TYPE_BOOLEAN, TRUE);

  /* F8 and friends */
  gtk_binding_entry_add_signal (binding_set,
				GDK_F8, 0,
459 460 461 462 463 464 465
				"cycle_handle_focus", 1,
				G_TYPE_BOOLEAN, FALSE);
 
  gtk_binding_entry_add_signal (binding_set,
				GDK_F8, GDK_SHIFT_MASK,
				"cycle_handle_focus", 1,
				G_TYPE_BOOLEAN, TRUE);
466
 
467 468 469 470
  add_tab_bindings (binding_set, 0);
  add_tab_bindings (binding_set, GDK_CONTROL_MASK);
  add_tab_bindings (binding_set, GDK_SHIFT_MASK);
  add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK);
471 472 473 474 475 476 477 478 479

  /* accept and cancel positions */
  gtk_binding_entry_add_signal (binding_set,
				GDK_Escape, 0,
				"cancel_position", 0);

  gtk_binding_entry_add_signal (binding_set,
				GDK_Return, 0,
				"accept_position", 0);
480 481 482
  gtk_binding_entry_add_signal (binding_set,
				GDK_ISO_Enter, 0,
				"accept_position", 0);
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Enter, 0,
				"accept_position", 0);
  gtk_binding_entry_add_signal (binding_set,
				GDK_space, 0,
				"accept_position", 0);
  gtk_binding_entry_add_signal (binding_set,
				GDK_KP_Space, 0,
				"accept_position", 0);

  /* move handle */
  add_move_binding (binding_set, GDK_Left, 0, GTK_SCROLL_STEP_LEFT);
  add_move_binding (binding_set, GDK_KP_Left, 0, GTK_SCROLL_STEP_LEFT);
  add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT);
  add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT);

  add_move_binding (binding_set, GDK_Right, 0, GTK_SCROLL_STEP_RIGHT);
  add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT);
  add_move_binding (binding_set, GDK_KP_Right, 0, GTK_SCROLL_STEP_RIGHT);
  add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT);

  add_move_binding (binding_set, GDK_Up, 0, GTK_SCROLL_STEP_UP);
  add_move_binding (binding_set, GDK_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP);
  add_move_binding (binding_set, GDK_KP_Up, 0, GTK_SCROLL_STEP_UP);
  add_move_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP);
  add_move_binding (binding_set, GDK_Page_Up, 0, GTK_SCROLL_PAGE_UP);
  add_move_binding (binding_set, GDK_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP);

  add_move_binding (binding_set, GDK_Down, 0, GTK_SCROLL_STEP_DOWN);
  add_move_binding (binding_set, GDK_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN);
  add_move_binding (binding_set, GDK_KP_Down, 0, GTK_SCROLL_STEP_DOWN);
  add_move_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN);
  add_move_binding (binding_set, GDK_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);
  add_move_binding (binding_set, GDK_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);

  add_move_binding (binding_set, GDK_Home, 0, GTK_SCROLL_START);
  add_move_binding (binding_set, GDK_KP_Home, 0, GTK_SCROLL_START);
  add_move_binding (binding_set, GDK_End, 0, GTK_SCROLL_END);
  add_move_binding (binding_set, GDK_KP_End, 0, GTK_SCROLL_END);
522 523

  g_type_class_add_private (object_class, sizeof (GtkPanedPrivate));  
524 525
}

Manish Singh's avatar
Manish Singh committed
526
static GType
527 528 529 530 531
gtk_paned_child_type (GtkContainer *container)
{
  if (!GTK_PANED (container)->child1 || !GTK_PANED (container)->child2)
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
532
    return G_TYPE_NONE;
Elliot Lee's avatar
Elliot Lee committed
533 534 535 536 537
}

static void
gtk_paned_init (GtkPaned *paned)
{
538
  GTK_WIDGET_SET_FLAGS (paned, GTK_NO_WINDOW | GTK_CAN_FOCUS);
539
  
Elliot Lee's avatar
Elliot Lee committed
540 541 542 543
  paned->child1 = NULL;
  paned->child2 = NULL;
  paned->handle = NULL;
  paned->xor_gc = NULL;
544
  paned->cursor_type = GDK_CROSS;
545
  
546 547
  paned->handle_pos.width = 5;
  paned->handle_pos.height = 5;
Elliot Lee's avatar
Elliot Lee committed
548
  paned->position_set = FALSE;
Owen Taylor's avatar
Owen Taylor committed
549
  paned->last_allocation = -1;
Elliot Lee's avatar
Elliot Lee committed
550
  paned->in_drag = FALSE;
551

552
  paned->priv = G_TYPE_INSTANCE_GET_PRIVATE (paned, GTK_TYPE_PANED, GtkPanedPrivate);
553 554 555
  paned->last_child1_focus = NULL;
  paned->last_child2_focus = NULL;
  paned->in_recursion = FALSE;
556
  paned->handle_prelit = FALSE;
557
  paned->original_position = -1;
558
  
559 560
  paned->handle_pos.x = -1;
  paned->handle_pos.y = -1;
561 562

  paned->drag_pos = -1;
Elliot Lee's avatar
Elliot Lee committed
563 564
}

565
static void
566 567 568 569
gtk_paned_set_property (GObject        *object,
			guint           prop_id,
			const GValue   *value,
			GParamSpec     *pspec)
570
{
571 572 573
  GtkPaned *paned = GTK_PANED (object);
  
  switch (prop_id)
574
    {
575 576 577 578 579 580 581
    case PROP_POSITION:
      gtk_paned_set_position (paned, g_value_get_int (value));
      break;
    case PROP_POSITION_SET:
      paned->position_set = g_value_get_boolean (value);
      gtk_widget_queue_resize (GTK_WIDGET (paned));
      break;
582
    default:
583
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
584 585 586 587 588
      break;
    }
}

static void
589 590 591 592
gtk_paned_get_property (GObject        *object,
			guint           prop_id,
			GValue         *value,
			GParamSpec     *pspec)
593
{
594 595 596
  GtkPaned *paned = GTK_PANED (object);
  
  switch (prop_id)
597
    {
598 599 600 601 602 603
    case PROP_POSITION:
      g_value_set_int (value, paned->child1_size);
      break;
    case PROP_POSITION_SET:
      g_value_set_boolean (value, paned->position_set);
      break;
604 605 606 607 608 609
    case PROP_MIN_POSITION:
      g_value_set_int (value, paned->min_position);
      break;
    case PROP_MAX_POSITION:
      g_value_set_int (value, paned->max_position);
      break;
610
    default:
611
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
612 613 614
      break;
    }
}
Elliot Lee's avatar
Elliot Lee committed
615

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
static void
gtk_paned_set_child_property (GtkContainer    *container,
			      GtkWidget       *child,
			      guint            property_id,
			      const GValue    *value,
			      GParamSpec      *pspec)
{
  GtkPaned *paned = GTK_PANED (container);
  gboolean old_value, new_value;

  g_assert (child == paned->child1 || child == paned->child2);

  new_value = g_value_get_boolean (value);
  switch (property_id)
    {
    case CHILD_PROP_RESIZE:
      if (child == paned->child1)
	{
	  old_value = paned->child1_resize;
	  paned->child1_resize = new_value;
	}
      else
	{
	  old_value = paned->child2_resize;
	  paned->child2_resize = new_value;
	}
      break;
    case CHILD_PROP_SHRINK:
      if (child == paned->child1)
	{
	  old_value = paned->child1_shrink;
	  paned->child1_shrink = new_value;
	}
      else
	{
	  old_value = paned->child2_shrink;
	  paned->child2_shrink = new_value;
	}
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
657
      old_value = -1; /* quiet gcc */
658 659 660
      break;
    }
  if (old_value != new_value)
661
    gtk_widget_queue_resize (GTK_WIDGET (container));
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
}

static void
gtk_paned_get_child_property (GtkContainer *container,
			      GtkWidget    *child,
			      guint         property_id,
			      GValue       *value,
			      GParamSpec   *pspec)
{
  GtkPaned *paned = GTK_PANED (container);

  g_assert (child == paned->child1 || child == paned->child2);
  
  switch (property_id)
    {
    case CHILD_PROP_RESIZE:
      if (child == paned->child1)
	g_value_set_boolean (value, paned->child1_resize);
      else
	g_value_set_boolean (value, paned->child2_resize);
      break;
    case CHILD_PROP_SHRINK:
      if (child == paned->child1)
	g_value_set_boolean (value, paned->child1_shrink);
      else
	g_value_set_boolean (value, paned->child2_shrink);
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}

695 696 697 698 699 700 701 702
static void
gtk_paned_finalize (GObject *object)
{
  GtkPaned *paned = GTK_PANED (object);
  
  gtk_paned_set_saved_focus (paned, NULL);
  gtk_paned_set_first_paned (paned, NULL);

Matthias Clasen's avatar
Matthias Clasen committed
703
  G_OBJECT_CLASS (gtk_paned_parent_class)->finalize (object);
704 705
}

Elliot Lee's avatar
Elliot Lee committed
706 707 708 709 710 711
static void
gtk_paned_realize (GtkWidget *widget)
{
  GtkPaned *paned;
  GdkWindowAttr attributes;
  gint attributes_mask;
712

Elliot Lee's avatar
Elliot Lee committed
713 714
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  paned = GTK_PANED (widget);
715

716
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
717
  g_object_ref (widget->window);
718
  
719
  attributes.window_type = GDK_WINDOW_CHILD;
720 721 722 723 724
  attributes.wclass = GDK_INPUT_ONLY;
  attributes.x = paned->handle_pos.x;
  attributes.y = paned->handle_pos.y;
  attributes.width = paned->handle_pos.width;
  attributes.height = paned->handle_pos.height;
725
  attributes.event_mask = gtk_widget_get_events (widget);
726
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
Elliot Lee's avatar
Elliot Lee committed
727
			    GDK_BUTTON_RELEASE_MASK |
728 729
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK |
Elliot Lee's avatar
Elliot Lee committed
730 731
			    GDK_POINTER_MOTION_MASK |
			    GDK_POINTER_MOTION_HINT_MASK);
732 733 734 735 736 737 738
  attributes_mask = GDK_WA_X | GDK_WA_Y;
  if (GTK_WIDGET_IS_SENSITIVE (widget))
    {
      attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
						      paned->cursor_type);
      attributes_mask |= GDK_WA_CURSOR;
    }
739

740 741 742
  paned->handle = gdk_window_new (widget->window,
				  &attributes, attributes_mask);
  gdk_window_set_user_data (paned->handle, paned);
743 744
  if (attributes_mask & GDK_WA_CURSOR)
    gdk_cursor_unref (attributes.cursor);
745

Elliot Lee's avatar
Elliot Lee committed
746
  widget->style = gtk_style_attach (widget->style, widget->window);
747

748 749 750
  if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1) &&
      paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
    gdk_window_show (paned->handle);
751
}
752

Elliot Lee's avatar
Elliot Lee committed
753 754 755
static void
gtk_paned_unrealize (GtkWidget *widget)
{
756
  GtkPaned *paned = GTK_PANED (widget);
757

Elliot Lee's avatar
Elliot Lee committed
758
  if (paned->xor_gc)
759
    {
Manish Singh's avatar
Manish Singh committed
760
      g_object_unref (paned->xor_gc);
761 762
      paned->xor_gc = NULL;
    }
763

Elliot Lee's avatar
Elliot Lee committed
764
  if (paned->handle)
765
    {
766
      gdk_window_set_user_data (paned->handle, NULL);
767
      gdk_window_destroy (paned->handle);
768
      paned->handle = NULL;
769
    }
770

771 772 773
  gtk_paned_set_last_child1_focus (paned, NULL);
  gtk_paned_set_last_child2_focus (paned, NULL);
  gtk_paned_set_saved_focus (paned, NULL);
774
  gtk_paned_set_first_paned (paned, NULL);
775
  
Matthias Clasen's avatar
Matthias Clasen committed
776 777
  if (GTK_WIDGET_CLASS (gtk_paned_parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (gtk_paned_parent_class)->unrealize) (widget);
Elliot Lee's avatar
Elliot Lee committed
778 779
}

780 781 782 783
static void
gtk_paned_map (GtkWidget *widget)
{
  GtkPaned *paned = GTK_PANED (widget);
784

785 786
  gdk_window_show (paned->handle);

Matthias Clasen's avatar
Matthias Clasen committed
787
  GTK_WIDGET_CLASS (gtk_paned_parent_class)->map (widget);
788 789 790 791 792 793 794 795 796
}

static void
gtk_paned_unmap (GtkWidget *widget)
{
  GtkPaned *paned = GTK_PANED (widget);
    
  gdk_window_hide (paned->handle);

Matthias Clasen's avatar
Matthias Clasen committed
797
  GTK_WIDGET_CLASS (gtk_paned_parent_class)->unmap (widget);
798
}
Havoc Pennington's avatar
Havoc Pennington committed
799

800
static gboolean
Elliot Lee's avatar
Elliot Lee committed
801 802 803
gtk_paned_expose (GtkWidget      *widget,
		  GdkEventExpose *event)
{
804
  GtkPaned *paned = GTK_PANED (widget);
805

806 807 808
  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
      paned->child1 && GTK_WIDGET_VISIBLE (paned->child1) &&
      paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
Elliot Lee's avatar
Elliot Lee committed
809
    {
810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
      GtkStateType state;
      
      if (gtk_widget_is_focus (widget))
	state = GTK_STATE_SELECTED;
      else if (paned->handle_prelit)
	state = GTK_STATE_PRELIGHT;
      else
	state = GTK_WIDGET_STATE (widget);
      
      gtk_paint_handle (widget->style, widget->window,
			state, GTK_SHADOW_NONE,
			&paned->handle_pos, widget, "paned",
			paned->handle_pos.x, paned->handle_pos.y,
			paned->handle_pos.width, paned->handle_pos.height,
			paned->orientation);
Elliot Lee's avatar
Elliot Lee committed
825
    }
826

827
  /* Chain up to draw children */
Matthias Clasen's avatar
Matthias Clasen committed
828
  GTK_WIDGET_CLASS (gtk_paned_parent_class)->expose_event (widget, event);
829
  
Elliot Lee's avatar
Elliot Lee committed
830 831 832
  return FALSE;
}

833 834 835 836 837 838 839 840 841 842 843 844
static gboolean
is_rtl (GtkPaned *paned)
{
  if (paned->orientation == GTK_ORIENTATION_VERTICAL &&
      gtk_widget_get_direction (GTK_WIDGET (paned)) == GTK_TEXT_DIR_RTL)
    {
      return TRUE;
    }

  return FALSE;
}

845 846 847 848 849 850 851 852 853 854 855 856
static void
update_drag (GtkPaned *paned)
{
  gint pos;
  gint handle_size;
  gint size;
  
  if (paned->orientation == GTK_ORIENTATION_HORIZONTAL)
    gtk_widget_get_pointer (GTK_WIDGET (paned), NULL, &pos);
  else
    gtk_widget_get_pointer (GTK_WIDGET (paned), &pos, NULL);

857 858 859 860 861
  pos -= paned->drag_pos;

  if (is_rtl (paned))
    {
      gtk_widget_style_get (GTK_WIDGET (paned),
862
			    "handle-size", &handle_size,
863 864 865 866 867 868 869 870 871 872
			    NULL);
      
      size = GTK_WIDGET (paned)->allocation.width - pos - handle_size;
    }
  else
    {
      size = pos;
    }

  size -= GTK_CONTAINER (paned)->border_width;
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
  
  size = CLAMP (size, paned->min_position, paned->max_position);

  if (size != paned->child1_size)
    gtk_paned_set_position (paned, size);
}

static gboolean
gtk_paned_enter (GtkWidget        *widget,
		 GdkEventCrossing *event)
{
  GtkPaned *paned = GTK_PANED (widget);
  
  if (paned->in_drag)
    update_drag (paned);
  else
    {
      paned->handle_prelit = TRUE;
891 892 893 894 895
      gtk_widget_queue_draw_area (widget,
				  paned->handle_pos.x,
				  paned->handle_pos.y,
				  paned->handle_pos.width,
				  paned->handle_pos.height);
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
    }
  
  return TRUE;
}

static gboolean
gtk_paned_leave (GtkWidget        *widget,
		 GdkEventCrossing *event)
{
  GtkPaned *paned = GTK_PANED (widget);
  
  if (paned->in_drag)
    update_drag (paned);
  else
    {
      paned->handle_prelit = FALSE;
912 913 914 915 916
      gtk_widget_queue_draw_area (widget,
				  paned->handle_pos.x,
				  paned->handle_pos.y,
				  paned->handle_pos.width,
				  paned->handle_pos.height);
917 918 919 920 921
    }

  return TRUE;
}

922 923 924 925 926 927 928 929 930 931 932 933
static gboolean
gtk_paned_focus (GtkWidget        *widget,
		 GtkDirectionType  direction)

{
  gboolean retval;
  
  /* This is a hack, but how can this be done without
   * excessive cut-and-paste from gtkcontainer.c?
   */

  GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS);
Matthias Clasen's avatar
Matthias Clasen committed
934
  retval = (* GTK_WIDGET_CLASS (gtk_paned_parent_class)->focus) (widget, direction);
935 936 937 938 939
  GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);

  return retval;
}

940 941 942 943 944 945 946 947 948 949 950
static gboolean
gtk_paned_button_press (GtkWidget      *widget,
			GdkEventButton *event)
{
  GtkPaned *paned = GTK_PANED (widget);

  if (!paned->in_drag &&
      (event->window == paned->handle) && (event->button == 1))
    {
      /* We need a server grab here, not gtk_grab_add(), since
       * we don't want to pass events on to the widget's children */
951 952 953 954 955 956 957 958 959 960 961 962
      if (gdk_pointer_grab (paned->handle, FALSE,
			    GDK_POINTER_MOTION_HINT_MASK
			    | GDK_BUTTON1_MOTION_MASK
			    | GDK_BUTTON_RELEASE_MASK
			    | GDK_ENTER_NOTIFY_MASK
			    | GDK_LEAVE_NOTIFY_MASK,
			    NULL, NULL,
			    event->time) != GDK_GRAB_SUCCESS)
	return FALSE;

      paned->in_drag = TRUE;
      paned->priv->grab_time = event->time;
963 964 965 966 967 968 969 970 971 972 973 974

      if (paned->orientation == GTK_ORIENTATION_HORIZONTAL)
	paned->drag_pos = event->y;
      else
	paned->drag_pos = event->x;
      
      return TRUE;
    }

  return FALSE;
}

975 976 977 978 979 980 981 982 983
static gboolean
gtk_paned_grab_broken (GtkWidget          *widget,
		       GdkEventGrabBroken *event)
{
  GtkPaned *paned = GTK_PANED (widget);

  paned->in_drag = FALSE;
  paned->drag_pos = -1;
  paned->position_set = TRUE;
984 985

  return TRUE;
986 987
}

988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
static void
stop_drag (GtkPaned *paned)
{
  paned->in_drag = FALSE;
  paned->drag_pos = -1;
  paned->position_set = TRUE;
  gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (paned)),
			      paned->priv->grab_time);
}

static void
gtk_paned_grab_notify (GtkWidget *widget,
		       gboolean   was_grabbed)
{
  GtkPaned *paned = GTK_PANED (widget);

  if (!was_grabbed && paned->in_drag)
    stop_drag (paned);
}

1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
static void
gtk_paned_state_changed (GtkWidget    *widget,
                         GtkStateType  previous_state)
{
  GtkPaned *paned = GTK_PANED (widget);
  GdkCursor *cursor;

  if (GTK_WIDGET_REALIZED (paned))
    {
      if (GTK_WIDGET_IS_SENSITIVE (widget))
        cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
                                             paned->cursor_type); 
      else
        cursor = NULL;

      gdk_window_set_cursor (paned->handle, cursor);

      if (cursor)
        gdk_cursor_unref (cursor);
    }
}

1030 1031 1032 1033 1034 1035 1036 1037
static gboolean
gtk_paned_button_release (GtkWidget      *widget,
			  GdkEventButton *event)
{
  GtkPaned *paned = GTK_PANED (widget);

  if (paned->in_drag && (event->button == 1))
    {
1038 1039
      stop_drag (paned);

1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060
      return TRUE;
    }

  return FALSE;
}

static gboolean
gtk_paned_motion (GtkWidget      *widget,
		  GdkEventMotion *event)
{
  GtkPaned *paned = GTK_PANED (widget);
  
  if (paned->in_drag)
    {
      update_drag (paned);
      return TRUE;
    }
  
  return FALSE;
}

Elliot Lee's avatar
Elliot Lee committed
1061
void
1062 1063
gtk_paned_add1 (GtkPaned  *paned,
		GtkWidget *widget)
Elliot Lee's avatar
Elliot Lee committed
1064
{
Owen Taylor's avatar
Owen Taylor committed
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
  gtk_paned_pack1 (paned, widget, FALSE, TRUE);
}

void
gtk_paned_add2 (GtkPaned  *paned,
		GtkWidget *widget)
{
  gtk_paned_pack2 (paned, widget, TRUE, TRUE);
}

void
1076 1077 1078 1079
gtk_paned_pack1 (GtkPaned  *paned,
		 GtkWidget *child,
		 gboolean   resize,
		 gboolean   shrink)
Owen Taylor's avatar
Owen Taylor committed
1080 1081
{
  g_return_if_fail (GTK_IS_PANED (paned));
1082
  g_return_if_fail (GTK_IS_WIDGET (child));
1083

Elliot Lee's avatar
Elliot Lee committed
1084 1085
  if (!paned->child1)
    {
1086
      paned->child1 = child;
Owen Taylor's avatar
Owen Taylor committed
1087 1088
      paned->child1_resize = resize;
      paned->child1_shrink = shrink;
1089 1090

      gtk_widget_set_parent (child, GTK_WIDGET (paned));
Elliot Lee's avatar
Elliot Lee committed
1091 1092 1093 1094
    }
}

void
Owen Taylor's avatar
Owen Taylor committed
1095
gtk_paned_pack2 (GtkPaned  *paned,
1096
		 GtkWidget *child,
Owen Taylor's avatar
Owen Taylor committed
1097 1098
		 gboolean   resize,
		 gboolean   shrink)
Elliot Lee's avatar
Elliot Lee committed
1099
{
Owen Taylor's avatar
Owen Taylor committed
1100
  g_return_if_fail (GTK_IS_PANED (paned));
1101
  g_return_if_fail (GTK_IS_WIDGET (child));
1102

Elliot Lee's avatar
Elliot Lee committed
1103 1104
  if (!paned->child2)
    {
1105
      paned->child2 = child;
Owen Taylor's avatar
Owen Taylor committed
1106 1107
      paned->child2_resize = resize;
      paned->child2_shrink = shrink;
1108

1109
      gtk_widget_set_parent (child, GTK_WIDGET (paned));
Elliot Lee's avatar
Elliot Lee committed
1110 1111 1112
    }
}

1113

Elliot Lee's avatar
Elliot Lee committed
1114 1115 1116 1117 1118
static void
gtk_paned_add (GtkContainer *container,
	       GtkWidget    *widget)
{
  GtkPaned *paned;
1119

Elliot Lee's avatar
Elliot Lee committed
1120
  g_return_if_fail (GTK_IS_PANED (container));
1121

Elliot Lee's avatar
Elliot Lee committed
1122
  paned = GTK_PANED (container);
1123

Elliot Lee's avatar
Elliot Lee committed
1124
  if (!paned->child1)
1125
    gtk_paned_add1 (paned, widget);
Elliot Lee's avatar
Elliot Lee committed
1126
  else if (!paned->child2)
1127
    gtk_paned_add2 (paned, widget);
Matthias Clasen's avatar
Matthias Clasen committed
1128 1129
  else
    g_warning ("GtkPaned cannot have more than 2 children\n");
Elliot Lee's avatar
Elliot Lee committed
1130 1131 1132 1133 1134 1135 1136
}

static void
gtk_paned_remove (GtkContainer *container,
		  GtkWidget    *widget)
{
  GtkPaned *paned;
1137
  gboolean was_visible;
1138

Elliot Lee's avatar
Elliot Lee committed
1139
  paned = GTK_PANED (container);
1140
  was_visible = GTK_WIDGET_VISIBLE (widget);
1141

Elliot Lee's avatar
Elliot Lee committed
1142 1143 1144
  if (paned->child1 == widget)
    {
      gtk_widget_unparent (widget);
1145

Elliot Lee's avatar
Elliot Lee committed
1146
      paned->child1 = NULL;
1147

1148
      if (was_visible && GTK_WIDGET_VISIBLE (container))
1149
	gtk_widget_queue_resize (GTK_WIDGET (container));
Elliot Lee's avatar
Elliot Lee committed
1150 1151 1152 1153
    }
  else if (paned->child2 == widget)
    {
      gtk_widget_unparent (widget);
1154

Elliot Lee's avatar
Elliot Lee committed
1155
      paned->child2 = NULL;
1156

1157
      if (was_visible && GTK_WIDGET_VISIBLE (container))
1158
	gtk_widget_queue_resize (GTK_WIDGET (container));
Elliot Lee's avatar
Elliot Lee committed
1159 1160 1161 1162
    }
}

static void
1163
gtk_paned_forall (GtkContainer *container,
1164
		  gboolean      include_internals,
1165 1166
		  GtkCallback   callback,
		  gpointer      callback_data)
Elliot Lee's avatar
Elliot Lee committed
1167 1168
{
  GtkPaned *paned;
1169

Elliot Lee's avatar
Elliot Lee committed
1170
  g_return_if_fail (callback != NULL);
1171

Elliot Lee's avatar
Elliot Lee committed
1172
  paned = GTK_PANED (container);
1173

Elliot Lee's avatar
Elliot Lee committed
1174
  if (paned->child1)
1175
    (*callback) (paned->child1, callback_data);
Elliot Lee's avatar
Elliot Lee committed
1176
  if (paned->child2)
1177
    (*callback) (paned->child2, callback_data);
Elliot Lee's avatar
Elliot Lee committed
1178 1179
}

1180 1181 1182 1183 1184 1185 1186 1187
/**
 * gtk_paned_get_position:
 * @paned: a #GtkPaned widget
 * 
 * Obtains the position of the divider between the two panes.
 * 
 * Return value: position of the divider
 **/
Jonathan Blandford's avatar
Jonathan Blandford committed
1188 1189 1190
gint
gtk_paned_get_position (GtkPaned  *paned)
{
1191
  g_return_val_if_fail (GTK_IS_PANED (paned), 0);
Jonathan Blandford's avatar
Jonathan Blandford committed
1192 1193 1194 1195

  return paned->child1_size;
}

1196 1197 1198
/**
 * gtk_paned_set_position:
 * @paned: a #GtkPaned widget
1199 1200
 * @position: pixel position of divider, a negative value means that the position
 *            is unset.
1201 1202 1203
 * 
 * Sets the position of the divider between the two panes.
 **/
Owen Taylor's avatar
Owen Taylor committed
1204
void
1205 1206
gtk_paned_set_position (GtkPaned *paned,
			gint      position)
Owen Taylor's avatar
Owen Taylor committed
1207
{
1208 1209
  GObject *object;
  
Owen Taylor's avatar
Owen Taylor committed
1210 1211
  g_return_if_fail (GTK_IS_PANED (paned));

1212 1213
  object = G_OBJECT (paned);
  
Owen Taylor's avatar
Owen Taylor committed
1214 1215
  if (position >= 0)
    {
1216 1217 1218 1219 1220 1221
      /* We don't clamp here - the assumption is that
       * if the total allocation changes at the same time
       * as the position, the position set is with reference
       * to the new total size. If only the position changes,
       * then clamping will occur in gtk_paned_compute_position()
       */
1222

1223
      paned->child1_size = position;
Owen Taylor's avatar
Owen Taylor committed
1224 1225 1226
      paned->position_set = TRUE;
    }
  else
1227 1228 1229
    {
      paned->position_set = FALSE;
    }
Owen Taylor's avatar
Owen Taylor committed
1230

1231 1232
  g_object_freeze_notify (object);
  g_object_notify (object, "position");
1233
  g_object_notify (object, "position-set");
1234 1235
  g_object_thaw_notify (object);

Owen Taylor's avatar
Owen Taylor committed
1236
  gtk_widget_queue_resize (GTK_WIDGET (paned));
1237 1238 1239

#ifdef G_OS_WIN32
  /* Hacky work-around for bug #144269 */
1240 1241 1242 1243
  if (paned->child2 != NULL)
    {
      gtk_widget_queue_draw (paned->child2);
    }
1244
#endif
Owen Taylor's avatar
Owen Taylor committed
1245 1246
}

1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
/**
 * gtk_paned_get_child1:
 * @paned: a #GtkPaned widget
 * 
 * Obtains the first child of the paned widget.
 * 
 * Return value: first child, or %NULL if it is not set.
 *
 * Since: 2.4
 **/
GtkWidget *
gtk_paned_get_child1 (GtkPaned *paned)
{
  g_return_val_if_fail (GTK_IS_PANED (paned), NULL);

  return paned->child1;
}

/**
 * gtk_paned_get_child2:
 * @paned: a #GtkPaned widget
 * 
 * Obtains the second child of the paned widget.
 * 
 * Return value: second child, or %NULL if it is not set.
 *
 * Since: 2.4
 **/
GtkWidget *
gtk_paned_get_child2 (GtkPaned *paned)
{
  g_return_val_if_fail (GTK_IS_PANED (paned), NULL);

  return paned->child2;
}

Owen Taylor's avatar
Owen Taylor committed
1283
void
1284 1285 1286 1287
gtk_paned_compute_position (GtkPaned *paned,
			    gint      allocation,
			    gint      child1_req,
			    gint      child2_req)
Owen Taylor's avatar
Owen Taylor committed
1288
{
1289
  gint old_position;
1290 1291
  gint old_min_position;
  gint old_max_position;
1292
  
Owen Taylor's avatar
Owen Taylor committed
1293 1294
  g_return_if_fail (GTK_IS_PANED (paned));

1295
  old_position = paned->child1_size;
1296 1297
  old_min_position = paned->min_position;
  old_max_position = paned->max_position;
1298

Owen Taylor's avatar
Owen Taylor committed
1299 1300 1301 1302
  paned->min_position = paned->child1_shrink ? 0 : child1_req;

  paned->max_position = allocation;
  if (!paned->child2_shrink)
Owen Taylor's avatar
Owen Taylor committed
1303
    paned->max_position = MAX (1, paned->max_position - child2_req);
1304
  paned->max_position = MAX (paned->min_position, paned->max_position);
Owen Taylor's avatar
Owen Taylor committed
1305 1306 1307 1308

  if (!paned->position_set)
    {
      if (paned->child1_resize && !paned->child2_resize)
1309
	paned->child1_size = MAX (0, allocation - child2_req);
Owen Taylor's avatar
Owen Taylor committed
1310 1311
      else if (!paned->child1_resize && paned->child2_resize)
	paned->child1_size = child1_req;
Owen Taylor's avatar
Owen Taylor committed
1312
      else if (child1_req + child2_req != 0)
1313
	paned->child1_size = allocation * ((gdouble)child1_req / (child1_req + child2_req)) + 0.5;
Owen Taylor's avatar
Owen Taylor committed
1314
      else
1315
	paned->child1_size = allocation * 0.5 + 0.5;
Owen Taylor's avatar
Owen Taylor committed
1316 1317 1318
    }
  else
    {
1319
      /* If the position was set before the initial allocation.
Owen Taylor's avatar
Owen Taylor committed
1320
       * (paned->last_allocation <= 0) just clamp it and leave it.
1321
       */
Owen Taylor's avatar
Owen Taylor committed
1322
      if (paned->last_allocation > 0)
1323 1324
	{
	  if (paned->child1_resize && !paned->child2_resize)
1325
	    paned->child1_size += allocation - paned->last_allocation;
1326
	  else if (!(!paned->child1_resize && paned->child2_resize))
1327
	    paned->child1_size = allocation * ((gdouble) paned->child1_size / (paned->last_allocation)) + 0.5;
1328
	}
Owen Taylor's avatar
Owen Taylor committed
1329 1330 1331 1332 1333 1334
    }

  paned->child1_size = CLAMP (paned->child1_size,
			      paned->min_position,
			      paned->max_position);

1335 1336 1337 1338 1339
  if (paned->child1)
    gtk_widget_set_child_visible (paned->child1, paned->child1_size != 0);
  
  if (paned->child2)
    gtk_widget_set_child_visible (paned->child2, paned->child1_size != allocation); 
1340

1341
  g_object_freeze_notify (G_OBJECT (paned));
1342 1343
  if (paned->child1_size != old_position)
    g_object_notify (G_OBJECT (paned), "position");
1344
  if (paned->min_position != old_min_position)
1345
    g_object_notify (G_OBJECT (paned), "min-position");
1346
  if (paned->max_position != old_max_position)
1347
    g_object_notify (G_OBJECT (paned), "max-position");