gtkpaned.c 81.6 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * 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
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16
 */
17 18

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

25
#include "config.h"
26

27 28
#include "gtkpaned.h"

29
#include "gtkbindings.h"
30 31
#include "gtkmain.h"
#include "gtkmarshalers.h"
32 33
#include "gtkorientable.h"
#include "gtkwindow.h"
34
#include "gtktypebuiltins.h"
35
#include "gtkorientableprivate.h"
36
#include "gtkprivate.h"
37
#include "gtkintl.h"
38
#include "a11y/gtkpanedaccessible.h"
Elliot Lee's avatar
Elliot Lee committed
39

40 41
/**
 * SECTION:gtkpaned
42
 * @Short_description: A widget with two adjustable panes
43 44
 * @Title: GtkPaned
 *
45 46 47
 * #GtkPaned has two panes, arranged either
 * horizontally or vertically. The division between
 * the two panes is adjustable by the user by dragging
48
 * a handle.
49 50
 *
 * Child widgets are
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
 * added to the panes of the widget with gtk_paned_pack1() and
 * gtk_paned_pack2(). The division between the two children is set by default
 * from the size requests of the children, but it can be adjusted by the
 * user.
 *
 * A paned widget draws a separator between the two child widgets and a
 * small handle that the user can drag to adjust the division. It does not
 * draw any relief around the children or around the separator. (The space
 * in which the separator is called the gutter.) Often, it is useful to put
 * each child inside a #GtkFrame with the shadow type set to %GTK_SHADOW_IN
 * so that the gutter appears as a ridge. No separator is drawn if one of
 * the children is missing.
 *
 * Each child has two options that can be set, @resize and @shrink. If
 * @resize is true, then when the #GtkPaned is resized, that child will
 * expand or shrink along with the paned widget. If @shrink is true, then
 * that child can be made smaller than its requisition by the user.
 * Setting @shrink to %FALSE allows the application to set a minimum size.
 * If @resize is false for both children, then this is treated as if
 * @resize is true for both children.
 *
 * The application can set the position of the slider as if it were set
 * by the user, by calling gtk_paned_set_position().
 *
 * <example>
 * <title>Creating a paned widget with minimum sizes.</title>
 * <programlisting>
 * GtkWidget *hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
 * GtkWidget *frame1 = gtk_frame_new (NULL);
 * GtkWidget *frame2 = gtk_frame_new (NULL);
 * gtk_frame_set_shadow_type (GTK_FRAME (frame1), GTK_SHADOW_IN);
 * gtk_frame_set_shadow_type (GTK_FRAME (frame2), GTK_SHADOW_IN);
 *
 * gtk_widget_set_size_request (hpaned, 200, -1);
 *
 * gtk_paned_pack1 (GTK_PANED (hpaned), frame1, TRUE, FALSE);
 * gtk_widget_set_size_request (frame1, 50, -1);
 *
 * gtk_paned_pack2 (GTK_PANED (hpaned), frame2, FALSE, FALSE);
 * gtk_widget_set_size_request (frame2, 50, -1);
 * </programlisting>
 * </example>
 */
94

95 96 97 98 99
enum {
  CHILD1,
  CHILD2
};

100 101 102 103 104
struct _GtkPanedPrivate
{
  GtkPaned       *first_paned;
  GtkWidget      *child1;
  GtkWidget      *child2;
105 106
  GdkWindow      *child1_window;
  GdkWindow      *child2_window;
107 108 109
  GtkWidget      *last_child1_focus;
  GtkWidget      *last_child2_focus;
  GtkWidget      *saved_focus;
110
  GtkOrientation  orientation;
111 112 113 114 115 116 117 118 119 120 121 122 123

  GdkCursorType  cursor_type;
  GdkDevice     *grab_device;
  GdkRectangle   handle_pos;
  GdkWindow     *handle;

  gint          child1_size;
  gint          drag_pos;
  gint          last_allocation;
  gint          max_position;
  gint          min_position;
  gint          original_position;

124 125
  guint32       grab_time;

126 127 128 129 130 131 132 133 134 135
  guint         handle_prelit : 1;
  guint         in_drag       : 1;
  guint         in_recursion  : 1;
  guint         child1_resize : 1;
  guint         child1_shrink : 1;
  guint         child2_resize : 1;
  guint         child2_shrink : 1;
  guint         position_set  : 1;
};

136
enum {
137
  PROP_0,
138
  PROP_ORIENTATION,
139
  PROP_POSITION,
140 141 142
  PROP_POSITION_SET,
  PROP_MIN_POSITION,
  PROP_MAX_POSITION
143
};
Elliot Lee's avatar
Elliot Lee committed
144

145 146 147 148 149 150
enum {
  CHILD_PROP_0,
  CHILD_PROP_RESIZE,
  CHILD_PROP_SHRINK
};

151 152 153 154 155 156
enum {
  CYCLE_CHILD_FOCUS,
  TOGGLE_HANDLE_FOCUS,
  MOVE_HANDLE,
  CYCLE_HANDLE_FOCUS,
  ACCEPT_POSITION,
157 158
  CANCEL_POSITION,
  LAST_SIGNAL
159 160 161 162 163 164 165 166 167 168
};

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);
169 170 171 172 173 174 175 176 177 178
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);
179
static void     gtk_paned_finalize              (GObject          *object);
180

181 182 183 184 185 186
static void     gtk_paned_get_preferred_width   (GtkWidget        *widget,
                                                 gint             *minimum,
                                                 gint             *natural);
static void     gtk_paned_get_preferred_height  (GtkWidget        *widget,
                                                 gint             *minimum,
                                                 gint             *natural);
187 188 189 190 191 192 193 194 195 196
static void     gtk_paned_get_preferred_width_for_height
                                                (GtkWidget        *widget,
                                                 gint              height,
                                                 gint             *minimum,
                                                 gint             *natural);
static void     gtk_paned_get_preferred_height_for_width
                                                (GtkWidget        *widget,
                                                 gint              width,
                                                 gint             *minimum,
                                                 gint              *natural);
197

198 199
static void     gtk_paned_size_allocate         (GtkWidget        *widget,
                                                 GtkAllocation    *allocation);
200 201 202 203
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);
204 205
static void     gtk_paned_state_flags_changed   (GtkWidget        *widget,
                                                 GtkStateFlags     previous_state);
206 207
static gboolean gtk_paned_draw                  (GtkWidget        *widget,
						 cairo_t          *cr);
208 209 210 211
static gboolean gtk_paned_enter                 (GtkWidget        *widget,
						 GdkEventCrossing *event);
static gboolean gtk_paned_leave                 (GtkWidget        *widget,
						 GdkEventCrossing *event);
212 213 214 215 216 217
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);
218 219
static gboolean gtk_paned_focus                 (GtkWidget        *widget,
						 GtkDirectionType  direction);
220 221
static gboolean gtk_paned_grab_broken           (GtkWidget          *widget,
						 GdkEventGrabBroken *event);
222 223 224 225 226 227 228 229
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);
230 231 232 233
static void     gtk_paned_calc_position         (GtkPaned         *paned,
                                                 gint              allocation,
                                                 gint              child1_req,
                                                 gint              child2_req);
234 235 236 237
static void     gtk_paned_set_focus_child       (GtkContainer     *container,
						 GtkWidget        *child);
static void     gtk_paned_set_saved_focus       (GtkPaned         *paned,
						 GtkWidget        *widget);
238 239
static void     gtk_paned_set_first_paned       (GtkPaned         *paned,
						 GtkPaned         *first_paned);
240 241 242 243 244 245 246 247 248 249 250 251 252
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);
253

254
static GType    gtk_paned_child_type            (GtkContainer     *container);
255 256
static void     gtk_paned_grab_notify           (GtkWidget        *widget,
		                                 gboolean          was_grabbed);
Elliot Lee's avatar
Elliot Lee committed
257

258

259 260 261
G_DEFINE_TYPE_WITH_CODE (GtkPaned, gtk_paned, GTK_TYPE_CONTAINER,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
                                                NULL))
Elliot Lee's avatar
Elliot Lee committed
262

263 264
static guint signals[LAST_SIGNAL] = { 0 };

265

266 267
static void
add_tab_bindings (GtkBindingSet    *binding_set,
268
		  GdkModifierType   modifiers)
269
{
270
  gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
271
                                "toggle-handle-focus", 0);
272
  gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
273
				"toggle-handle-focus", 0);
274 275 276 277 278 279 280 281 282
}

static void
add_move_binding (GtkBindingSet   *binding_set,
		  guint            keyval,
		  GdkModifierType  mask,
		  GtkScrollType    scroll)
{
  gtk_binding_entry_add_signal (binding_set, keyval, mask,
283
				"move-handle", 1,
284 285 286
				GTK_TYPE_SCROLL_TYPE, scroll);
}

Elliot Lee's avatar
Elliot Lee committed
287 288 289
static void
gtk_paned_class_init (GtkPanedClass *class)
{
290
  GObjectClass *object_class;
Elliot Lee's avatar
Elliot Lee committed
291 292
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;
293 294
  GtkPanedClass *paned_class;
  GtkBindingSet *binding_set;
295

296
  object_class = (GObjectClass *) class;
297 298
  widget_class = (GtkWidgetClass *) class;
  container_class = (GtkContainerClass *) class;
299
  paned_class = (GtkPanedClass *) class;
300

301 302
  object_class->set_property = gtk_paned_set_property;
  object_class->get_property = gtk_paned_get_property;
303
  object_class->finalize = gtk_paned_finalize;
304

305 306
  widget_class->get_preferred_width = gtk_paned_get_preferred_width;
  widget_class->get_preferred_height = gtk_paned_get_preferred_height;
307 308
  widget_class->get_preferred_width_for_height = gtk_paned_get_preferred_width_for_height;
  widget_class->get_preferred_height_for_width = gtk_paned_get_preferred_height_for_width;
309
  widget_class->size_allocate = gtk_paned_size_allocate;
Elliot Lee's avatar
Elliot Lee committed
310 311
  widget_class->realize = gtk_paned_realize;
  widget_class->unrealize = gtk_paned_unrealize;
312 313
  widget_class->map = gtk_paned_map;
  widget_class->unmap = gtk_paned_unmap;
314
  widget_class->draw = gtk_paned_draw;
315
  widget_class->focus = gtk_paned_focus;
316 317 318 319 320
  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;
321
  widget_class->grab_broken_event = gtk_paned_grab_broken;
322
  widget_class->grab_notify = gtk_paned_grab_notify;
323
  widget_class->state_flags_changed = gtk_paned_state_flags_changed;
324

Elliot Lee's avatar
Elliot Lee committed
325 326
  container_class->add = gtk_paned_add;
  container_class->remove = gtk_paned_remove;
327
  container_class->forall = gtk_paned_forall;
328
  container_class->child_type = gtk_paned_child_type;
329
  container_class->set_focus_child = gtk_paned_set_focus_child;
330 331
  container_class->set_child_property = gtk_paned_set_child_property;
  container_class->get_child_property = gtk_paned_get_child_property;
332
  gtk_container_class_handle_border_width (container_class);
333 334 335 336 337 338 339

  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;
340

341 342 343 344
  g_object_class_override_property (object_class,
                                    PROP_ORIENTATION,
                                    "orientation");

345 346 347
  g_object_class_install_property (object_class,
				   PROP_POSITION,
				   g_param_spec_int ("position",
348 349
						     P_("Position"),
						     P_("Position of paned separator in pixels (0 means all the way to the left/top)"),
350 351 352
						     0,
						     G_MAXINT,
						     0,
353
						     GTK_PARAM_READWRITE));
354

355 356
  g_object_class_install_property (object_class,
				   PROP_POSITION_SET,
357
				   g_param_spec_boolean ("position-set",
358 359
							 P_("Position Set"),
							 P_("TRUE if the Position property should be used"),
360
							 FALSE,
361
							 GTK_PARAM_READWRITE));
362

363
  gtk_widget_class_install_style_property (widget_class,
364
					   g_param_spec_int ("handle-size",
365 366
							     P_("Handle Size"),
							     P_("Width of handle"),
367 368 369
							     0,
							     G_MAXINT,
							     5,
370
							     GTK_PARAM_READABLE));
371 372 373 374 375 376 377 378 379
  /**
   * 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
380
				   PROP_MIN_POSITION,
381
				   g_param_spec_int ("min-position",
382 383
						     P_("Minimal Position"),
						     P_("Smallest possible value for the \"position\" property"),
384 385 386
						     0,
						     G_MAXINT,
						     0,
387
						     GTK_PARAM_READABLE));
388 389 390 391 392 393 394 395 396 397

  /**
   * 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
398
				   PROP_MAX_POSITION,
399
				   g_param_spec_int ("max-position",
400 401
						     P_("Maximal Position"),
						     P_("Largest possible value for the \"position\" property"),
402 403 404
						     0,
						     G_MAXINT,
						     G_MAXINT,
405
						     GTK_PARAM_READABLE));
406

407 408 409 410 411 412 413 414
  /**
   * GtkPaned:resize:
   *
   * The "resize" child property determines whether the child expands and
   * shrinks along with the paned widget.
   *
   * Since: 2.4
   */
415 416 417
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_RESIZE,
					      g_param_spec_boolean ("resize", 
418 419
								    P_("Resize"),
								    P_("If TRUE, the child expands and shrinks along with the paned widget"),
420
								    TRUE,
421
								    GTK_PARAM_READWRITE));
422

423 424 425 426 427 428 429 430
  /**
   * GtkPaned:shrink:
   *
   * The "shrink" child property determines whether the child can be made
   * smaller than its requisition.
   *
   * Since: 2.4
   */
431 432 433
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_SHRINK,
					      g_param_spec_boolean ("shrink", 
434 435
								    P_("Shrink"),
								    P_("If TRUE, the child can be made smaller than its requisition"),
436
								    TRUE,
437
								    GTK_PARAM_READWRITE));
438

439 440 441 442 443 444 445 446 447 448 449 450 451
  /**
   * 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
   */
452
  signals [CYCLE_CHILD_FOCUS] =
453
    g_signal_new (I_("cycle-child-focus"),
454 455 456 457 458 459 460 461
		  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);

462 463 464 465 466 467 468 469 470 471 472 473 474
  /**
   * 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
   */
475
  signals [TOGGLE_HANDLE_FOCUS] =
476
    g_signal_new (I_("toggle-handle-focus"),
477 478 479 480 481 482 483
		  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);

484 485 486 487 488 489 490 491 492 493 494 495
  /**
   * 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
   */
496
  signals[MOVE_HANDLE] =
497
    g_signal_new (I_("move-handle"),
498 499 500 501 502 503 504 505
		  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);

506 507 508 509 510 511 512 513 514 515 516 517 518 519
  /**
   * 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
   */
520
  signals [CYCLE_HANDLE_FOCUS] =
521
    g_signal_new (I_("cycle-handle-focus"),
522 523 524 525 526 527 528 529
		  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);

530 531 532 533 534 535 536 537 538 539 540 541 542
  /**
   * 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
   */
543
  signals [ACCEPT_POSITION] =
544
    g_signal_new (I_("accept-position"),
545 546 547 548 549 550 551
		  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);

552 553 554 555 556 557 558 559 560 561 562 563 564 565
  /**
   * 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
   */
566
  signals [CANCEL_POSITION] =
567
    g_signal_new (I_("cancel-position"),
568 569 570 571 572 573 574
		  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
575
  binding_set = gtk_binding_set_by_class (class);
576 577

  /* F6 and friends */
578
  gtk_binding_entry_add_signal (binding_set,
579
                                GDK_KEY_F6, 0,
580
                                "cycle-child-focus", 1, 
581 582
                                G_TYPE_BOOLEAN, FALSE);
  gtk_binding_entry_add_signal (binding_set,
583
				GDK_KEY_F6, GDK_SHIFT_MASK,
584
				"cycle-child-focus", 1,
585 586 587 588
				G_TYPE_BOOLEAN, TRUE);

  /* F8 and friends */
  gtk_binding_entry_add_signal (binding_set,
589
				GDK_KEY_F8, 0,
590
				"cycle-handle-focus", 1,
591 592 593
				G_TYPE_BOOLEAN, FALSE);
 
  gtk_binding_entry_add_signal (binding_set,
594
				GDK_KEY_F8, GDK_SHIFT_MASK,
595
				"cycle-handle-focus", 1,
596
				G_TYPE_BOOLEAN, TRUE);
597
 
598 599 600 601
  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);
602 603 604

  /* accept and cancel positions */
  gtk_binding_entry_add_signal (binding_set,
605
				GDK_KEY_Escape, 0,
606
				"cancel-position", 0);
607 608

  gtk_binding_entry_add_signal (binding_set,
609
				GDK_KEY_Return, 0,
610
				"accept-position", 0);
611
  gtk_binding_entry_add_signal (binding_set,
612
				GDK_KEY_ISO_Enter, 0,
613
				"accept-position", 0);
614
  gtk_binding_entry_add_signal (binding_set,
615
				GDK_KEY_KP_Enter, 0,
616
				"accept-position", 0);
617
  gtk_binding_entry_add_signal (binding_set,
618
				GDK_KEY_space, 0,
619
				"accept-position", 0);
620
  gtk_binding_entry_add_signal (binding_set,
621
				GDK_KEY_KP_Space, 0,
622
				"accept-position", 0);
623 624

  /* move handle */
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
  add_move_binding (binding_set, GDK_KEY_Left, 0, GTK_SCROLL_STEP_LEFT);
  add_move_binding (binding_set, GDK_KEY_KP_Left, 0, GTK_SCROLL_STEP_LEFT);
  add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT);
  add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT);

  add_move_binding (binding_set, GDK_KEY_Right, 0, GTK_SCROLL_STEP_RIGHT);
  add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT);
  add_move_binding (binding_set, GDK_KEY_KP_Right, 0, GTK_SCROLL_STEP_RIGHT);
  add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT);

  add_move_binding (binding_set, GDK_KEY_Up, 0, GTK_SCROLL_STEP_UP);
  add_move_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP);
  add_move_binding (binding_set, GDK_KEY_KP_Up, 0, GTK_SCROLL_STEP_UP);
  add_move_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP);
  add_move_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_UP);
  add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP);

  add_move_binding (binding_set, GDK_KEY_Down, 0, GTK_SCROLL_STEP_DOWN);
  add_move_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN);
  add_move_binding (binding_set, GDK_KEY_KP_Down, 0, GTK_SCROLL_STEP_DOWN);
  add_move_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN);
  add_move_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);
  add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);

  add_move_binding (binding_set, GDK_KEY_Home, 0, GTK_SCROLL_START);
  add_move_binding (binding_set, GDK_KEY_KP_Home, 0, GTK_SCROLL_START);
  add_move_binding (binding_set, GDK_KEY_End, 0, GTK_SCROLL_END);
  add_move_binding (binding_set, GDK_KEY_KP_End, 0, GTK_SCROLL_END);
653

654
  g_type_class_add_private (object_class, sizeof (GtkPanedPrivate));
655
  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_PANED_ACCESSIBLE);
656 657
}

Manish Singh's avatar
Manish Singh committed
658
static GType
659 660
gtk_paned_child_type (GtkContainer *container)
{
661 662 663 664
  GtkPaned *paned = GTK_PANED (container);
  GtkPanedPrivate *priv = paned->priv;

  if (!priv->child1 || !priv->child2)
665 666
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
667
    return G_TYPE_NONE;
Elliot Lee's avatar
Elliot Lee committed
668 669 670 671 672
}

static void
gtk_paned_init (GtkPaned *paned)
{
673 674
  GtkPanedPrivate *priv;

675
  gtk_widget_set_has_window (GTK_WIDGET (paned), FALSE);
676
  gtk_widget_set_can_focus (GTK_WIDGET (paned), TRUE);
677 678

  /* We only need to redraw when the handle position moves, which is
679 680
   * independent of the overall allocation of the GtkPaned
   */
681 682
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (paned), FALSE);

683
  paned->priv = G_TYPE_INSTANCE_GET_PRIVATE (paned, GTK_TYPE_PANED, GtkPanedPrivate);
684
  priv = paned->priv;
685

686 687
  priv->orientation = GTK_ORIENTATION_HORIZONTAL;
  priv->cursor_type = GDK_SB_H_DOUBLE_ARROW;
688

689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
  priv->child1 = NULL;
  priv->child2 = NULL;
  priv->handle = NULL;

  priv->handle_pos.width = 5;
  priv->handle_pos.height = 5;
  priv->position_set = FALSE;
  priv->last_allocation = -1;
  priv->in_drag = FALSE;

  priv->last_child1_focus = NULL;
  priv->last_child2_focus = NULL;
  priv->in_recursion = FALSE;
  priv->handle_prelit = FALSE;
  priv->original_position = -1;

  priv->handle_pos.x = -1;
  priv->handle_pos.y = -1;
707

708
  priv->drag_pos = -1;
Elliot Lee's avatar
Elliot Lee committed
709 710
}

711
static void
712 713 714 715
gtk_paned_set_property (GObject        *object,
			guint           prop_id,
			const GValue   *value,
			GParamSpec     *pspec)
716
{
717
  GtkPaned *paned = GTK_PANED (object);
718
  GtkPanedPrivate *priv = paned->priv;
719

720
  switch (prop_id)
721
    {
722
    case PROP_ORIENTATION:
723
      priv->orientation = g_value_get_enum (value);
724
      _gtk_orientable_set_style_classes (GTK_ORIENTABLE (paned));
725

726 727
      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
        priv->cursor_type = GDK_SB_H_DOUBLE_ARROW;
728
      else
729
        priv->cursor_type = GDK_SB_V_DOUBLE_ARROW;
730

731 732
      /* state_flags_changed updates the cursor */
      gtk_paned_state_flags_changed (GTK_WIDGET (paned), 0);
733 734
      gtk_widget_queue_resize (GTK_WIDGET (paned));
      break;
735 736 737 738
    case PROP_POSITION:
      gtk_paned_set_position (paned, g_value_get_int (value));
      break;
    case PROP_POSITION_SET:
739
      priv->position_set = g_value_get_boolean (value);
740
      gtk_widget_queue_resize_no_redraw (GTK_WIDGET (paned));
741
      break;
742
    default:
743
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
744 745 746 747 748
      break;
    }
}

static void
749 750 751 752
gtk_paned_get_property (GObject        *object,
			guint           prop_id,
			GValue         *value,
			GParamSpec     *pspec)
753
{
754
  GtkPaned *paned = GTK_PANED (object);
755
  GtkPanedPrivate *priv = paned->priv;
756

757
  switch (prop_id)
758
    {
759
    case PROP_ORIENTATION:
760
      g_value_set_enum (value, priv->orientation);
761
      break;
762
    case PROP_POSITION:
763
      g_value_set_int (value, priv->child1_size);
764 765
      break;
    case PROP_POSITION_SET:
766
      g_value_set_boolean (value, priv->position_set);
767
      break;
768
    case PROP_MIN_POSITION:
769
      g_value_set_int (value, priv->min_position);
770 771
      break;
    case PROP_MAX_POSITION:
772
      g_value_set_int (value, priv->max_position);
773
      break;
774
    default:
775
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
776 777 778
      break;
    }
}
Elliot Lee's avatar
Elliot Lee committed
779

780 781 782 783 784 785 786 787
static void
gtk_paned_set_child_property (GtkContainer    *container,
			      GtkWidget       *child,
			      guint            property_id,
			      const GValue    *value,
			      GParamSpec      *pspec)
{
  GtkPaned *paned = GTK_PANED (container);
788
  GtkPanedPrivate *priv = paned->priv;
789 790
  gboolean old_value, new_value;

791
  g_assert (child == priv->child1 || child == priv->child2);
792 793 794 795 796

  new_value = g_value_get_boolean (value);
  switch (property_id)
    {
    case CHILD_PROP_RESIZE:
797
      if (child == priv->child1)
798
	{
799 800
	  old_value = priv->child1_resize;
	  priv->child1_resize = new_value;
801 802 803
	}
      else
	{
804 805
	  old_value = priv->child2_resize;
	  priv->child2_resize = new_value;
806 807 808
	}
      break;
    case CHILD_PROP_SHRINK:
809
      if (child == priv->child1)
810
	{
811 812
	  old_value = priv->child1_shrink;
	  priv->child1_shrink = new_value;
813 814 815
	}
      else
	{
816 817
	  old_value = priv->child2_shrink;
	  priv->child2_shrink = new_value;
818 819 820 821
	}
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
822
      old_value = -1; /* quiet gcc */
823 824 825
      break;
    }
  if (old_value != new_value)
826
    gtk_widget_queue_resize_no_redraw (GTK_WIDGET (container));
827 828 829 830 831 832 833 834 835 836
}

static void
gtk_paned_get_child_property (GtkContainer *container,
			      GtkWidget    *child,
			      guint         property_id,
			      GValue       *value,
			      GParamSpec   *pspec)
{
  GtkPaned *paned = GTK_PANED (container);
837
  GtkPanedPrivate *priv = paned->priv;
838

839
  g_assert (child == priv->child1 || child == priv->child2);
840 841 842 843
  
  switch (property_id)
    {
    case CHILD_PROP_RESIZE:
844 845
      if (child == priv->child1)
	g_value_set_boolean (value, priv->child1_resize);
846
      else
847
	g_value_set_boolean (value, priv->child2_resize);
848 849
      break;
    case CHILD_PROP_SHRINK:
850 851
      if (child == priv->child1)
	g_value_set_boolean (value, priv->child1_shrink);
852
      else
853
	g_value_set_boolean (value, priv->child2_shrink);
854 855 856 857 858 859 860
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}

861 862 863 864 865 866 867 868
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
869
  G_OBJECT_CLASS (gtk_paned_parent_class)->finalize (object);
870 871
}

Benjamin Otte's avatar
Benjamin Otte committed
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
static void
get_preferred_size_for_size (GtkWidget      *widget,
                             GtkOrientation  orientation,
                             gint            size,
                             gint           *minimum,
                             gint           *natural)
{
  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    if (size < 0)
      gtk_widget_get_preferred_width (widget, minimum, natural);
    else
      gtk_widget_get_preferred_width_for_height (widget, size, minimum, natural);
  else
    if (size < 0)
      gtk_widget_get_preferred_height (widget, minimum, natural);
    else
      gtk_widget_get_preferred_height_for_width (widget, size, minimum, natural);
}

891
static void
892 893
gtk_paned_get_preferred_size (GtkWidget      *widget,
                              GtkOrientation  orientation,
Benjamin Otte's avatar
Benjamin Otte committed
894
                              gint            size,
895 896
                              gint           *minimum,
                              gint           *natural)
897 898
{
  GtkPaned *paned = GTK_PANED (widget);
899
  GtkPanedPrivate *priv = paned->priv;
900
  gint child_min, child_nat;
901

902
  *minimum = *natural = 0;
903

904
  if (priv->child1 && gtk_widget_get_visible (priv->child1))
905
    {
Benjamin Otte's avatar
Benjamin Otte committed
906
      get_preferred_size_for_size (priv->child1, orientation, size, &child_min, &child_nat);
907 908 909 910
      if (priv->child1_shrink && priv->orientation == orientation)
        *minimum = 0;
      else
        *minimum = child_min;
911
      *natural = child_nat;
912 913
    }

914
  if (priv->child2 && gtk_widget_get_visible (priv->child2))
915
    {
Benjamin Otte's avatar
Benjamin Otte committed
916
      get_preferred_size_for_size (priv->child2, orientation, size, &child_min, &child_nat);
917

918
      if (priv->orientation == orientation)
919
        {
920 921
          if (!priv->child2_shrink)
            *minimum += child_min;
922
          *natural += child_nat;
923 924 925
        }
      else
        {
926 927
          *minimum = MAX (*minimum, child_min);
          *natural = MAX (*natural, child_nat);
928 929 930
        }
    }

931 932
  if (priv->child1 && gtk_widget_get_visible (priv->child1) &&
      priv->child2 && gtk_widget_get_visible (priv->child2))
933 934 935 936 937
    {
      gint handle_size;

      gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);

938 939 940 941 942
      if (priv->orientation == orientation)
        {
          *minimum += handle_size;
          *natural += handle_size;
        }
943 944 945
    }
}

946 947 948 949 950
static void
gtk_paned_get_preferred_width (GtkWidget *widget,
                               gint      *minimum,
                               gint      *natural)
{
Benjamin Otte's avatar
Benjamin Otte committed
951
  gtk_paned_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, -1, minimum, natural);
952 953 954 955 956 957 958
}

static void
gtk_paned_get_preferred_height (GtkWidget *widget,
                                gint      *minimum,
                                gint      *natural)
{
Benjamin Otte's avatar
Benjamin Otte committed
959
  gtk_paned_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, -1, minimum, natural);
960 961
}

962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979
static void
gtk_paned_get_preferred_width_for_height (GtkWidget *widget,
                                          gint       height,
                                          gint      *minimum,
                                          gint      *natural)
{
  gtk_paned_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
}

static void
gtk_paned_get_preferred_height_for_width (GtkWidget *widget,
                                          gint       width,
                                          gint      *minimum,
                                          gint      *natural)
{
  gtk_paned_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
}

980 981 982 983
static void
flip_child (GtkWidget     *widget,
            GtkAllocation *child_pos)
{
984 985 986 987 988 989
  GtkAllocation allocation;
  gint x, width;

  gtk_widget_get_allocation (widget, &allocation);
  x = allocation.x;
  width = allocation.width;
990 991 992 993

  child_pos->x = 2 * x + width - child_pos->x - child_pos->width;
}

994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
static void
gtk_paned_set_child_visible (GtkPaned  *paned,
                             guint      id,
                             gboolean   visible)
{
  GtkPanedPrivate *priv = paned->priv;
  GtkWidget *child;

  child = id == CHILD1 ? priv->child1 : priv->child2;

  if (child == NULL)
    return;

  gtk_widget_set_child_visible (child, visible);

  if (gtk_widget_get_mapped (GTK_WIDGET (paned)))
    {
      GdkWindow *window = id == CHILD1 ? priv->child1_window : priv->child2_window;

1013 1014 1015 1016 1017 1018 1019
      if (visible != gdk_window_is_visible (window))
        {
          if (visible)
            gdk_window_show (window);
          else
            gdk_window_hide (window);
        }
1020 1021 1022
    }
}

1023 1024 1025
static void
gtk_paned_child_allocate (GtkWidget           *child,
                          GdkWindow           *child_window, /* can be NULL */
1026 1027
                          const GtkAllocation *window_allocation,
                          GtkAllocation       *child_allocation)
1028 1029 1030
{
  if (child_window)
    gdk_window_move_resize (child_window,
1031 1032
                            window_allocation->x, window_allocation->y,
                            window_allocation->width, window_allocation->height);
1033

1034
  gtk_widget_size_allocate (child, child_allocation);
1035
}
1036

1037 1038 1039 1040 1041
static void
gtk_paned_size_allocate (GtkWidget     *widget,
                         GtkAllocation *allocation)
{
  GtkPaned *paned = GTK_PANED (widget);
1042
  GtkPanedPrivate *priv = paned->priv;
1043

1044
  gtk_widget_set_allocation (widget, allocation);
1045

1046 1047
  if (priv->child1 && gtk_widget_get_visible (priv->child1) &&
      priv->child2 && gtk_widget_get_visible (priv->child2))
1048
    {
1049 1050
      GtkAllocation child1_allocation, window1_allocation;
      GtkAllocation child2_allocation, window2_allocation;
1051
      GtkAllocation priv_child1_allocation;
1052 1053 1054 1055 1056
      GdkRectangle old_handle_pos;
      gint handle_size;

      gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);

1057
      old_handle_pos = priv->handle_pos;
1058

1059
      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1060
        {
1061 1062 1063 1064 1065 1066 1067 1068 1069
          gint child1_width, child2_width;

          gtk_widget_get_preferred_width_for_height (priv->child1,
                                                     allocation->height,
                                                     &child1_width, NULL);
          gtk_widget_get_preferred_width_for_height (priv->child2,
                                                     allocation->height,
                                                     &child2_width, NULL);

1070
          gtk_paned_calc_position (paned,
1071
                                   MAX (1, allocation->width - handle_size),
1072 1073
                                   child1_width,
                                   child2_width);
1074

1075 1076
          priv->handle_pos.x = allocation->x + priv->child1_size;
          priv->handle_pos.y = allocation->y;
1077
          priv->handle_pos.width = handle_size;
1078
          priv->handle_pos.height = allocation->height;
1079

1080 1081 1082 1083
          window1_allocation.height = window2_allocation.height = allocation->height;
          window1_allocation.width = MAX (1, priv->child1_size);
          window1_allocation.x = allocation->x;
          window1_allocation.y = window2_allocation.y = allocation->y;
1084

1085 1086
          window2_allocation.x = window1_allocation.x + priv->child1_size + priv->handle_pos.width;
          window2_allocation.width = MAX (1, allocation->x + allocation->width - window2_allocation.x);
1087 1088 1089

          if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_RTL)
            {
1090 1091
              flip_child (widget, &(window2_allocation));
              flip_child (widget, &(window1_allocation));
1092
              flip_child (widget, &(priv->handle_pos));
1093
            }
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113

          child1_allocation.x = child1_allocation.y = 0;
          child1_allocation.width = window1_allocation.width;
          child1_allocation.height = window1_allocation.height;
          if (child1_width > child1_allocation.width)
            {
              if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_LTR)
                child1_allocation.x -= child1_width - child1_allocation.width;
              child1_allocation.width = child1_width;
            }

          child2_allocation.x = child2_allocation.y = 0;
          child2_allocation.width = window2_allocation.width;
          child2_allocation.height = window2_allocation.height;
          if (child2_width > child2_allocation.width)
            {
              if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_RTL)
                child2_allocation.x -= child2_width - child2_allocation.width;
              child2_allocation.width = child2_width;
            }
1114 1115 1116
        }
      else
        {
1117 1118 1119 1120 1121 1122 1123 1124 1125
          gint child1_height, child2_height;

          gtk_widget_get_preferred_height_for_width (priv->child1,
                                                     allocation->width,
                                                     &child1_height, NULL);
          gtk_widget_get_preferred_height_for_width (priv->child2,
                                                     allocation->width,
                                                     &child2_height, NULL);

1126
          gtk_paned_calc_position (paned,
1127
                                   MAX (1, allocation->height - handle_size),
1128 1129
                                   child1_height,
                                   child2_height);
1130

1131 1132 1133
          priv->handle_pos.x = allocation->x;
          priv->handle_pos.y = allocation->y + priv->child1_size;
          priv->handle_pos.width = allocation->width;
1134
          priv->handle_pos.height = handle_size;
1135

1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
          window1_allocation.width = window2_allocation.width = allocation->width;
          window1_allocation.height = MAX (1, priv->child1_size);
          window1_allocation.x = window2_allocation.x = allocation->x;
          window1_allocation.y = allocation->y;

          window2_allocation.y = window1_allocation.y + priv->child1_size + priv->handle_pos.height;
          window2_allocation.height = MAX (1, allocation->y + allocation->height - window2_allocation.y);

          child1_allocation.x = child1_allocation.y = 0;
          child1_allocation.width = window1_allocation.width;
          child1_allocation.height = window1_allocation.height;
          if (child1_height > child1_allocation.height)
            {
              child1_allocation.y -= child1_height - child1_allocation.height;
              child1_allocation.height = child1_height;
            }
1152

1153 1154 1155 1156 1157
          child2_allocation.x = child2_allocation.y = 0;
          child2_allocation.width = window2_allocation.width;
          child2_allocation.height = window2_allocation.height;
          if (child2_height > child2_allocation.height)
            child2_allocation.height = child2_height;
1158 1159
        }

1160
      if (gtk_widget_get_mapped (widget) &&
1161 1162 1163 1164
          (old_handle_pos.x != priv->handle_pos.x ||
           old_handle_pos.y != priv->handle_pos.y ||
           old_handle_pos.width != priv->handle_pos.width ||
           old_handle_pos.height != priv->handle_pos.height))
1165
        {
1166 1167 1168 1169 1170
          GdkWindow *window;

          window = gtk_widget_get_window (widget);
          gdk_window_invalidate_rect (window, &old_handle_pos, FALSE);
          gdk_window_invalidate_rect (window, &priv->handle_pos, FALSE);
1171 1172
        }

1173
      if (gtk_widget_get_realized (widget))
1174
	{
1175
	  if (gtk_widget_get_mapped (widget))
1176
	    gdk_window_show (priv->handle);
1177

1178
          if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1179
            {
1180 1181 1182
              gdk_window_move_resize (priv->handle,
                                      priv->handle_pos.x,
                                      priv->handle_pos.y,
1183
                                      handle_size,
1184
                                      priv->handle_pos.height);
1185 1186 1187
            }
          else
            {
1188 1189 1190 1191
              gdk_window_move_resize (priv->handle,
                                      priv->handle_pos.x,
                                      priv->handle_pos.y,
                                      priv->handle_pos.width,
1192 1193 1194 1195 1196 1197 1198
                                      handle_size);
            }
	}

      /* Now allocate the childen, making sure, when resizing not to
       * overlap the windows
       */
1199
      gtk_widget_get_allocation (priv->child1, &priv_child1_allocation);
1200
      if (gtk_widget_get_mapped (widget) &&
1201
          ((priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1202
            priv_child1_allocation.width < child1_allocation.width) ||
1203

1204
           (priv->orientation == GTK_ORIENTATION_VERTICAL &&
1205
            priv_child1_allocation.height < child1_allocation.height)))
1206
	{
1207 1208 1209 1210 1211 1212 1213 1214
          gtk_paned_child_allocate (priv->child2,
                                    priv->child2_window,
                                    &window2_allocation,
                                    &child2_allocation);
          gtk_paned_child_allocate (priv->child1,
                                    priv->child1_window,
                                    &window1_allocation,
                                    &child1_allocation);
1215 1216 1217
	}
      else
	{
1218 1219 1220 1221 1222 1223 1224 1225
          gtk_paned_child_allocate (priv->child1,
                                    priv->child1_window,
                                    &window1_allocation,
                                    &child1_allocation);
          gtk_paned_child_allocate (priv->child2,
                                    priv->child2_window,
                                    &window2_allocation,
                                    &child2_allocation);
1226 1227 1228 1229
	}
    }
  else
    {
1230
      GtkAllocation window_allocation, child_allocation;
1231

1232
      if (gtk_widget_get_realized (widget))
1233
	gdk_window_hide (priv->handle);
1234

1235 1236 1237 1238 1239
      window_allocation.x = allocation->x;
      window_allocation.y = allocation->y;
      window_allocation.width = allocation->width;
      window_allocation.height = allocation->height;
      child_allocation.x = child_allocation.y = 0;
1240 1241
      child_allocation.width = allocation->width;
      child_allocation.height = allocation->height;
1242

1243
      if (priv->child1 && gtk_widget_get_visible (priv->child1))
1244
        {
1245 1246 1247 1248
          gtk_paned_set_child_visible (paned, 0, TRUE);
          if (priv->child2)
            gtk_paned_set_child_visible (paned, 1, FALSE);

1249 1250 1251 1252
          gtk_paned_child_allocate (priv->child1,
                                    priv->child1_window,
                                    &window_allocation,
                                    &child_allocation);
1253
        }
1254
      else if (priv->child2 && gtk_widget_get_visible (priv->child2))
1255
        {
1256 1257 1258 1259
          gtk_paned_set_child_visible (paned, 1, TRUE);
          if (priv->child1)
            gtk_paned_set_child_visible (paned, 0, FALSE);

1260 1261 1262 1263
          gtk_paned_child_allocate (priv->child2,
                                    priv->child2_window,
                                    &window_allocation,
                                    &child_allocation);
1264
        }
1265 1266 1267 1268 1269 1270 1271
      else
        {
          if (priv->child1)
            gtk_paned_set_child_visible (paned, 0, FALSE);
          if (priv->child2)
            gtk_paned_set_child_visible (paned, 1, FALSE);
        }
1272 1273 1274
    }
}

1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
static GdkWindow *
gtk_paned_create_child_window (GtkPaned  *paned,
                               GtkWidget *child) /* may be NULL */
{
  GtkWidget *widget = GTK_WIDGET (paned);
  GtkPanedPrivate *priv = paned->priv;
  GdkWindow *window;
  GdkWindowAttr attributes;
  gint attributes_mask;

  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
  if (child)
    {
      GtkAllocation allocation;
      int handle_size;

      gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);

      gtk_widget_get_allocation (widget, &allocation);
      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1297 1298
          child == priv->child2 && priv->child1 &&
          gtk_widget_get_visible (priv->child1))
1299 1300 1301 1302
        attributes.x = priv->handle_pos.x + handle_size;
      else
        attributes.x = allocation.x;
      if (priv->orientation == GTK_ORIENTATION_VERTICAL &&
1303 1304
          child == priv->child2 && priv->child1 &&
          gtk_widget_get_visible (priv->child1))
1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331
        attributes.y = priv->handle_pos.y + handle_size;
      else
        attributes.y = allocation.y;

      gtk_widget_get_allocation (child, &allocation);
      attributes.width = allocation.width;
      attributes.height = allocation.height;
      attributes_mask = GDK_WA_X | GDK_WA_Y;
    }
  else
    {
      attributes.width = 1;
      attributes.height = 1;
      attributes_mask = 0;
    }

  window = gdk_window_new (gtk_widget_get_window (widget),
                           &attributes, attributes_mask);
  gdk_window_set_user_data (window, paned);
  gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);

  if (child)
    gtk_widget_set_parent_window (child, window);

  return window;
}

Elliot Lee's avatar
Elliot Lee committed
1332 1333 1334
static void
gtk_paned_realize (GtkWidget *widget)
{
1335 1336
  GtkPaned *paned = GTK_PANED (widget);
  GtkPanedPrivate *priv = paned->priv;
1337
  GdkWindow *window;
Elliot Lee's avatar
Elliot Lee committed
1338 1339
  GdkWindowAttr attributes;
  gint attributes_mask;
1340

1341
  gtk_widget_set_realized (widget, TRUE);
1342

1343 1344 1345 1346
  window = gtk_widget_get_parent_window (widget);
  gtk_widget_set_window (widget, window);
  g_object_ref (window);

1347
  attributes.window_type = GDK_WINDOW_CHILD;
1348
  attributes.wclass = GDK_INPUT_ONLY;
1349 1350 1351 1352
  attributes.x = priv->handle_pos.x;
  attributes.y = priv->handle_pos.y;
  attributes.width = priv->handle_pos.width;
  attributes.height = priv->handle_pos.height;
1353
  attributes.event_mask = gtk_widget_get_events (widget);
1354
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
Elliot Lee's avatar
Elliot Lee committed
1355
			    GDK_BUTTON_RELEASE_MASK |
1356 1357
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK |
1358
			    GDK_POINTER_MOTION_MASK);
1359
  attributes_mask = GDK_WA_X | GDK_WA_Y;
1360
  if (gtk_widget_is_sensitive (widget))
1361 1362
    {
      attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
1363
						      priv->cursor_type);
1364 1365
      attributes_mask |= GDK_WA_CURSOR;
    }
1366

1367 1368
  priv->handle =