gtkhandlebox.c 47.2 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
Elliot Lee's avatar
Elliot Lee committed
3
 * Copyright (C) 1998 Elliot Lee
4 5
 *
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7 8 9 10 11 12
 * 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
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16 17 18
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
19
 */
20

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

28
#include "config.h"
29

30
#include <stdlib.h>
31

32
#include "gtkhandlebox.h"
33
#include "gtkinvisible.h"
34
#include "gtkmain.h"
35
#include "gtkmarshalers.h"
36
#include "gtkwindow.h"
37
#include "gtktypebuiltins.h"
38
#include "gtkprivate.h"
39
#include "gtkintl.h"
40

41

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
/**
 * SECTION:gtkhandlebox
 * @Short_description: a widget for detachable window portions
 * @Title: GtkHandleBox
 *
 * The #GtkHandleBox widget allows a portion of a window to be "torn
 * off". It is a bin widget which displays its child and a handle that
 * the user can drag to tear off a separate window (the <firstterm>float
 * window</firstterm>) containing the child widget. A thin
 * <firstterm>ghost</firstterm> is drawn in the original location of the
 * handlebox. By dragging the separate window back to its original
 * location, it can be reattached.
 *
 * When reattaching, the ghost and float window, must be aligned
 * along one of the edges, the <firstterm>snap edge</firstterm>.
 * This either can be specified by the application programmer
 * explicitely, or GTK+ will pick a reasonable default based
 * on the handle position.
 *
 * To make detaching and reattaching the handlebox as minimally confusing
 * as possible to the user, it is important to set the snap edge so that
 * the snap edge does not move when the handlebox is deattached. For
 * instance, if the handlebox is packed at the bottom of a VBox, then
 * when the handlebox is detached, the bottom edge of the handlebox's
 * allocation will remain fixed as the height of the handlebox shrinks,
 * so the snap edge should be set to %GTK_POS_BOTTOM.
 */

70

71
struct _GtkHandleBoxPrivate
72
{
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
  /* Properties */
  GtkPositionType handle_position;
  GtkPositionType snap_edge;
  GtkShadowType   shadow_type;
  gboolean        child_detached;
  /* Properties */

  GtkAllocation   attach_allocation;
  GtkAllocation   float_allocation;

  GdkDevice      *grab_device;

  GdkWindow      *bin_window;     /* parent window for children */
  GdkWindow      *float_window;

  /* Variables used during a drag
   */
  gint            orig_x;
  gint            orig_y;

  guint           float_window_mapped : 1;
  guint           in_drag : 1;
  guint           shrink_on_detach : 1;
96 97
};

98
enum {
99
  PROP_0,
100
  PROP_SHADOW_TYPE,
101
  PROP_HANDLE_POSITION,
102
  PROP_SNAP_EDGE,
Tim Janik's avatar
Tim Janik committed
103 104
  PROP_SNAP_EDGE_SET,
  PROP_CHILD_DETACHED
105
};
106

Elliot Lee's avatar
Elliot Lee committed
107
#define DRAG_HANDLE_SIZE 10
108
#define CHILDLESS_SIZE	25
109
#define GHOST_HEIGHT 3
110
#define TOLERANCE 5
111

112
enum {
113 114 115 116 117
  SIGNAL_CHILD_ATTACHED,
  SIGNAL_CHILD_DETACHED,
  SIGNAL_LAST
};

118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
/* The algorithm for docking and redocking implemented here
 * has a couple of nice properties:
 *
 * 1) During a single drag, docking always occurs at the
 *    the same cursor position. This means that the users
 *    motions are reversible, and that you won't
 *    undock/dock oscillations.
 *
 * 2) Docking generally occurs at user-visible features.
 *    The user, once they figure out to redock, will
 *    have useful information about doing it again in
 *    the future.
 *
 * Please try to preserve these properties if you
 * change the algorithm. (And the current algorithm
 * is far from ideal). Briefly, the current algorithm
 * for deciding whether the handlebox is docked or not:
 *
 * 1) The decision is done by comparing two rectangles - the
 *    allocation if the widget at the start of the drag,
 *    and the boundary of hb->bin_window at the start of
 *    of the drag offset by the distance that the cursor
 *    has moved.
 *
 * 2) These rectangles must have one edge, the "snap_edge"
 *    of the handlebox, aligned within TOLERANCE.
 * 
 * 3) On the other dimension, the extents of one rectangle
 *    must be contained in the extents of the other,
 *    extended by tolerance. That is, either we can have:
 *
 * <-TOLERANCE-|--------bin_window--------------|-TOLERANCE->
 *         <--------float_window-------------------->
 *
 * or we can have:
 *
 * <-TOLERANCE-|------float_window--------------|-TOLERANCE->
 *          <--------bin_window-------------------->
 */

158 159 160 161 162 163 164 165 166 167 168 169
static void     gtk_handle_box_set_property  (GObject        *object,
                                              guint           param_id,
                                              const GValue   *value,
                                              GParamSpec     *pspec);
static void     gtk_handle_box_get_property  (GObject        *object,
                                              guint           param_id,
                                              GValue         *value,
                                              GParamSpec     *pspec);
static void     gtk_handle_box_map           (GtkWidget      *widget);
static void     gtk_handle_box_unmap         (GtkWidget      *widget);
static void     gtk_handle_box_realize       (GtkWidget      *widget);
static void     gtk_handle_box_unrealize     (GtkWidget      *widget);
170
static void     gtk_handle_box_style_updated (GtkWidget      *widget);
171 172
static void     gtk_handle_box_size_request  (GtkWidget      *widget,
                                              GtkRequisition *requisition);
173 174 175 176 177 178
static void     gtk_handle_box_get_preferred_width (GtkWidget *widget,
						    gint      *minimum,
						    gint      *natural);
static void     gtk_handle_box_get_preferred_height (GtkWidget *widget,
						    gint      *minimum,
						    gint      *natural);
179 180 181 182 183 184
static void     gtk_handle_box_size_allocate (GtkWidget      *widget,
                                              GtkAllocation  *real_allocation);
static void     gtk_handle_box_add           (GtkContainer   *container,
                                              GtkWidget      *widget);
static void     gtk_handle_box_remove        (GtkContainer   *container,
                                              GtkWidget      *widget);
Benjamin Otte's avatar
Benjamin Otte committed
185 186
static gboolean gtk_handle_box_draw          (GtkWidget      *widget,
                                              cairo_t        *cr);
187 188 189 190 191 192 193 194 195 196 197
static gboolean gtk_handle_box_button_press  (GtkWidget      *widget,
                                              GdkEventButton *event);
static gboolean gtk_handle_box_motion        (GtkWidget      *widget,
                                              GdkEventMotion *event);
static gboolean gtk_handle_box_delete_event  (GtkWidget      *widget,
                                              GdkEventAny    *event);
static void     gtk_handle_box_reattach      (GtkHandleBox   *hb);
static void     gtk_handle_box_end_drag      (GtkHandleBox   *hb,
                                              guint32         time);

static guint handle_box_signals[SIGNAL_LAST] = { 0 };
198

Matthias Clasen's avatar
Matthias Clasen committed
199
G_DEFINE_TYPE (GtkHandleBox, gtk_handle_box, GTK_TYPE_BIN)
200 201 202 203

static void
gtk_handle_box_class_init (GtkHandleBoxClass *class)
{
204
  GObjectClass *gobject_class;
205 206
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;
207

208
  gobject_class = (GObjectClass *) class;
209
  widget_class = (GtkWidgetClass *) class;
210
  container_class = (GtkContainerClass *) class;
211

212 213 214
  gobject_class->set_property = gtk_handle_box_set_property;
  gobject_class->get_property = gtk_handle_box_get_property;
  
215 216
  g_object_class_install_property (gobject_class,
                                   PROP_SHADOW_TYPE,
217
                                   g_param_spec_enum ("shadow-type",
218 219
                                                      P_("Shadow type"),
                                                      P_("Appearance of the shadow that surrounds the container"),
220
						      GTK_TYPE_SHADOW_TYPE,
Matthias Clasen's avatar
Matthias Clasen committed
221
						      GTK_SHADOW_OUT,
222
                                                      GTK_PARAM_READWRITE));
223
  
224 225
  g_object_class_install_property (gobject_class,
                                   PROP_HANDLE_POSITION,
226
                                   g_param_spec_enum ("handle-position",
227 228
                                                      P_("Handle position"),
                                                      P_("Position of the handle relative to the child widget"),
229 230
						      GTK_TYPE_POSITION_TYPE,
						      GTK_POS_LEFT,
231
                                                      GTK_PARAM_READWRITE));
232
  
233 234
  g_object_class_install_property (gobject_class,
                                   PROP_SNAP_EDGE,
235
                                   g_param_spec_enum ("snap-edge",
236 237
                                                      P_("Snap edge"),
                                                      P_("Side of the handlebox that's lined up with the docking point to dock the handlebox"),
238
						      GTK_TYPE_POSITION_TYPE,
239
						      GTK_POS_TOP,
240
                                                      GTK_PARAM_READWRITE));
Manish Singh's avatar
Manish Singh committed
241

242 243
  g_object_class_install_property (gobject_class,
                                   PROP_SNAP_EDGE_SET,
244
                                   g_param_spec_boolean ("snap-edge-set",
245 246
							 P_("Snap edge set"),
							 P_("Whether to use the value from the snap_edge property or a value derived from handle_position"),
247
							 FALSE,
248
							 GTK_PARAM_READWRITE));
249

Tim Janik's avatar
Tim Janik committed
250 251 252 253 254 255 256 257
  g_object_class_install_property (gobject_class,
                                   PROP_CHILD_DETACHED,
                                   g_param_spec_boolean ("child-detached",
							 P_("Child Detached"),
							 P_("A boolean value indicating whether the handlebox's child is attached or detached."),
							 FALSE,
							 GTK_PARAM_READABLE));

258 259
  widget_class->map = gtk_handle_box_map;
  widget_class->unmap = gtk_handle_box_unmap;
260
  widget_class->realize = gtk_handle_box_realize;
261
  widget_class->unrealize = gtk_handle_box_unrealize;
262
  widget_class->style_updated = gtk_handle_box_style_updated;
263 264
  widget_class->get_preferred_width = gtk_handle_box_get_preferred_width;
  widget_class->get_preferred_height = gtk_handle_box_get_preferred_height;
265
  widget_class->size_allocate = gtk_handle_box_size_allocate;
Benjamin Otte's avatar
Benjamin Otte committed
266
  widget_class->draw = gtk_handle_box_draw;
267
  widget_class->button_press_event = gtk_handle_box_button_press;
268
  widget_class->delete_event = gtk_handle_box_delete_event;
269

270
  container_class->add = gtk_handle_box_add;
271 272
  container_class->remove = gtk_handle_box_remove;

273 274
  class->child_attached = NULL;
  class->child_detached = NULL;
275

276 277 278 279 280 281 282 283 284 285
  /**
   * GtkHandleBox::child-attached:
   * @handlebox: the object which received the signal.
   * @widget: the child widget of the handlebox.
   *   (this argument provides no extra information
   *   and is here only for backwards-compatibility)
   *
   * This signal is emitted when the contents of the
   * handlebox are reattached to the main window.
   */
286
  handle_box_signals[SIGNAL_CHILD_ATTACHED] =
287
    g_signal_new (I_("child-attached"),
Manish Singh's avatar
Manish Singh committed
288 289 290 291 292 293 294
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkHandleBoxClass, child_attached),
		  NULL, NULL,
		  _gtk_marshal_VOID__OBJECT,
		  G_TYPE_NONE, 1,
		  GTK_TYPE_WIDGET);
295 296 297 298 299 300 301 302 303 304 305

  /**
   * GtkHandleBox::child-detached:
   * @handlebox: the object which received the signal.
   * @widget: the child widget of the handlebox.
   *   (this argument provides no extra information
   *   and is here only for backwards-compatibility)
   *
   * This signal is emitted when the contents of the
   * handlebox are detached from the main window.
   */
306
  handle_box_signals[SIGNAL_CHILD_DETACHED] =
307
    g_signal_new (I_("child-detached"),
Manish Singh's avatar
Manish Singh committed
308 309 310 311 312 313 314
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkHandleBoxClass, child_detached),
		  NULL, NULL,
		  _gtk_marshal_VOID__OBJECT,
		  G_TYPE_NONE, 1,
		  GTK_TYPE_WIDGET);
315

316
  g_type_class_add_private (gobject_class, sizeof (GtkHandleBoxPrivate));
317 318
}

319
static void
320
gtk_handle_box_init (GtkHandleBox *handle_box)
321
{
322
  GtkHandleBoxPrivate *priv;
323
  GtkStyleContext *context;
324 325 326

  handle_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (handle_box,
                                                  GTK_TYPE_HANDLE_BOX,
327
                                                  GtkHandleBoxPrivate);
328 329
  priv = handle_box->priv;

330
  gtk_widget_set_has_window (GTK_WIDGET (handle_box), TRUE);
331

332 333 334 335 336 337 338 339 340
  priv->bin_window = NULL;
  priv->float_window = NULL;
  priv->shadow_type = GTK_SHADOW_OUT;
  priv->handle_position = GTK_POS_LEFT;
  priv->float_window_mapped = FALSE;
  priv->child_detached = FALSE;
  priv->in_drag = FALSE;
  priv->shrink_on_detach = TRUE;
  priv->snap_edge = -1;
341 342 343

  context = gtk_widget_get_style_context (GTK_WIDGET (handle_box));
  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DOCK);
344 345
}

346 347 348 349 350
static void 
gtk_handle_box_set_property (GObject         *object,
			     guint            prop_id,
			     const GValue    *value,
			     GParamSpec      *pspec)
351
{
352
  GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
353

354
  switch (prop_id)
355
    {
356
    case PROP_SHADOW_TYPE:
357 358 359 360
      gtk_handle_box_set_shadow_type (handle_box, g_value_get_enum (value));
      break;
    case PROP_HANDLE_POSITION:
      gtk_handle_box_set_handle_position (handle_box, g_value_get_enum (value));
361
      break;
362 363
    case PROP_SNAP_EDGE:
      gtk_handle_box_set_snap_edge (handle_box, g_value_get_enum (value));
364
      break;
365 366 367 368
    case PROP_SNAP_EDGE_SET:
      if (!g_value_get_boolean (value))
	gtk_handle_box_set_snap_edge (handle_box, (GtkPositionType)-1);
      break;
369 370
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371 372 373 374
      break;
    }
}

375 376 377 378 379
static void 
gtk_handle_box_get_property (GObject         *object,
			     guint            prop_id,
			     GValue          *value,
			     GParamSpec      *pspec)
380
{
381
  GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
382
  GtkHandleBoxPrivate *priv = handle_box->priv;
383

384
  switch (prop_id)
385
    {
386
    case PROP_SHADOW_TYPE:
387
      g_value_set_enum (value, priv->shadow_type);
388
      break;
389
    case PROP_HANDLE_POSITION:
390
      g_value_set_enum (value, priv->handle_position);
391
      break;
392
    case PROP_SNAP_EDGE:
393
      g_value_set_enum (value,
394 395
			(priv->snap_edge == -1 ?
			 GTK_POS_TOP : priv->snap_edge));
396 397
      break;
    case PROP_SNAP_EDGE_SET:
398
      g_value_set_boolean (value, priv->snap_edge != -1);
399
      break;
Tim Janik's avatar
Tim Janik committed
400
    case PROP_CHILD_DETACHED:
401
      g_value_set_boolean (value, priv->child_detached);
Tim Janik's avatar
Tim Janik committed
402
      break;
403
    default:
404
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
405 406 407
      break;
    }
}
408 409 410 411 412 413 414 415

/**
 * gtk_handle_box_new:
 *
 * Create a new handle box.
 *
 * Returns: a new #GtkHandleBox.
 */
416
GtkWidget*
417 418
gtk_handle_box_new (void)
{
Manish Singh's avatar
Manish Singh committed
419
  return g_object_new (GTK_TYPE_HANDLE_BOX, NULL);
420 421 422 423 424
}

static void
gtk_handle_box_map (GtkWidget *widget)
{
425
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
426
  GtkHandleBoxPrivate *priv = hb->priv;
427
  GtkBin *bin = GTK_BIN (widget);
Javier Jardón's avatar
Javier Jardón committed
428
  GtkWidget *child;
429

430
  gtk_widget_set_mapped (widget, TRUE);
431

Javier Jardón's avatar
Javier Jardón committed
432
  child = gtk_bin_get_child (bin);
433 434
  if (child != NULL &&
      gtk_widget_get_visible (child) &&
Javier Jardón's avatar
Javier Jardón committed
435 436
      !gtk_widget_get_mapped (child))
    gtk_widget_map (child);
437

438
  if (priv->child_detached && !priv->float_window_mapped)
439
    {
440 441
      gdk_window_show (priv->float_window);
      priv->float_window_mapped = TRUE;
442 443
    }

444
  gdk_window_show (priv->bin_window);
445
  gdk_window_show (gtk_widget_get_window (widget));
446 447 448 449 450
}

static void
gtk_handle_box_unmap (GtkWidget *widget)
{
451
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
452
  GtkHandleBoxPrivate *priv = hb->priv;
453

454
  gtk_widget_set_mapped (widget, FALSE);
455

456
  gdk_window_hide (gtk_widget_get_window (widget));
457
  if (priv->float_window_mapped)
458
    {
459 460
      gdk_window_hide (priv->float_window);
      priv->float_window_mapped = FALSE;
461
    }
462 463

  GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unmap (widget);
464 465 466 467 468
}

static void
gtk_handle_box_realize (GtkWidget *widget)
{
469
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
470
  GtkHandleBoxPrivate *priv = hb->priv;
471 472
  GtkAllocation allocation;
  GtkRequisition requisition;
473
  GtkStyleContext *context;
Javier Jardón's avatar
Javier Jardón committed
474
  GtkWidget *child;
475
  GdkWindow *window;
476 477 478
  GdkWindowAttr attributes;
  gint attributes_mask;

479
  gtk_widget_set_realized (widget, TRUE);
480

481 482 483 484 485 486
  gtk_widget_get_allocation (widget, &allocation);

  attributes.x = allocation.x;
  attributes.y = allocation.y;
  attributes.width = allocation.width;
  attributes.height = allocation.height;
487 488 489
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
490
  attributes.event_mask = (gtk_widget_get_events (widget)
Matthias Clasen's avatar
Matthias Clasen committed
491
                           | GDK_EXPOSURE_MASK);
492
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
493 494 495 496 497

  window = gdk_window_new (gtk_widget_get_parent_window (widget),
                           &attributes, attributes_mask);
  gtk_widget_set_window (widget, window);
  gdk_window_set_user_data (window, widget);
498 499 500

  attributes.x = 0;
  attributes.y = 0;
501 502
  attributes.width = allocation.width;
  attributes.height = allocation.height;
503
  attributes.window_type = GDK_WINDOW_CHILD;
Matthias Clasen's avatar
Matthias Clasen committed
504 505 506 507 508 509
  attributes.event_mask = (gtk_widget_get_events (widget)
                           | GDK_EXPOSURE_MASK
                           | GDK_BUTTON1_MOTION_MASK
                           | GDK_POINTER_MOTION_HINT_MASK
                           | GDK_BUTTON_PRESS_MASK
                           | GDK_BUTTON_RELEASE_MASK);
510
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
511 512 513

  priv->bin_window = gdk_window_new (window,
                                     &attributes, attributes_mask);
514
  gdk_window_set_user_data (priv->bin_window, widget);
Javier Jardón's avatar
Javier Jardón committed
515 516 517

  child = gtk_bin_get_child (GTK_BIN (hb));
  if (child)
518
    gtk_widget_set_parent_window (child, priv->bin_window);
Matthias Clasen's avatar
Matthias Clasen committed
519

520
  gtk_widget_get_preferred_size (widget, &requisition, NULL);
521

522 523
  attributes.x = 0;
  attributes.y = 0;
524 525
  attributes.width = requisition.width;
  attributes.height = requisition.height;
526 527 528
  attributes.window_type = GDK_WINDOW_TOPLEVEL;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
Matthias Clasen's avatar
Matthias Clasen committed
529 530 531 532 533 534
  attributes.event_mask = (gtk_widget_get_events (widget)
                           | GDK_KEY_PRESS_MASK
                           | GDK_ENTER_NOTIFY_MASK
                           | GDK_LEAVE_NOTIFY_MASK
                           | GDK_FOCUS_CHANGE_MASK
                           | GDK_STRUCTURE_MASK);
Cody Russell's avatar
Cody Russell committed
535
  attributes.type_hint = GDK_WINDOW_TYPE_HINT_TOOLBAR;
536
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_TYPE_HINT;
537
  priv->float_window = gdk_window_new (gtk_widget_get_root_window (widget),
Matthias Clasen's avatar
Matthias Clasen committed
538
                                       &attributes, attributes_mask);
539 540 541
  gdk_window_set_user_data (priv->float_window, widget);
  gdk_window_set_decorations (priv->float_window, 0);
  gdk_window_set_type_hint (priv->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
542

543 544 545 546
  context = gtk_widget_get_style_context (widget);
  gtk_style_context_set_background (context, window);
  gtk_style_context_set_background (context, priv->bin_window);
  gtk_style_context_set_background (context, priv->float_window);
547 548
}

549 550 551
static void
gtk_handle_box_unrealize (GtkWidget *widget)
{
552
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
553
  GtkHandleBoxPrivate *priv = hb->priv;
554

555 556 557 558 559 560
  gdk_window_set_user_data (priv->bin_window, NULL);
  gdk_window_destroy (priv->bin_window);
  priv->bin_window = NULL;
  gdk_window_set_user_data (priv->float_window, NULL);
  gdk_window_destroy (priv->float_window);
  priv->float_window = NULL;
561

562
  GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize (widget);
563 564
}

Owen Taylor's avatar
Owen Taylor committed
565
static void
566
gtk_handle_box_style_updated (GtkWidget *widget)
Owen Taylor's avatar
Owen Taylor committed
567
{
568
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
569
  GtkHandleBoxPrivate *priv = hb->priv;
Owen Taylor's avatar
Owen Taylor committed
570

571 572
  GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->style_updated (widget);

573
  if (gtk_widget_get_realized (widget) &&
574
      gtk_widget_get_has_window (widget))
Owen Taylor's avatar
Owen Taylor committed
575
    {
576 577 578 579 580 581 582 583
      GtkStateFlags state;
      GtkStyleContext *context;

      context = gtk_widget_get_style_context (widget);
      state = gtk_widget_get_state_flags (widget);

      gtk_style_context_save (context);
      gtk_style_context_set_state (context, state);
584

585 586 587
      gtk_style_context_set_background (context, gtk_widget_get_window (widget));
      gtk_style_context_set_background (context, priv->bin_window);
      gtk_style_context_set_background (context, priv->float_window);
588

589
      gtk_style_context_restore (context);
Owen Taylor's avatar
Owen Taylor committed
590 591 592
    }
}

593 594 595
static int
effective_handle_position (GtkHandleBox *hb)
{
596
  GtkHandleBoxPrivate *priv = hb->priv;
597 598 599
  int handle_position;

  if (gtk_widget_get_direction (GTK_WIDGET (hb)) == GTK_TEXT_DIR_LTR)
600
    handle_position = priv->handle_position;
601 602
  else
    {
603
      switch (priv->handle_position)
604 605 606 607 608 609 610 611
	{
	case GTK_POS_LEFT:
	  handle_position = GTK_POS_RIGHT;
	  break;
	case GTK_POS_RIGHT:
	  handle_position = GTK_POS_LEFT;
	  break;
	default:
612
	  handle_position = priv->handle_position;
613 614 615 616 617 618 619
	  break;
	}
    }

  return handle_position;
}

620 621
static void
gtk_handle_box_size_request (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
622
			     GtkRequisition *requisition)
623
{
624 625
  GtkBin *bin = GTK_BIN (widget);
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
626
  GtkHandleBoxPrivate *priv = hb->priv;
627
  GtkRequisition child_requisition;
Javier Jardón's avatar
Javier Jardón committed
628
  GtkWidget *child;
629
  gint handle_position;
630

631 632 633 634
  handle_position = effective_handle_position (hb);

  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
635 636 637 638 639
    {
      requisition->width = DRAG_HANDLE_SIZE;
      requisition->height = 0;
    }
  else
640
    {
641 642 643
      requisition->width = 0;
      requisition->height = DRAG_HANDLE_SIZE;
    }
644

Javier Jardón's avatar
Javier Jardón committed
645
  child = gtk_bin_get_child (bin);
646
  /* if our child is not visible, we still request its size, since we
647
   * won't have any useful hint for our size otherwise.
648
   */
Javier Jardón's avatar
Javier Jardón committed
649
  if (child)
650
    {
651
      gtk_widget_get_preferred_size (child, &child_requisition, NULL);
652
    }
653 654 655 656 657
  else
    {
      child_requisition.width = 0;
      child_requisition.height = 0;
    }      
658

659
  if (priv->child_detached)
660
    {
661
      /* FIXME: This doesn't work currently */
662
      if (!priv->shrink_on_detach)
663
	{
664 665
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
666
	    requisition->height += child_requisition.height;
667
	  else
668
	    requisition->width += child_requisition.width;
669
	}
670
      else
671
	{
672 673 674 675 676 677 678 679
          GtkStyleContext *context;
          GtkStateFlags state;
          GtkBorder padding;

          context = gtk_widget_get_style_context (widget);
          state = gtk_widget_get_state_flags (widget);
          gtk_style_context_get_padding (context, state, &padding);

680 681
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
682
	    requisition->height += padding.top;
683
	  else
684
	    requisition->width += padding.left;
685
	}
686
    }
687 688
  else
    {
689 690 691 692 693
      guint border_width;

      border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
      requisition->width += border_width * 2;
      requisition->height += border_width * 2;
694
      
Javier Jardón's avatar
Javier Jardón committed
695
      if (child)
696
	{
697 698
	  requisition->width += child_requisition.width;
	  requisition->height += child_requisition.height;
699 700 701 702 703 704
	}
      else
	{
	  requisition->width += CHILDLESS_SIZE;
	  requisition->height += CHILDLESS_SIZE;
	}
705
    }
706 707
}

708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
static void
gtk_handle_box_get_preferred_width (GtkWidget *widget,
				     gint      *minimum,
				     gint      *natural)
{
  GtkRequisition requisition;

  gtk_handle_box_size_request (widget, &requisition);

  *minimum = *natural = requisition.width;
}

static void
gtk_handle_box_get_preferred_height (GtkWidget *widget,
				     gint      *minimum,
				     gint      *natural)
{
  GtkRequisition requisition;

  gtk_handle_box_size_request (widget, &requisition);

  *minimum = *natural = requisition.height;
}


733 734
static void
gtk_handle_box_size_allocate (GtkWidget     *widget,
735
			      GtkAllocation *allocation)
736
{
737 738
  GtkBin *bin = GTK_BIN (widget);
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
739
  GtkHandleBoxPrivate *priv = hb->priv;
740
  GtkRequisition child_requisition;
Javier Jardón's avatar
Javier Jardón committed
741
  GtkWidget *child;
742
  gint handle_position;
743

744 745
  handle_position = effective_handle_position (hb);

Javier Jardón's avatar
Javier Jardón committed
746 747 748
  child = gtk_bin_get_child (bin);

  if (child)
749
    {
750
      gtk_widget_get_preferred_size (child, &child_requisition, NULL);
751
    }
752 753 754 755
  else
    {
      child_requisition.width = 0;
      child_requisition.height = 0;
756
    }
757

758
  gtk_widget_set_allocation (widget, allocation);
759

760 761 762 763
  if (gtk_widget_get_realized (widget))
    gdk_window_move_resize (gtk_widget_get_window (widget),
                            allocation->x, allocation->y,
                            allocation->width, allocation->height);
764

765
  if (child != NULL && gtk_widget_get_visible (child))
766
    {
767
      GtkAllocation child_allocation;
768 769
      guint border_width;

770
      border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
771

772 773
      child_allocation.x = border_width;
      child_allocation.y = border_width;
774
      if (handle_position == GTK_POS_LEFT)
775
	child_allocation.x += DRAG_HANDLE_SIZE;
776
      else if (handle_position == GTK_POS_TOP)
777
	child_allocation.y += DRAG_HANDLE_SIZE;
778

779
      if (priv->child_detached)
780
	{
781 782 783
	  guint float_width;
	  guint float_height;
	  
784 785
	  child_allocation.width = child_requisition.width;
	  child_allocation.height = child_requisition.height;
786 787 788 789
	  
	  float_width = child_allocation.width + 2 * border_width;
	  float_height = child_allocation.height + 2 * border_width;
	  
790 791
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
792 793 794 795
	    float_width += DRAG_HANDLE_SIZE;
	  else
	    float_height += DRAG_HANDLE_SIZE;

796
	  if (gtk_widget_get_realized (widget))
797
	    {
798
	      gdk_window_resize (priv->float_window,
799 800
				 float_width,
				 float_height);
801
	      gdk_window_move_resize (priv->bin_window,
802 803 804 805 806
				      0,
				      0,
				      float_width,
				      float_height);
	    }
807 808 809
	}
      else
	{
810 811
	  child_allocation.width = MAX (1, (gint) allocation->width - 2 * border_width);
	  child_allocation.height = MAX (1, (gint) allocation->height - 2 * border_width);
812

813 814
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
815 816 817 818
	    child_allocation.width -= DRAG_HANDLE_SIZE;
	  else
	    child_allocation.height -= DRAG_HANDLE_SIZE;
	  
819
	  if (gtk_widget_get_realized (widget))
820
	    gdk_window_move_resize (priv->bin_window,
821 822
				    0,
				    0,
823 824
				    allocation->width,
				    allocation->height);
825 826
	}

Javier Jardón's avatar
Javier Jardón committed
827
      gtk_widget_size_allocate (child, &child_allocation);
828
    }
829
}
830

831
static void
Benjamin Otte's avatar
Benjamin Otte committed
832 833
gtk_handle_box_draw_ghost (GtkHandleBox *hb,
                           cairo_t      *cr)
834
{
835
  GtkWidget *widget = GTK_WIDGET (hb);
836 837
  GtkStateFlags state;
  GtkStyleContext *context;
838 839 840 841
  guint x;
  guint y;
  guint width;
  guint height;
Benjamin Otte's avatar
Benjamin Otte committed
842 843
  gint allocation_width;
  gint allocation_height;
844
  gint handle_position;
845

846
  handle_position = effective_handle_position (hb);
Benjamin Otte's avatar
Benjamin Otte committed
847 848 849
  allocation_width = gtk_widget_get_allocated_width (widget);
  allocation_height = gtk_widget_get_allocated_height (widget);

850 851
  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
852
    {
Benjamin Otte's avatar
Benjamin Otte committed
853
      x = handle_position == GTK_POS_LEFT ? 0 : allocation_width - DRAG_HANDLE_SIZE;
854 855
      y = 0;
      width = DRAG_HANDLE_SIZE;
Benjamin Otte's avatar
Benjamin Otte committed
856
      height = allocation_height;
857
    }
858 859 860
  else
    {
      x = 0;
Benjamin Otte's avatar
Benjamin Otte committed
861 862
      y = handle_position == GTK_POS_TOP ? 0 : allocation_height - DRAG_HANDLE_SIZE;
      width = allocation_width;
863 864
      height = DRAG_HANDLE_SIZE;
    }
865

866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
  context = gtk_widget_get_style_context (widget);
  state = gtk_widget_get_state_flags (widget);

  gtk_style_context_save (context);
  gtk_style_context_set_state (context, state);

  gtk_render_background (context, cr, x, y, width, height);
  gtk_render_frame (context, cr, x, y, width, height);

  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
    gtk_render_line (context, cr,
                     handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
                     allocation_height / 2,
                     handle_position == GTK_POS_LEFT ? allocation_width : allocation_width - DRAG_HANDLE_SIZE,
                     allocation_height / 2);
  else
    gtk_render_line (context, cr,
                     allocation_width / 2,
                     handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
                     allocation_width / 2,
                     handle_position == GTK_POS_TOP ? allocation_height : allocation_height - DRAG_HANDLE_SIZE);

  gtk_style_context_restore (context);
890 891
}

892 893 894 895 896 897 898 899
/**
 * gtk_handle_box_set_shadow_type:
 * @handle_box: a #GtkHandleBox
 * @type: the shadow type.
 *
 * Sets the type of shadow to be drawn around the border
 * of the handle box.
 */
900 901 902 903
void
gtk_handle_box_set_shadow_type (GtkHandleBox  *handle_box,
				GtkShadowType  type)
{
904
  GtkHandleBoxPrivate *priv;
905

906 907
  g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));

908 909 910
  priv = handle_box->priv;

  if ((GtkShadowType) priv->shadow_type != type)
911
    {
912
      priv->shadow_type = type;
913
      g_object_notify (G_OBJECT (handle_box), "shadow-type");
914 915 916
      gtk_widget_queue_resize (GTK_WIDGET (handle_box));
    }
}
917

918 919 920
/**
 * gtk_handle_box_get_shadow_type:
 * @handle_box: a #GtkHandleBox
921
 *
922 923 924 925 926 927 928 929 930 931
 * Gets the type of shadow drawn around the handle box. See
 * gtk_handle_box_set_shadow_type().