gtkhandlebox.c 43.9 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
#include <stdlib.h>
30
#include "gtkhandlebox.h"
31
#include "gtkinvisible.h"
32
#include "gtkmain.h"
33
#include "gtkmarshalers.h"
34
#include "gtkwindow.h"
35
#include "gtksizerequest.h"
36
#include "gtkprivate.h"
37
#include "gtkintl.h"
38

39

40

41
struct _GtkHandleBoxPrivate
42
{
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
  /* 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;
66 67
};

68
enum {
69
  PROP_0,
70
  PROP_SHADOW_TYPE,
71
  PROP_HANDLE_POSITION,
72
  PROP_SNAP_EDGE,
Tim Janik's avatar
Tim Janik committed
73 74
  PROP_SNAP_EDGE_SET,
  PROP_CHILD_DETACHED
75
};
76

Elliot Lee's avatar
Elliot Lee committed
77
#define DRAG_HANDLE_SIZE 10
78
#define CHILDLESS_SIZE	25
79
#define GHOST_HEIGHT 3
80
#define TOLERANCE 5
81

82
enum {
83 84 85 86 87
  SIGNAL_CHILD_ATTACHED,
  SIGNAL_CHILD_DETACHED,
  SIGNAL_LAST
};

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
/* 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-------------------->
 */

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 158 159 160 161 162 163 164 165 166
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);
static void     gtk_handle_box_style_set     (GtkWidget      *widget,
                                              GtkStyle       *previous_style);
static void     gtk_handle_box_size_request  (GtkWidget      *widget,
                                              GtkRequisition *requisition);
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);
static void     gtk_handle_box_draw_ghost    (GtkHandleBox   *hb);
static void     gtk_handle_box_paint         (GtkWidget      *widget,
                                              GdkEventExpose *event,
                                              GdkRectangle   *area);
static gboolean gtk_handle_box_expose        (GtkWidget      *widget,
                                              GdkEventExpose *event);
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 };
167

Matthias Clasen's avatar
Matthias Clasen committed
168
G_DEFINE_TYPE (GtkHandleBox, gtk_handle_box, GTK_TYPE_BIN)
169 170 171 172

static void
gtk_handle_box_class_init (GtkHandleBoxClass *class)
{
173
  GObjectClass *gobject_class;
174 175
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;
176

177
  gobject_class = (GObjectClass *) class;
178
  widget_class = (GtkWidgetClass *) class;
179
  container_class = (GtkContainerClass *) class;
180

181 182 183
  gobject_class->set_property = gtk_handle_box_set_property;
  gobject_class->get_property = gtk_handle_box_get_property;
  
184 185
  g_object_class_install_property (gobject_class,
                                   PROP_SHADOW_TYPE,
186
                                   g_param_spec_enum ("shadow-type",
187 188
                                                      P_("Shadow type"),
                                                      P_("Appearance of the shadow that surrounds the container"),
189
						      GTK_TYPE_SHADOW_TYPE,
Matthias Clasen's avatar
Matthias Clasen committed
190
						      GTK_SHADOW_OUT,
191
                                                      GTK_PARAM_READWRITE));
192
  
193 194
  g_object_class_install_property (gobject_class,
                                   PROP_HANDLE_POSITION,
195
                                   g_param_spec_enum ("handle-position",
196 197
                                                      P_("Handle position"),
                                                      P_("Position of the handle relative to the child widget"),
198 199
						      GTK_TYPE_POSITION_TYPE,
						      GTK_POS_LEFT,
200
                                                      GTK_PARAM_READWRITE));
201
  
202 203
  g_object_class_install_property (gobject_class,
                                   PROP_SNAP_EDGE,
204
                                   g_param_spec_enum ("snap-edge",
205 206
                                                      P_("Snap edge"),
                                                      P_("Side of the handlebox that's lined up with the docking point to dock the handlebox"),
207
						      GTK_TYPE_POSITION_TYPE,
208
						      GTK_POS_TOP,
209
                                                      GTK_PARAM_READWRITE));
Manish Singh's avatar
Manish Singh committed
210

211 212
  g_object_class_install_property (gobject_class,
                                   PROP_SNAP_EDGE_SET,
213
                                   g_param_spec_boolean ("snap-edge-set",
214 215
							 P_("Snap edge set"),
							 P_("Whether to use the value from the snap_edge property or a value derived from handle_position"),
216
							 FALSE,
217
							 GTK_PARAM_READWRITE));
218

Tim Janik's avatar
Tim Janik committed
219 220 221 222 223 224 225 226
  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));

227 228
  widget_class->map = gtk_handle_box_map;
  widget_class->unmap = gtk_handle_box_unmap;
229
  widget_class->realize = gtk_handle_box_realize;
230
  widget_class->unrealize = gtk_handle_box_unrealize;
Owen Taylor's avatar
Owen Taylor committed
231
  widget_class->style_set = gtk_handle_box_style_set;
232 233 234
  widget_class->size_request = gtk_handle_box_size_request;
  widget_class->size_allocate = gtk_handle_box_size_allocate;
  widget_class->expose_event = gtk_handle_box_expose;
235
  widget_class->button_press_event = gtk_handle_box_button_press;
236
  widget_class->delete_event = gtk_handle_box_delete_event;
237

238
  container_class->add = gtk_handle_box_add;
239 240
  container_class->remove = gtk_handle_box_remove;

241 242
  class->child_attached = NULL;
  class->child_detached = NULL;
243 244

  handle_box_signals[SIGNAL_CHILD_ATTACHED] =
245
    g_signal_new (I_("child-attached"),
Manish Singh's avatar
Manish Singh committed
246 247 248 249 250 251 252
		  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);
253
  handle_box_signals[SIGNAL_CHILD_DETACHED] =
254
    g_signal_new (I_("child-detached"),
Manish Singh's avatar
Manish Singh committed
255 256 257 258 259 260 261
		  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);
262

263
  g_type_class_add_private (gobject_class, sizeof (GtkHandleBoxPrivate));
264 265
}

266
static void
267
gtk_handle_box_init (GtkHandleBox *handle_box)
268
{
269
  GtkHandleBoxPrivate *priv;
270 271 272

  handle_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (handle_box,
                                                  GTK_TYPE_HANDLE_BOX,
273
                                                  GtkHandleBoxPrivate);
274 275
  priv = handle_box->priv;

276
  gtk_widget_set_has_window (GTK_WIDGET (handle_box), TRUE);
277

278 279 280 281 282 283 284 285 286
  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;
287 288
}

289 290 291 292 293
static void 
gtk_handle_box_set_property (GObject         *object,
			     guint            prop_id,
			     const GValue    *value,
			     GParamSpec      *pspec)
294
{
295
  GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
296

297
  switch (prop_id)
298
    {
299
    case PROP_SHADOW_TYPE:
300 301 302 303
      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));
304
      break;
305 306
    case PROP_SNAP_EDGE:
      gtk_handle_box_set_snap_edge (handle_box, g_value_get_enum (value));
307
      break;
308 309 310 311
    case PROP_SNAP_EDGE_SET:
      if (!g_value_get_boolean (value))
	gtk_handle_box_set_snap_edge (handle_box, (GtkPositionType)-1);
      break;
312 313
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
314 315 316 317
      break;
    }
}

318 319 320 321 322
static void 
gtk_handle_box_get_property (GObject         *object,
			     guint            prop_id,
			     GValue          *value,
			     GParamSpec      *pspec)
323
{
324
  GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
325
  GtkHandleBoxPrivate *priv = handle_box->priv;
326

327
  switch (prop_id)
328
    {
329
    case PROP_SHADOW_TYPE:
330
      g_value_set_enum (value, priv->shadow_type);
331
      break;
332
    case PROP_HANDLE_POSITION:
333
      g_value_set_enum (value, priv->handle_position);
334
      break;
335
    case PROP_SNAP_EDGE:
336
      g_value_set_enum (value,
337 338
			(priv->snap_edge == -1 ?
			 GTK_POS_TOP : priv->snap_edge));
339 340
      break;
    case PROP_SNAP_EDGE_SET:
341
      g_value_set_boolean (value, priv->snap_edge != -1);
342
      break;
Tim Janik's avatar
Tim Janik committed
343
    case PROP_CHILD_DETACHED:
344
      g_value_set_boolean (value, priv->child_detached);
Tim Janik's avatar
Tim Janik committed
345
      break;
346
    default:
347
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
348 349 350 351
      break;
    }
}
 
352
GtkWidget*
353 354
gtk_handle_box_new (void)
{
Manish Singh's avatar
Manish Singh committed
355
  return g_object_new (GTK_TYPE_HANDLE_BOX, NULL);
356 357 358 359 360
}

static void
gtk_handle_box_map (GtkWidget *widget)
{
361
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
362
  GtkHandleBoxPrivate *priv = hb->priv;
363
  GtkBin *bin = GTK_BIN (widget);
Javier Jardón's avatar
Javier Jardón committed
364
  GtkWidget *child;
365

366
  gtk_widget_set_mapped (widget, TRUE);
367

Javier Jardón's avatar
Javier Jardón committed
368
  child = gtk_bin_get_child (bin);
369 370
  if (child != NULL &&
      gtk_widget_get_visible (child) &&
Javier Jardón's avatar
Javier Jardón committed
371 372
      !gtk_widget_get_mapped (child))
    gtk_widget_map (child);
373

374
  if (priv->child_detached && !priv->float_window_mapped)
375
    {
376 377
      gdk_window_show (priv->float_window);
      priv->float_window_mapped = TRUE;
378 379
    }

380
  gdk_window_show (priv->bin_window);
381
  gdk_window_show (gtk_widget_get_window (widget));
382 383 384 385 386
}

static void
gtk_handle_box_unmap (GtkWidget *widget)
{
387
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
388
  GtkHandleBoxPrivate *priv = hb->priv;
389

390
  gtk_widget_set_mapped (widget, FALSE);
391

392
  gdk_window_hide (gtk_widget_get_window (widget));
393
  if (priv->float_window_mapped)
394
    {
395 396
      gdk_window_hide (priv->float_window);
      priv->float_window_mapped = FALSE;
397
    }
398 399 400 401 402
}

static void
gtk_handle_box_realize (GtkWidget *widget)
{
403
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
404
  GtkHandleBoxPrivate *priv = hb->priv;
405 406 407 408
  GtkAllocation allocation;
  GtkRequisition requisition;
  GtkStateType state;
  GtkStyle *style;
Javier Jardón's avatar
Javier Jardón committed
409
  GtkWidget *child;
410
  GdkWindow *window;
411 412 413
  GdkWindowAttr attributes;
  gint attributes_mask;

414
  gtk_widget_set_realized (widget, TRUE);
415

416 417 418 419 420 421
  gtk_widget_get_allocation (widget, &allocation);

  attributes.x = allocation.x;
  attributes.y = allocation.y;
  attributes.width = allocation.width;
  attributes.height = allocation.height;
422 423 424 425
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
426 427
  attributes.event_mask = (gtk_widget_get_events (widget)
			   | GDK_EXPOSURE_MASK);
428
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
429 430 431 432 433

  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);
434 435 436

  attributes.x = 0;
  attributes.y = 0;
437 438
  attributes.width = allocation.width;
  attributes.height = allocation.height;
439
  attributes.window_type = GDK_WINDOW_CHILD;
Soeren Sandmann's avatar
Soeren Sandmann committed
440 441 442 443 444
  attributes.event_mask = (gtk_widget_get_events (widget) |
			   GDK_EXPOSURE_MASK |
			   GDK_BUTTON1_MOTION_MASK |
			   GDK_POINTER_MOTION_HINT_MASK |
			   GDK_BUTTON_PRESS_MASK |
445 446
			    GDK_BUTTON_RELEASE_MASK);
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
447 448 449

  priv->bin_window = gdk_window_new (window,
                                     &attributes, attributes_mask);
450
  gdk_window_set_user_data (priv->bin_window, widget);
Javier Jardón's avatar
Javier Jardón committed
451 452 453

  child = gtk_bin_get_child (GTK_BIN (hb));
  if (child)
454
    gtk_widget_set_parent_window (child, priv->bin_window);
455
  
456 457
  gtk_size_request_get_size (GTK_SIZE_REQUEST (widget), &requisition, NULL);

458 459
  attributes.x = 0;
  attributes.y = 0;
460 461
  attributes.width = requisition.width;
  attributes.height = requisition.height;
462 463 464 465 466 467 468 469 470 471
  attributes.window_type = GDK_WINDOW_TOPLEVEL;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
  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
472 473
  attributes.type_hint = GDK_WINDOW_TYPE_HINT_TOOLBAR;
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_TYPE_HINT;
474
  priv->float_window = gdk_window_new (gtk_widget_get_root_window (widget),
475
				     &attributes, attributes_mask);
476 477 478
  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);
479 480 481 482 483 484 485

  gtk_widget_style_attach (widget);
  style = gtk_widget_get_style (widget);
  state = gtk_widget_get_state (widget);
  gtk_style_set_background (style, window, state);
  gtk_style_set_background (style, priv->bin_window, state);
  gtk_style_set_background (style, priv->float_window, state);
486 487
}

488 489 490
static void
gtk_handle_box_unrealize (GtkWidget *widget)
{
491
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
492
  GtkHandleBoxPrivate *priv = hb->priv;
493

494 495 496 497 498 499
  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;
500

501
  GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize (widget);
502 503
}

Owen Taylor's avatar
Owen Taylor committed
504 505 506 507
static void
gtk_handle_box_style_set (GtkWidget *widget,
			  GtkStyle  *previous_style)
{
508
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
509
  GtkHandleBoxPrivate *priv = hb->priv;
Owen Taylor's avatar
Owen Taylor committed
510

511
  if (gtk_widget_get_realized (widget) &&
512
      gtk_widget_get_has_window (widget))
Owen Taylor's avatar
Owen Taylor committed
513
    {
514 515 516 517 518 519 520 521 522
      GtkStateType state;
      GtkStyle *style;

      style = gtk_widget_get_style (widget);
      state = gtk_widget_get_state (widget);

      gtk_style_set_background (style, gtk_widget_get_window (widget), state);
      gtk_style_set_background (style, priv->bin_window, state);
      gtk_style_set_background (style, priv->float_window, state);
Owen Taylor's avatar
Owen Taylor committed
523 524 525
    }
}

526 527 528
static int
effective_handle_position (GtkHandleBox *hb)
{
529
  GtkHandleBoxPrivate *priv = hb->priv;
530 531 532
  int handle_position;

  if (gtk_widget_get_direction (GTK_WIDGET (hb)) == GTK_TEXT_DIR_LTR)
533
    handle_position = priv->handle_position;
534 535
  else
    {
536
      switch (priv->handle_position)
537 538 539 540 541 542 543 544
	{
	case GTK_POS_LEFT:
	  handle_position = GTK_POS_RIGHT;
	  break;
	case GTK_POS_RIGHT:
	  handle_position = GTK_POS_LEFT;
	  break;
	default:
545
	  handle_position = priv->handle_position;
546 547 548 549 550 551 552
	  break;
	}
    }

  return handle_position;
}

553 554
static void
gtk_handle_box_size_request (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
555
			     GtkRequisition *requisition)
556
{
557 558
  GtkBin *bin = GTK_BIN (widget);
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
559
  GtkHandleBoxPrivate *priv = hb->priv;
560
  GtkRequisition child_requisition;
Javier Jardón's avatar
Javier Jardón committed
561
  GtkWidget *child;
562
  gint handle_position;
563

564 565 566 567
  handle_position = effective_handle_position (hb);

  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
568 569 570 571 572
    {
      requisition->width = DRAG_HANDLE_SIZE;
      requisition->height = 0;
    }
  else
573
    {
574 575 576
      requisition->width = 0;
      requisition->height = DRAG_HANDLE_SIZE;
    }
577

Javier Jardón's avatar
Javier Jardón committed
578
  child = gtk_bin_get_child (bin);
579
  /* if our child is not visible, we still request its size, since we
580
   * won't have any useful hint for our size otherwise.
581
   */
Javier Jardón's avatar
Javier Jardón committed
582
  if (child)
583 584 585 586
    {
      gtk_size_request_get_size (GTK_SIZE_REQUEST (child),
                                 &child_requisition, NULL);
    }
587 588 589 590 591
  else
    {
      child_requisition.width = 0;
      child_requisition.height = 0;
    }      
592

593
  if (priv->child_detached)
594
    {
595
      /* FIXME: This doesn't work currently */
596
      if (!priv->shrink_on_detach)
597
	{
598 599
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
600
	    requisition->height += child_requisition.height;
601
	  else
602
	    requisition->width += child_requisition.width;
603
	}
604
      else
605
	{
606 607
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
608
	    requisition->height += gtk_widget_get_style (widget)->ythickness;
609
	  else
610
	    requisition->width += gtk_widget_get_style (widget)->xthickness;
611
	}
612
    }
613 614
  else
    {
615 616 617 618 619
      guint border_width;

      border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
      requisition->width += border_width * 2;
      requisition->height += border_width * 2;
620
      
Javier Jardón's avatar
Javier Jardón committed
621
      if (child)
622
	{
623 624
	  requisition->width += child_requisition.width;
	  requisition->height += child_requisition.height;
625 626 627 628 629 630
	}
      else
	{
	  requisition->width += CHILDLESS_SIZE;
	  requisition->height += CHILDLESS_SIZE;
	}
631
    }
632 633 634 635
}

static void
gtk_handle_box_size_allocate (GtkWidget     *widget,
636
			      GtkAllocation *allocation)
637
{
638 639
  GtkBin *bin = GTK_BIN (widget);
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
640
  GtkHandleBoxPrivate *priv = hb->priv;
641
  GtkRequisition child_requisition;
Javier Jardón's avatar
Javier Jardón committed
642
  GtkWidget *child;
643
  gint handle_position;
644

645 646
  handle_position = effective_handle_position (hb);

Javier Jardón's avatar
Javier Jardón committed
647 648 649
  child = gtk_bin_get_child (bin);

  if (child)
650 651 652 653
    {
      gtk_size_request_get_size (GTK_SIZE_REQUEST (child),
                                 &child_requisition, NULL);
    }
654 655 656 657
  else
    {
      child_requisition.width = 0;
      child_requisition.height = 0;
658
    }
659

660
  gtk_widget_set_allocation (widget, allocation);
661

662 663 664 665
  if (gtk_widget_get_realized (widget))
    gdk_window_move_resize (gtk_widget_get_window (widget),
                            allocation->x, allocation->y,
                            allocation->width, allocation->height);
666

667
  if (child != NULL && gtk_widget_get_visible (child))
668
    {
669
      GtkAllocation child_allocation;
670 671
      guint border_width;

672
      border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
673

674 675
      child_allocation.x = border_width;
      child_allocation.y = border_width;
676
      if (handle_position == GTK_POS_LEFT)
677
	child_allocation.x += DRAG_HANDLE_SIZE;
678
      else if (handle_position == GTK_POS_TOP)
679
	child_allocation.y += DRAG_HANDLE_SIZE;
680

681
      if (priv->child_detached)
682
	{
683 684 685
	  guint float_width;
	  guint float_height;
	  
686 687
	  child_allocation.width = child_requisition.width;
	  child_allocation.height = child_requisition.height;
688 689 690 691
	  
	  float_width = child_allocation.width + 2 * border_width;
	  float_height = child_allocation.height + 2 * border_width;
	  
692 693
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
694 695 696 697
	    float_width += DRAG_HANDLE_SIZE;
	  else
	    float_height += DRAG_HANDLE_SIZE;

698
	  if (gtk_widget_get_realized (widget))
699
	    {
700
	      gdk_window_resize (priv->float_window,
701 702
				 float_width,
				 float_height);
703
	      gdk_window_move_resize (priv->bin_window,
704 705 706 707 708
				      0,
				      0,
				      float_width,
				      float_height);
	    }
709 710 711
	}
      else
	{
712 713
	  child_allocation.width = MAX (1, (gint) allocation->width - 2 * border_width);
	  child_allocation.height = MAX (1, (gint) allocation->height - 2 * border_width);
714

715 716
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
717 718 719 720
	    child_allocation.width -= DRAG_HANDLE_SIZE;
	  else
	    child_allocation.height -= DRAG_HANDLE_SIZE;
	  
721
	  if (gtk_widget_get_realized (widget))
722
	    gdk_window_move_resize (priv->bin_window,
723 724
				    0,
				    0,
725 726
				    allocation->width,
				    allocation->height);
727 728
	}

Javier Jardón's avatar
Javier Jardón committed
729
      gtk_widget_size_allocate (child, &child_allocation);
730
    }
731
}
732

733
static void
734
gtk_handle_box_draw_ghost (GtkHandleBox *hb)
735
{
736 737 738 739 740
  GtkAllocation allocation;
  GtkWidget *widget = GTK_WIDGET (hb);
  GtkStateType state;
  GtkStyle *style;
  GdkWindow *window;
741 742 743 744
  guint x;
  guint y;
  guint width;
  guint height;
745
  gint handle_position;
746

747 748
  gtk_widget_get_allocation (widget, &allocation);

749 750 751
  handle_position = effective_handle_position (hb);
  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
752
    {
753
      x = handle_position == GTK_POS_LEFT ? 0 : allocation.width - DRAG_HANDLE_SIZE;
754 755
      y = 0;
      width = DRAG_HANDLE_SIZE;
756
      height = allocation.height;
757
    }
758 759 760
  else
    {
      x = 0;
761 762
      y = handle_position == GTK_POS_TOP ? 0 : allocation.height - DRAG_HANDLE_SIZE;
      width = allocation.width;
763 764
      height = DRAG_HANDLE_SIZE;
    }
765 766 767 768 769 770 771

  style = gtk_widget_get_style (widget);
  window = gtk_widget_get_window (widget);
  state = gtk_widget_get_state (widget);

  gtk_paint_shadow (style, window,
		    state, GTK_SHADOW_ETCHED_IN,
772 773 774 775 776
		    NULL, widget, "handle",
		    x,
		    y,
		    width,
		    height);
777 778
   if (handle_position == GTK_POS_LEFT ||
       handle_position == GTK_POS_RIGHT)
779 780
     gtk_paint_hline (style, window,
                      state,
781
		      NULL, widget, "handlebox",
782
		      handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
783 784
		      handle_position == GTK_POS_LEFT ? allocation.width : allocation.width - DRAG_HANDLE_SIZE,
		      allocation.height / 2);
785
   else
786 787
     gtk_paint_vline (style, window,
                      state,
788
		      NULL, widget, "handlebox",
789
		      handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
790 791
		      handle_position == GTK_POS_TOP ? allocation.height : allocation.height - DRAG_HANDLE_SIZE,
		      allocation.width / 2);
792 793
}

794
static void
795
draw_textured_frame (GtkWidget *widget, GdkWindow *window, GdkRectangle *rect, GtkShadowType shadow,
796
		     GdkRectangle *clip, GtkOrientation orientation)
797
{
798 799
   gtk_paint_handle (gtk_widget_get_style (widget), window,
                     GTK_STATE_NORMAL, shadow,
800 801
		     clip, widget, "handlebox",
		     rect->x, rect->y, rect->width, rect->height, 
802
		     orientation);
803 804
}

805 806 807 808
void
gtk_handle_box_set_shadow_type (GtkHandleBox  *handle_box,
				GtkShadowType  type)
{
809
  GtkHandleBoxPrivate *priv;
810

811 812
  g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));

813 814 815
  priv = handle_box->priv;

  if ((GtkShadowType) priv->shadow_type != type)
816
    {
817
      priv->shadow_type = type;
818
      g_object_notify (G_OBJECT (handle_box), "shadow-type");
819 820 821
      gtk_widget_queue_resize (GTK_WIDGET (handle_box));
    }
}
822

823 824 825 826 827 828 829 830 831 832 833 834 835 836
/**
 * gtk_handle_box_get_shadow_type:
 * @handle_box: a #GtkHandleBox
 * 
 * Gets the type of shadow drawn around the handle box. See
 * gtk_handle_box_set_shadow_type().
 *
 * Return value: the type of shadow currently drawn around the handle box.
 **/
GtkShadowType
gtk_handle_box_get_shadow_type (GtkHandleBox *handle_box)
{
  g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_SHADOW_ETCHED_OUT);

837
  return handle_box->priv->shadow_type;
838 839
}

840 841 842 843
void        
gtk_handle_box_set_handle_position  (GtkHandleBox    *handle_box,
				     GtkPositionType  position)
{
844
  GtkHandleBoxPrivate *priv;
845

846 847
  g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));

848 849 850
  priv = handle_box->priv;

  if ((GtkPositionType) priv->handle_position != position)
851
    {
852
      priv->handle_position = position;
853
      g_object_notify (G_OBJECT (handle_box), "handle-position");
854 855 856 857
      gtk_widget_queue_resize (GTK_WIDGET (handle_box));
    }
}

858 859 860 861 862 863 864 865 866 867 868 869 870 871
/**
 * gtk_handle_box_get_handle_position:
 * @handle_box: a #GtkHandleBox
 *
 * Gets the handle position of the handle box. See
 * gtk_handle_box_set_handle_position().
 *
 * Return value: the current handle position.
 **/
GtkPositionType
gtk_handle_box_get_handle_position (GtkHandleBox *handle_box)
{
  g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_POS_LEFT);

872
  return handle_box->priv->handle_position;