gtkpaned.c 95.5 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 "gtkwidgetprivate.h"
Matthias Clasen's avatar
Matthias Clasen committed
39
#include "gtkcontainerprivate.h"
40
#include "a11y/gtkpanedaccessible.h"
Matthias Clasen's avatar
Matthias Clasen committed
41
#include "gtkcsscustomgadgetprivate.h"
42 43
#include "gtkcssnodeprivate.h"
#include "gtkstylecontextprivate.h"
Matthias Clasen's avatar
Matthias Clasen committed
44 45 46 47
#include "gtkcssstylepropertyprivate.h"
#include "gtkcssnumbervalueprivate.h"

#include <math.h>
Elliot Lee's avatar
Elliot Lee committed
48

49 50
/**
 * SECTION:gtkpaned
51
 * @Short_description: A widget with two adjustable panes
52 53
 * @Title: GtkPaned
 *
54 55 56
 * #GtkPaned has two panes, arranged either
 * horizontally or vertically. The division between
 * the two panes is adjustable by the user by dragging
57
 * a handle.
58 59
 *
 * Child widgets are
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
 * 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().
 *
84 85
 * # CSS nodes
 *
86 87
 * |[<!-- language="plain" -->
 * paned
88 89 90
 * ├── <child>
 * ├── separator[.wide]
 * ╰── <child>
91 92
 * ]|
 *
93
 * GtkPaned has a main CSS node with name paned, and a subnode for
94 95
 * the separator with name separator. The subnodes gets a .wide style
 * class when the paned is supposed to be wide.
96
 *
97 98
 * ## Creating a paned widget with minimum sizes.
 *
99
 * |[<!-- language="C" -->
100 101 102 103 104 105 106 107 108 109 110 111 112
 * 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);
113
 * ]|
114
 */
115

116 117 118 119 120
enum {
  CHILD1,
  CHILD2
};

121 122 123 124 125
struct _GtkPanedPrivate
{
  GtkPaned       *first_paned;
  GtkWidget      *child1;
  GtkWidget      *child2;
126 127
  GdkWindow      *child1_window;
  GdkWindow      *child2_window;
128 129 130
  GtkWidget      *last_child1_focus;
  GtkWidget      *last_child2_focus;
  GtkWidget      *saved_focus;
131
  GtkOrientation  orientation;
132 133 134

  GdkRectangle   handle_pos;
  GdkWindow     *handle;
Matthias Clasen's avatar
Matthias Clasen committed
135 136 137

  GtkCssGadget  *gadget;
  GtkCssGadget  *handle_gadget;
138

139 140
  GtkGesture    *pan_gesture;

141 142 143 144 145 146 147 148 149 150 151 152 153 154
  gint          child1_size;
  gint          drag_pos;
  gint          last_allocation;
  gint          max_position;
  gint          min_position;
  gint          original_position;

  guint         handle_prelit : 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;
155
  guint         panning       : 1;
156 157
};

158
enum {
159
  PROP_0,
160
  PROP_ORIENTATION,
161
  PROP_POSITION,
162 163
  PROP_POSITION_SET,
  PROP_MIN_POSITION,
164 165
  PROP_MAX_POSITION,
  PROP_WIDE_HANDLE
166
};
Elliot Lee's avatar
Elliot Lee committed
167

168 169 170 171 172 173
enum {
  CHILD_PROP_0,
  CHILD_PROP_RESIZE,
  CHILD_PROP_SHRINK
};

174 175 176 177 178 179
enum {
  CYCLE_CHILD_FOCUS,
  TOGGLE_HANDLE_FOCUS,
  MOVE_HANDLE,
  CYCLE_HANDLE_FOCUS,
  ACCEPT_POSITION,
180 181
  CANCEL_POSITION,
  LAST_SIGNAL
182 183 184 185 186 187 188 189 190 191
};

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);
192 193 194 195 196 197 198 199 200 201
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);
202
static void     gtk_paned_finalize              (GObject          *object);
203

204 205 206 207 208 209
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);
210 211 212 213 214 215 216 217 218 219
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);
220

221 222
static void     gtk_paned_size_allocate         (GtkWidget        *widget,
                                                 GtkAllocation    *allocation);
223 224 225 226
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);
227 228
static void     gtk_paned_state_flags_changed   (GtkWidget        *widget,
                                                 GtkStateFlags     previous_state);
Benjamin Otte's avatar
Benjamin Otte committed
229 230
static gboolean gtk_paned_draw                  (GtkWidget        *widget,
						 cairo_t          *cr);
231 232 233 234
static gboolean gtk_paned_enter                 (GtkWidget        *widget,
						 GdkEventCrossing *event);
static gboolean gtk_paned_leave                 (GtkWidget        *widget,
						 GdkEventCrossing *event);
235 236 237 238 239 240 241 242 243 244
static gboolean gtk_paned_focus                 (GtkWidget        *widget,
						 GtkDirectionType  direction);
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);
245 246 247 248
static void     gtk_paned_calc_position         (GtkPaned         *paned,
                                                 gint              allocation,
                                                 gint              child1_req,
                                                 gint              child2_req);
249 250 251 252
static void     gtk_paned_set_focus_child       (GtkContainer     *container,
						 GtkWidget        *child);
static void     gtk_paned_set_saved_focus       (GtkPaned         *paned,
						 GtkWidget        *widget);
253 254
static void     gtk_paned_set_first_paned       (GtkPaned         *paned,
						 GtkPaned         *first_paned);
255 256 257 258 259 260 261 262 263 264 265 266 267
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);
268

269
static GType    gtk_paned_child_type            (GtkContainer     *container);
Elliot Lee's avatar
Elliot Lee committed
270

271 272 273
static void     update_drag                     (GtkPaned         *paned,
                                                 int               xpos,
                                                 int               ypos);
274

275
G_DEFINE_TYPE_WITH_CODE (GtkPaned, gtk_paned, GTK_TYPE_CONTAINER,
276
                         G_ADD_PRIVATE (GtkPaned)
277 278
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
                                                NULL))
Elliot Lee's avatar
Elliot Lee committed
279

280 281
static guint signals[LAST_SIGNAL] = { 0 };

282

283 284
static void
add_tab_bindings (GtkBindingSet    *binding_set,
285
		  GdkModifierType   modifiers)
286
{
287
  gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
288
                                "toggle-handle-focus", 0);
289
  gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
290
				"toggle-handle-focus", 0);
291 292 293 294 295 296 297 298 299
}

static void
add_move_binding (GtkBindingSet   *binding_set,
		  guint            keyval,
		  GdkModifierType  mask,
		  GtkScrollType    scroll)
{
  gtk_binding_entry_add_signal (binding_set, keyval, mask,
300
				"move-handle", 1,
301 302 303
				GTK_TYPE_SCROLL_TYPE, scroll);
}

Elliot Lee's avatar
Elliot Lee committed
304 305 306
static void
gtk_paned_class_init (GtkPanedClass *class)
{
307
  GObjectClass *object_class;
Elliot Lee's avatar
Elliot Lee committed
308 309
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;
310 311
  GtkPanedClass *paned_class;
  GtkBindingSet *binding_set;
312

313
  object_class = (GObjectClass *) class;
314 315
  widget_class = (GtkWidgetClass *) class;
  container_class = (GtkContainerClass *) class;
316
  paned_class = (GtkPanedClass *) class;
317

318 319
  object_class->set_property = gtk_paned_set_property;
  object_class->get_property = gtk_paned_get_property;
320
  object_class->finalize = gtk_paned_finalize;
321

322 323
  widget_class->get_preferred_width = gtk_paned_get_preferred_width;
  widget_class->get_preferred_height = gtk_paned_get_preferred_height;
324 325
  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;
326
  widget_class->size_allocate = gtk_paned_size_allocate;
Elliot Lee's avatar
Elliot Lee committed
327 328
  widget_class->realize = gtk_paned_realize;
  widget_class->unrealize = gtk_paned_unrealize;
329 330
  widget_class->map = gtk_paned_map;
  widget_class->unmap = gtk_paned_unmap;
Benjamin Otte's avatar
Benjamin Otte committed
331
  widget_class->draw = gtk_paned_draw;
332
  widget_class->focus = gtk_paned_focus;
333 334
  widget_class->enter_notify_event = gtk_paned_enter;
  widget_class->leave_notify_event = gtk_paned_leave;
335
  widget_class->state_flags_changed = gtk_paned_state_flags_changed;
336

Elliot Lee's avatar
Elliot Lee committed
337 338
  container_class->add = gtk_paned_add;
  container_class->remove = gtk_paned_remove;
339
  container_class->forall = gtk_paned_forall;
340
  container_class->child_type = gtk_paned_child_type;
341
  container_class->set_focus_child = gtk_paned_set_focus_child;
342 343
  container_class->set_child_property = gtk_paned_set_child_property;
  container_class->get_child_property = gtk_paned_get_child_property;
344
  gtk_container_class_handle_border_width (container_class);
345 346 347 348 349 350 351

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

353 354 355 356
  g_object_class_override_property (object_class,
                                    PROP_ORIENTATION,
                                    "orientation");

357
  g_object_class_install_property (object_class,
358 359 360 361 362 363
                                   PROP_POSITION,
                                   g_param_spec_int ("position",
                                                     P_("Position"),
                                                     P_("Position of paned separator in pixels (0 means all the way to the left/top)"),
                                                     0, G_MAXINT, 0,
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
364

365
  g_object_class_install_property (object_class,
366 367 368 369 370 371
                                   PROP_POSITION_SET,
                                   g_param_spec_boolean ("position-set",
                                                         P_("Position Set"),
                                                         P_("TRUE if the Position property should be used"),
                                                         FALSE,
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
372

373 374 375
  /**
   * GtkPaned:min-position:
   *
376 377 378
   * The smallest possible value for the position property.
   * This property is derived from the size and shrinkability
   * of the widget's children.
379 380 381 382
   *
   * Since: 2.4
   */
  g_object_class_install_property (object_class,
383 384 385 386 387 388
                                   PROP_MIN_POSITION,
                                   g_param_spec_int ("min-position",
                                                     P_("Minimal Position"),
                                                     P_("Smallest possible value for the \"position\" property"),
                                                     0, G_MAXINT, 0,
                                                     GTK_PARAM_READABLE|G_PARAM_EXPLICIT_NOTIFY));
389 390 391 392

  /**
   * GtkPaned:max-position:
   *
393 394 395
   * The largest possible value for the position property.
   * This property is derived from the size and shrinkability
   * of the widget's children.
396 397 398 399
   *
   * Since: 2.4
   */
  g_object_class_install_property (object_class,
400 401 402 403 404 405 406
                                   PROP_MAX_POSITION,
                                   g_param_spec_int ("max-position",
                                                     P_("Maximal Position"),
                                                     P_("Largest possible value for the \"position\" property"),
                                                     0, G_MAXINT, G_MAXINT,
                                                     GTK_PARAM_READABLE|G_PARAM_EXPLICIT_NOTIFY));

407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
  /**
   * GtkPaned:wide-handle:
   *
   * Setting this property to %TRUE indicates that the paned needs
   * to provide stronger visual separation (e.g. because it separates
   * between two notebooks, whose tab rows would otherwise merge visually).
   *
   * Since: 3.16 
   */
  g_object_class_install_property (object_class,
                                   PROP_WIDE_HANDLE,
                                   g_param_spec_boolean ("wide-handle",
                                                         P_("Wide Handle"),
                                                         P_("Whether the paned should have a prominent handle"),
                                                         FALSE,
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));

424 425 426 427 428 429 430
  /**
   * GtkPaned::handle-size:
   *
   * The width of the handle.
   *
   * Deprecated: 3.20: Use CSS min-width and min-height instead.
   */
431 432 433 434 435 436 437
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("handle-size",
							     P_("Handle Size"),
							     P_("Width of handle"),
							     0,
							     G_MAXINT,
							     5,
Matthias Clasen's avatar
Matthias Clasen committed
438
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
439

440 441 442 443 444 445 446 447
  /**
   * GtkPaned:resize:
   *
   * The "resize" child property determines whether the child expands and
   * shrinks along with the paned widget.
   *
   * Since: 2.4
   */
448 449 450
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_RESIZE,
					      g_param_spec_boolean ("resize", 
451 452
								    P_("Resize"),
								    P_("If TRUE, the child expands and shrinks along with the paned widget"),
453
								    TRUE,
454
								    GTK_PARAM_READWRITE));
455

456 457 458 459 460 461 462 463
  /**
   * GtkPaned:shrink:
   *
   * The "shrink" child property determines whether the child can be made
   * smaller than its requisition.
   *
   * Since: 2.4
   */
464 465 466
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_SHRINK,
					      g_param_spec_boolean ("shrink", 
467 468
								    P_("Shrink"),
								    P_("If TRUE, the child can be made smaller than its requisition"),
469
								    TRUE,
470
								    GTK_PARAM_READWRITE));
471

472 473 474 475 476 477
  /**
   * GtkPaned::cycle-child-focus:
   * @widget: the object that received the signal
   * @reversed: whether cycling backward or forward
   *
   * The ::cycle-child-focus signal is a 
478
   * [keybinding signal][GtkBindingSignal]
479 480 481 482 483 484
   * which gets emitted to cycle the focus between the children of the paned.
   *
   * The default binding is f6.
   *
   * Since: 2.0
   */
485
  signals [CYCLE_CHILD_FOCUS] =
486
    g_signal_new (I_("cycle-child-focus"),
487 488 489 490 491 492 493 494
		  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);

495 496 497 498 499
  /**
   * GtkPaned::toggle-handle-focus:
   * @widget: the object that received the signal
   *
   * The ::toggle-handle-focus is a 
500
   * [keybinding signal][GtkBindingSignal]
501 502 503 504 505 506 507
   * 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
   */
508
  signals [TOGGLE_HANDLE_FOCUS] =
509
    g_signal_new (I_("toggle-handle-focus"),
510 511 512 513 514 515 516
		  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);

517 518 519 520 521 522
  /**
   * GtkPaned::move-handle:
   * @widget: the object that received the signal
   * @scroll_type: a #GtkScrollType
   *
   * The ::move-handle signal is a 
523
   * [keybinding signal][GtkBindingSignal]
524 525 526 527 528
   * which gets emitted to move the handle when the user is using key bindings 
   * to move it.
   *
   * Since: 2.0
   */
529
  signals[MOVE_HANDLE] =
530
    g_signal_new (I_("move-handle"),
531 532 533 534 535 536 537 538
		  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);

539 540 541 542 543 544
  /**
   * GtkPaned::cycle-handle-focus:
   * @widget: the object that received the signal
   * @reversed: whether cycling backward or forward
   *
   * The ::cycle-handle-focus signal is a 
545
   * [keybinding signal][GtkBindingSignal]
546 547 548 549 550 551 552
   * 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
   */
553
  signals [CYCLE_HANDLE_FOCUS] =
554
    g_signal_new (I_("cycle-handle-focus"),
555 556 557 558 559 560 561 562
		  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);

563 564 565 566 567
  /**
   * GtkPaned::accept-position:
   * @widget: the object that received the signal
   *
   * The ::accept-position signal is a 
568
   * [keybinding signal][GtkBindingSignal]
569 570 571 572 573 574 575
   * 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
   */
576
  signals [ACCEPT_POSITION] =
577
    g_signal_new (I_("accept-position"),
578 579 580 581 582 583 584
		  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);

585 586 587 588 589
  /**
   * GtkPaned::cancel-position:
   * @widget: the object that received the signal
   *
   * The ::cancel-position signal is a 
590
   * [keybinding signal][GtkBindingSignal]
591 592 593 594 595 596 597 598
   * 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
   */
599
  signals [CANCEL_POSITION] =
600
    g_signal_new (I_("cancel-position"),
601 602 603 604 605 606 607
		  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
608
  binding_set = gtk_binding_set_by_class (class);
609 610

  /* F6 and friends */
611
  gtk_binding_entry_add_signal (binding_set,
612
                                GDK_KEY_F6, 0,
613
                                "cycle-child-focus", 1, 
614 615
                                G_TYPE_BOOLEAN, FALSE);
  gtk_binding_entry_add_signal (binding_set,
616
				GDK_KEY_F6, GDK_SHIFT_MASK,
617
				"cycle-child-focus", 1,
618 619 620 621
				G_TYPE_BOOLEAN, TRUE);

  /* F8 and friends */
  gtk_binding_entry_add_signal (binding_set,
622
				GDK_KEY_F8, 0,
623
				"cycle-handle-focus", 1,
624 625 626
				G_TYPE_BOOLEAN, FALSE);
 
  gtk_binding_entry_add_signal (binding_set,
627
				GDK_KEY_F8, GDK_SHIFT_MASK,
628
				"cycle-handle-focus", 1,
629
				G_TYPE_BOOLEAN, TRUE);
630
 
631 632 633 634
  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);
635 636 637

  /* accept and cancel positions */
  gtk_binding_entry_add_signal (binding_set,
638
				GDK_KEY_Escape, 0,
639
				"cancel-position", 0);
640 641

  gtk_binding_entry_add_signal (binding_set,
642
				GDK_KEY_Return, 0,
643
				"accept-position", 0);
644
  gtk_binding_entry_add_signal (binding_set,
645
				GDK_KEY_ISO_Enter, 0,
646
				"accept-position", 0);
647
  gtk_binding_entry_add_signal (binding_set,
648
				GDK_KEY_KP_Enter, 0,
649
				"accept-position", 0);
650
  gtk_binding_entry_add_signal (binding_set,
651
				GDK_KEY_space, 0,
652
				"accept-position", 0);
653
  gtk_binding_entry_add_signal (binding_set,
654
				GDK_KEY_KP_Space, 0,
655
				"accept-position", 0);
656 657

  /* move handle */
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
  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);
686

687
  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_PANED_ACCESSIBLE);
688
  gtk_widget_class_set_css_name (widget_class, "paned");
689 690
}

Manish Singh's avatar
Manish Singh committed
691
static GType
692 693
gtk_paned_child_type (GtkContainer *container)
{
694 695 696 697
  GtkPaned *paned = GTK_PANED (container);
  GtkPanedPrivate *priv = paned->priv;

  if (!priv->child1 || !priv->child2)
698 699
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
700
    return G_TYPE_NONE;
Elliot Lee's avatar
Elliot Lee committed
701 702
}

703 704 705 706 707 708 709 710 711 712 713
static gboolean
initiates_touch_drag (GtkPaned *paned,
                      gdouble   start_x,
                      gdouble   start_y)
{
  gint handle_size, handle_pos, drag_pos;
  GtkPanedPrivate *priv = paned->priv;
  GtkAllocation allocation;

#define TOUCH_EXTRA_AREA_WIDTH 50
  gtk_widget_get_allocation (GTK_WIDGET (paned), &allocation);
Matthias Clasen's avatar
Matthias Clasen committed
714 715 716 717 718
  gtk_css_gadget_get_preferred_size (priv->handle_gadget,
                                     priv->orientation,
                                     -1,
                                     NULL, &handle_size,
                                     NULL, NULL);
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749

  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
    {
      handle_pos = priv->handle_pos.x - allocation.x;
      drag_pos = start_x;
    }
  else
    {
      handle_pos = priv->handle_pos.y - allocation.y;
      drag_pos = start_y;
    }

  if (drag_pos < handle_pos - TOUCH_EXTRA_AREA_WIDTH ||
      drag_pos > handle_pos + handle_size + TOUCH_EXTRA_AREA_WIDTH)
    return FALSE;

#undef TOUCH_EXTRA_AREA_WIDTH

  return TRUE;
}

static void
pan_gesture_drag_begin_cb (GtkGestureDrag *gesture,
                           gdouble         start_x,
                           gdouble         start_y,
                           GtkPaned       *paned)
{
  GtkPanedPrivate *priv = paned->priv;
  GdkEventSequence *sequence;
  GtkAllocation allocation;
  const GdkEvent *event;
750 751
  GdkDevice *device;
  gboolean is_touch;
752 753 754

  sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
  event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
755
  device = gdk_event_get_source_device (event);
756 757 758
  gtk_widget_get_allocation (GTK_WIDGET (paned), &allocation);
  paned->priv->panning = FALSE;

759 760 761
  is_touch = (event->type == GDK_TOUCH_BEGIN ||
              gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN);

762
  if (event->any.window == priv->handle ||
763
      (is_touch && initiates_touch_drag (paned, start_x, start_y)))
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
    {
      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
        priv->drag_pos = start_x - (priv->handle_pos.x - allocation.x);
      else
        priv->drag_pos = start_y - (priv->handle_pos.y - allocation.y);

      gtk_gesture_set_state (GTK_GESTURE (gesture),
                             GTK_EVENT_SEQUENCE_CLAIMED);
    }
  else
    {
      gtk_gesture_set_state (GTK_GESTURE (gesture),
                             GTK_EVENT_SEQUENCE_DENIED);
    }
}

static void
pan_gesture_pan_cb (GtkGesturePan   *gesture,
                    GtkPanDirection  direction,
                    gdouble          offset,
                    GtkPaned        *paned)
{
  gdouble start_x, start_y, offset_x, offset_y;

  paned->priv->panning = TRUE;

  gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (gesture),
                               &start_x, &start_y);
  gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture),
                               &offset_x, &offset_y);
  update_drag (paned, start_x + offset_x, start_y + offset_y);
}

static void
pan_gesture_drag_end_cb (GtkGestureDrag *gesture,
                         gdouble         offset_x,
                         gdouble         offset_y,
                         GtkPaned       *paned)
{
  if (!paned->priv->panning)
    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
}

807
static void
808 809 810 811
gtk_paned_set_property (GObject        *object,
			guint           prop_id,
			const GValue   *value,
			GParamSpec     *pspec)
812
{
813
  GtkPaned *paned = GTK_PANED (object);
814
  GtkPanedPrivate *priv = paned->priv;
815

816
  switch (prop_id)
817
    {
818
    case PROP_ORIENTATION:
819
      if (priv->orientation != g_value_get_enum (value))
820
        {
821 822 823 824
          priv->orientation = g_value_get_enum (value);
          _gtk_orientable_set_style_classes (GTK_ORIENTABLE (paned));

          if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
Matthias Clasen's avatar
Matthias Clasen committed
825 826
            gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (priv->pan_gesture),
                                             GTK_ORIENTATION_HORIZONTAL);
827
          else
Matthias Clasen's avatar
Matthias Clasen committed
828 829
            gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (priv->pan_gesture),
                                             GTK_ORIENTATION_VERTICAL);
830 831 832 833 834

          /* state_flags_changed updates the cursor */
          gtk_paned_state_flags_changed (GTK_WIDGET (paned), 0);
          gtk_widget_queue_resize (GTK_WIDGET (paned));
          g_object_notify_by_pspec (object, pspec);
835
        }
836
      break;
837 838 839 840
    case PROP_POSITION:
      gtk_paned_set_position (paned, g_value_get_int (value));
      break;
    case PROP_POSITION_SET:
841 842 843 844 845 846
      if (priv->position_set != g_value_get_boolean (value))
        {
          priv->position_set = g_value_get_boolean (value);
          gtk_widget_queue_resize_no_redraw (GTK_WIDGET (paned));
          g_object_notify_by_pspec (object, pspec);
        }
847
      break;
848 849 850
    case PROP_WIDE_HANDLE:
      gtk_paned_set_wide_handle (paned, g_value_get_boolean (value));
      break;
851
    default:
852
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
853 854 855 856 857
      break;
    }
}

static void
858 859 860 861
gtk_paned_get_property (GObject        *object,
			guint           prop_id,
			GValue         *value,
			GParamSpec     *pspec)
862
{
863
  GtkPaned *paned = GTK_PANED (object);
864
  GtkPanedPrivate *priv = paned->priv;
865

866
  switch (prop_id)
867
    {
868
    case PROP_ORIENTATION:
869
      g_value_set_enum (value, priv->orientation);
870
      break;
871
    case PROP_POSITION:
872
      g_value_set_int (value, priv->child1_size);
873 874
      break;
    case PROP_POSITION_SET:
875
      g_value_set_boolean (value, priv->position_set);
876
      break;
877
    case PROP_MIN_POSITION:
878
      g_value_set_int (value, priv->min_position);
879 880
      break;
    case PROP_MAX_POSITION:
881
      g_value_set_int (value, priv->max_position);
882
      break;
883 884 885
    case PROP_WIDE_HANDLE:
      g_value_set_boolean (value, gtk_paned_get_wide_handle (paned));
      break;
886
    default:
887
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
888 889 890
      break;
    }
}
Elliot Lee's avatar
Elliot Lee committed
891

892 893 894 895 896 897 898 899
static void
gtk_paned_set_child_property (GtkContainer    *container,
			      GtkWidget       *child,
			      guint            property_id,
			      const GValue    *value,
			      GParamSpec      *pspec)
{
  GtkPaned *paned = GTK_PANED (container);
900
  GtkPanedPrivate *priv = paned->priv;
901 902
  gboolean old_value, new_value;

903
  g_assert (child == priv->child1 || child == priv->child2);
904 905 906 907 908

  new_value = g_value_get_boolean (value);
  switch (property_id)
    {
    case CHILD_PROP_RESIZE:
909
      if (child == priv->child1)
910
	{
911 912
	  old_value = priv->child1_resize;
	  priv->child1_resize = new_value;
913 914 915
	}
      else
	{
916 917
	  old_value = priv->child2_resize;
	  priv->child2_resize = new_value;
918 919 920
	}
      break;
    case CHILD_PROP_SHRINK:
921
      if (child == priv->child1)
922
	{
923 924
	  old_value = priv->child1_shrink;
	  priv->child1_shrink = new_value;
925 926 927
	}
      else
	{
928 929
	  old_value = priv->child2_shrink;
	  priv->child2_shrink = new_value;
930 931 932 933
	}
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
934
      old_value = -1; /* quiet gcc */
935 936 937
      break;
    }
  if (old_value != new_value)
938
    gtk_widget_queue_resize_no_redraw (GTK_WIDGET (container));
939 940 941 942 943 944 945 946 947 948
}

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

951
  g_assert (child == priv->child1 || child == priv->child2);
952 953 954 955
  
  switch (property_id)
    {
    case CHILD_PROP_RESIZE:
956 957
      if (child == priv->child1)
	g_value_set_boolean (value, priv->child1_resize);
958
      else
959
	g_value_set_boolean (value, priv->child2_resize);
960 961
      break;
    case CHILD_PROP_SHRINK:
962 963
      if (child == priv->child1)
	g_value_set_boolean (value, priv->child1_shrink);
964
      else
965
	g_value_set_boolean (value, priv->child2_shrink);
966 967 968 969 970 971 972
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}

973 974 975 976 977 978 979 980
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);

981 982
  g_clear_object (&paned->priv->pan_gesture);

Matthias Clasen's avatar
Matthias Clasen committed
983 984 985
  g_clear_object (&paned->priv->handle_gadget);
  g_clear_object (&paned->priv->gadget);

Matthias Clasen's avatar
Matthias Clasen committed
986
  G_OBJECT_CLASS (gtk_paned_parent_class)->finalize (object);
987 988
}

989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 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_compute_position (GtkPaned *paned,
                            gint      allocation,
                            gint      child1_req,
                            gint      child2_req,
                            gint     *min_pos,
                            gint     *max_pos,
                            gint     *out_pos)
{
  GtkPanedPrivate *priv = paned->priv;
  gint min, max, pos;

  min = priv->child1_shrink ? 0 : child1_req;

  max = allocation;
  if (!priv->child2_shrink)
    max = MAX (1, max - child2_req);
  max = MAX (min, max);

  if (!priv->position_set)
    {
      if (priv->child1_resize && !priv->child2_resize)
	pos = MAX (0, allocation - child2_req);
      else if (!priv->child1_resize && priv->child2_resize)
	pos = child1_req;
      else if (child1_req + child2_req != 0)
	pos = allocation * ((gdouble)child1_req / (child1_req + child2_req)) + 0.5;
      else
	pos = allocation * 0.5 + 0.5;
    }
  else
    {
      /* If the position was set before the initial allocation.
       * (priv->last_allocation <= 0) just clamp it and leave it.
       */
      if (priv->last_allocation > 0)
	{
	  if (priv->child1_resize && !priv->child2_resize)
	    pos = priv->child1_size + allocation - priv->last_allocation;
	  else if (!(!priv->child1_resize && priv->child2_resize))
	    pos = allocation * ((gdouble) priv->child1_size / (priv->last_allocation)) + 0.5;
Benjamin Otte's avatar
Benjamin Otte committed
1030 1031
          else
            pos = priv->child1_size;
1032 1033
	}
      else
Benjamin Otte's avatar
Benjamin Otte committed
1034
        pos = priv->child1_size;
1035 1036 1037 1038 1039
    }

  pos = CLAMP (pos, min, max);
  
  if (min_pos)
Benjamin Otte's avatar
Benjamin Otte committed
1040
    *min_pos = min;
1041
  if (max_pos)
Benjamin Otte's avatar
Benjamin Otte committed
1042
    *max_pos = max;
1043 1044 1045 1046
  if (out_pos)
    *out_pos = pos;
}

Benjamin Otte's avatar
Benjamin Otte committed
1047
static void
1048 1049 1050 1051
gtk_paned_get_preferred_size_for_orientation (GtkWidget      *widget,
                                              gint            size,
                                              gint           *minimum,
                                              gint           *natural)
Benjamin Otte's avatar
Benjamin Otte committed
1052
{
1053
  GtkPaned *paned = GTK_PANED (widget);
Benjamin Otte's avatar
Benjamin Otte committed
1054
  GtkPanedPrivate *priv = paned->priv;
1055
  gint child_min, child_nat;
Benjamin Otte's avatar
Benjamin Otte committed
1056

1057
  *minimum = *natural = 0;
Benjamin Otte's avatar
Benjamin Otte committed
1058

1059
  if (priv->child1 && gtk_widget_get_visible (priv->child1))
Benjamin Otte's avatar
Benjamin Otte committed
1060
    {
1061 1062 1063
      _gtk_widget_get_preferred_size_for_size (priv->child1, priv->orientation, size, &child_min, &child_nat, NULL, NULL);
      if (priv->child1_shrink)
        *minimum = 0;
Benjamin Otte's avatar
Benjamin Otte committed
1064
      else
1065 1066
        *minimum = child_min;
      *natural = child_nat;
Benjamin Otte's avatar
Benjamin Otte committed
1067
    }
1068 1069

  if (priv->child2 && gtk_widget_get_visible (priv->child2))
Benjamin Otte's avatar
Benjamin Otte committed
1070
    {
1071 1072 1073 1074 1075
      _gtk_widget_get_preferred_size_for_size (priv->child2, priv->orientation, size, &child_min, &child_nat, NULL, NULL);

      if (!priv->child2_shrink)
        *minimum += child_min;
      *natural += child_nat;
Benjamin Otte's avatar
Benjamin Otte committed
1076 1077
    }

1078 1079 1080 1081 1082
  if (priv->child1 && gtk_widget_get_visible (priv->child1) &&
      priv->child2 && gtk_widget_get_visible (priv->child2))
    {
      gint handle_size;

Matthias Clasen's avatar
Matthias Clasen committed
1083 1084 1085 1086 1087
      gtk_css_gadget_get_preferred_size (priv->handle_gadget,
                                         priv->orientation,
                                         -1,
                                         NULL, &handle_size,
                                         NULL, NULL);
1088 1089 1090 1091

      *minimum += handle_size;
      *natural += handle_size;
    }
Benjamin Otte's avatar
Benjamin Otte committed
1092 1093
}

1094
static void
1095 1096 1097 1098
gtk_paned_get_preferred_size_for_opposite_orientation (GtkWidget      *widget,
                                                       gint            size,
                                                       gint           *minimum,
                                                       gint           *natural)
1099 1100
{
  GtkPaned *paned = GTK_PANED (widget);
1101
  GtkPanedPrivate *priv = paned->priv;
1102
  gint for_child1, for_child2;
1103
  gint child_min, child_nat;
1104

1105 1106 1107 1108 1109 1110 1111
  if (size > -1 &&
      priv->child1 && gtk_widget_get_visible (priv->child1) &&
      priv->child2 && gtk_widget_get_visible (priv->child2))
    {
      gint child1_req, child2_req;
      gint handle_size;

Matthias Clasen's avatar
Matthias Clasen committed
1112 1113 1114 1115 1116
      gtk_css_gadget_get_preferred_size (priv->handle_gadget,
                                         OPPOSITE_ORIENTATION (priv->orientation),
                                         -1,
                                         NULL, &handle_size,
                                         NULL, NULL);
1117 1118 1119 1120 1121

      _gtk_widget_get_preferred_size_for_size (priv->child1, priv->orientation, -1, &child1_req, NULL, NULL, NULL);
      _gtk_widget_get_preferred_size_for_size (priv->child2, priv->orientation, -1, &child2_req, NULL, NULL, NULL);

      gtk_paned_compute_position (paned,
1122
                                  size - handle_size, child1_req, child2_req,
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
                                  NULL, NULL, &for_child1);

      for_child2 = size - for_child1 - handle_size;
    }
  else
    {
      for_child1 = size;
      for_child2 = size;
    }

1133
  *minimum = *natural = 0;
1134

1135
  if (priv->child1 && gtk_widget_get_visible (priv->child1))
1136
    {
1137 1138
      _gtk_widget_get_preferred_size_for_size (priv->child1,
                                               OPPOSITE_ORIENTATION (priv->orientation),
1139
                                               for_child1,
1140 1141
                                               &child_min, &child_nat,
                                               NULL, NULL);
1142 1143
      
      *minimum = child_min;
1144
      *natural = child_nat;
1145 1146
    }

1147
  if (priv->child2 && gtk_widget_get_visible (priv->child2))
1148
    {
1149 1150
      _gtk_widget_get_preferred_size_for_size (priv->child2,
                                               OPPOSITE_ORIENTATION (priv->orientation),
1151
                                               for_child2,
1152 1153
                                               &child_min, &child_nat,
                                               NULL, NULL);
1154

1155 1156 1157 1158
      *minimum = MAX (*minimum, child_min);
      *natural = MAX (*natural, child_nat);
    }
}
1159

Matthias Clasen's avatar
Matthias Clasen committed
1160 1161 1162
static gint
get_number (GtkCssStyle *style,
            guint        property)
1163
{
Matthias Clasen's avatar
Matthias Clasen committed
1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
  double d = _gtk_css_number_value_get (gtk_css_style_get_value (style, property), 100.0);

  if (d < 1)
    return ceil (d);
  else
    return floor (d);
}

static void
gtk_paned_measure_handle (GtkCssGadget   *gadget,
                          GtkOrientation  orientation,
                          int             size,
                          int            *minimum,
                          int            *natural,
                          int            *minimum_baseline,
                          int            *natural_baseline,
                          gpointer        data)
{
  GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
  GtkCssStyle *style;
  gint min_size;

  style = gtk_css_gadget_get_style (gadget);
Matthias Clasen's avatar
Matthias Clasen committed
1187 1188 1189 1190 1191 1192 1193 1194
  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    min_size = get_number (style, GTK_CSS_PROPERTY_MIN_WIDTH);
  else
    min_size = get_number (style, GTK_CSS_PROPERTY_MIN_HEIGHT);

  if (min_size != 0)
    *minimum = *natural = min_size;
  else
Matthias Clasen's avatar
Matthias Clasen committed
1195
    {
Matthias Clasen's avatar
Matthias Clasen committed
1196
      GtkStyleContext *context;
Matthias Clasen's avatar
Matthias Clasen committed
1197

Matthias Clasen's avatar
Matthias Clasen committed
1198 1199 1200 1201 1202 1203
      context = gtk_widget_get_style_context (widget);
      gtk_style_context_save_to_node (context, gtk_css_gadget_get_node (gadget));
      gtk_widget_style_get (widget, "handle-size", &min_size, NULL);
      gtk_style_context_restore (context);

      *minimum = *natural = min_size;
Matthias Clasen's avatar
Matthias Clasen committed
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
    }
}

static void
gtk_paned_measure (GtkCssGadget   *gadget,
                   GtkOrientation  orientation,
                   int             size,
                   int            *minimum,
                   int            *natural,
                   int            *minimum_baseline,
                   int            *natural_baseline,
                   gpointer        data)
{
  GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
1218 1219
  GtkPaned *paned = GTK_PANED (widget);
  GtkPanedPrivate *priv = paned->priv;
1220

1221
  if (orientation == priv->orientation)
1222
    gtk_paned_get_preferred_size_for_orientation (widget, size, minimum, natural);
1223
  else
1224
    gtk_paned_get_preferred_size_for_opposite_orientation (widget, size, minimum, natural);
1225 1226
}

1227 1228 1229 1230 1231
static void
gtk_paned_get_preferred_width (GtkWidget *widget,
                               gint      *minimum,
                               gint      *natural)
{
Matthias Clasen's avatar
Matthias Clasen committed
1232 1233 1234 1235 1236
  gtk_css_gadget_get_preferred_size (GTK_PANED (widget)->priv->gadget,
                                     GTK_ORIENTATION_HORIZONTAL,
                                     -1,
                                     minimum, natural,
                                     NULL, NULL);
1237 1238 1239 1240 1241 1242 1243
}

static void
gtk_paned_get_preferred_height (GtkWidget *widget,
                                gint      *minimum,
                                gint      *natural)
{
Matthias Clasen's avatar
Matthias Clasen committed
1244 1245 1246 1247 1248
  gtk_css_gadget_get_preferred_size (GTK_PANED (widget)->priv->gadget,
                                     GTK_ORIENTATION_VERTICAL,
                                     -1,
                                     minimum, natural,
                                     NULL, NULL);
1249 1250
}

1251 1252 1253 1254 1255 1256
static void
gtk_paned_get_preferred_width_for_height (GtkWidget *widget,
                                          gint       height,
                                          gint      *minimum,
                                          gint      *natural)
{