gtkhandlebox.c 41.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
#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 "gtkprivate.h"
36
#include "gtkintl.h"
37
#include "gtkalias.h"
38

39 40 41 42 43 44 45 46
typedef struct _GtkHandleBoxPrivate GtkHandleBoxPrivate;

struct _GtkHandleBoxPrivate
{
  gint orig_x;
  gint orig_y;
};

47
enum {
48 49
  PROP_0,
  PROP_SHADOW,
50
  PROP_SHADOW_TYPE,
51
  PROP_HANDLE_POSITION,
52
  PROP_SNAP_EDGE,
Tim Janik's avatar
Tim Janik committed
53 54
  PROP_SNAP_EDGE_SET,
  PROP_CHILD_DETACHED
55
};
56

Elliot Lee's avatar
Elliot Lee committed
57
#define DRAG_HANDLE_SIZE 10
58
#define CHILDLESS_SIZE	25
59
#define GHOST_HEIGHT 3
60
#define TOLERANCE 5
61

62
enum {
63 64 65 66 67
  SIGNAL_CHILD_ATTACHED,
  SIGNAL_CHILD_DETACHED,
  SIGNAL_LAST
};

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
/* 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-------------------->
 */

108 109 110 111 112 113 114 115 116 117 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
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 };
147

Matthias Clasen's avatar
Matthias Clasen committed
148
G_DEFINE_TYPE (GtkHandleBox, gtk_handle_box, GTK_TYPE_BIN)
149 150 151 152

static void
gtk_handle_box_class_init (GtkHandleBoxClass *class)
{
153
  GObjectClass *gobject_class;
154 155
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;
156

157
  gobject_class = (GObjectClass *) class;
158
  widget_class = (GtkWidgetClass *) class;
159
  container_class = (GtkContainerClass *) class;
160

161 162 163 164 165
  gobject_class->set_property = gtk_handle_box_set_property;
  gobject_class->get_property = gtk_handle_box_get_property;
  
  g_object_class_install_property (gobject_class,
                                   PROP_SHADOW,
166
                                   g_param_spec_enum ("shadow", NULL,
167
                                                      P_("Deprecated property, use shadow_type instead"),
168
						      GTK_TYPE_SHADOW_TYPE,
Matthias Clasen's avatar
Matthias Clasen committed
169
						      GTK_SHADOW_OUT,
170
                                                      GTK_PARAM_READWRITE));
171 172
  g_object_class_install_property (gobject_class,
                                   PROP_SHADOW_TYPE,
173
                                   g_param_spec_enum ("shadow-type",
174 175
                                                      P_("Shadow type"),
                                                      P_("Appearance of the shadow that surrounds the container"),
176
						      GTK_TYPE_SHADOW_TYPE,
Matthias Clasen's avatar
Matthias Clasen committed
177
						      GTK_SHADOW_OUT,
178
                                                      GTK_PARAM_READWRITE));
179
  
180 181
  g_object_class_install_property (gobject_class,
                                   PROP_HANDLE_POSITION,
182
                                   g_param_spec_enum ("handle-position",
183 184
                                                      P_("Handle position"),
                                                      P_("Position of the handle relative to the child widget"),
185 186
						      GTK_TYPE_POSITION_TYPE,
						      GTK_POS_LEFT,
187
                                                      GTK_PARAM_READWRITE));
188
  
189 190
  g_object_class_install_property (gobject_class,
                                   PROP_SNAP_EDGE,
191
                                   g_param_spec_enum ("snap-edge",
192 193
                                                      P_("Snap edge"),
                                                      P_("Side of the handlebox that's lined up with the docking point to dock the handlebox"),
194
						      GTK_TYPE_POSITION_TYPE,
195
						      GTK_POS_TOP,
196
                                                      GTK_PARAM_READWRITE));
Manish Singh's avatar
Manish Singh committed
197

198 199
  g_object_class_install_property (gobject_class,
                                   PROP_SNAP_EDGE_SET,
200
                                   g_param_spec_boolean ("snap-edge-set",
201 202
							 P_("Snap edge set"),
							 P_("Whether to use the value from the snap_edge property or a value derived from handle_position"),
203
							 FALSE,
204
							 GTK_PARAM_READWRITE));
205

Tim Janik's avatar
Tim Janik committed
206 207 208 209 210 211 212 213
  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));

214 215
  widget_class->map = gtk_handle_box_map;
  widget_class->unmap = gtk_handle_box_unmap;
216
  widget_class->realize = gtk_handle_box_realize;
217
  widget_class->unrealize = gtk_handle_box_unrealize;
Owen Taylor's avatar
Owen Taylor committed
218
  widget_class->style_set = gtk_handle_box_style_set;
219 220 221
  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;
222
  widget_class->button_press_event = gtk_handle_box_button_press;
223
  widget_class->delete_event = gtk_handle_box_delete_event;
224

225
  container_class->add = gtk_handle_box_add;
226 227
  container_class->remove = gtk_handle_box_remove;

228 229
  class->child_attached = NULL;
  class->child_detached = NULL;
230 231

  handle_box_signals[SIGNAL_CHILD_ATTACHED] =
232
    g_signal_new (I_("child-attached"),
Manish Singh's avatar
Manish Singh committed
233 234 235 236 237 238 239
		  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);
240
  handle_box_signals[SIGNAL_CHILD_DETACHED] =
241
    g_signal_new (I_("child-detached"),
Manish Singh's avatar
Manish Singh committed
242 243 244 245 246 247 248
		  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);
249 250

  g_type_class_add_private (gobject_class, sizeof (GtkHandleBoxPrivate));    
251 252
}

253
static GtkHandleBoxPrivate *
254 255
gtk_handle_box_get_private (GtkHandleBox *hb)
{
256
  return G_TYPE_INSTANCE_GET_PRIVATE (hb, GTK_TYPE_HANDLE_BOX, GtkHandleBoxPrivate);
257 258
}

259
static void
260
gtk_handle_box_init (GtkHandleBox *handle_box)
261
{
262
  GTK_WIDGET_UNSET_FLAGS (handle_box, GTK_NO_WINDOW);
263

264
  handle_box->bin_window = NULL;
265
  handle_box->float_window = NULL;
266
  handle_box->shadow_type = GTK_SHADOW_OUT;
267 268 269 270
  handle_box->handle_position = GTK_POS_LEFT;
  handle_box->float_window_mapped = FALSE;
  handle_box->child_detached = FALSE;
  handle_box->in_drag = FALSE;
271
  handle_box->shrink_on_detach = TRUE;
272
  handle_box->snap_edge = -1;
273 274
}

275 276 277 278 279
static void 
gtk_handle_box_set_property (GObject         *object,
			     guint            prop_id,
			     const GValue    *value,
			     GParamSpec      *pspec)
280
{
281
  GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
282

283
  switch (prop_id)
284
    {
285
    case PROP_SHADOW:
286
    case PROP_SHADOW_TYPE:
287 288 289 290
      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));
291
      break;
292 293
    case PROP_SNAP_EDGE:
      gtk_handle_box_set_snap_edge (handle_box, g_value_get_enum (value));
294
      break;
295 296 297 298
    case PROP_SNAP_EDGE_SET:
      if (!g_value_get_boolean (value))
	gtk_handle_box_set_snap_edge (handle_box, (GtkPositionType)-1);
      break;
299 300
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
301 302 303 304
      break;
    }
}

305 306 307 308 309
static void 
gtk_handle_box_get_property (GObject         *object,
			     guint            prop_id,
			     GValue          *value,
			     GParamSpec      *pspec)
310
{
311 312
  GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
  
313
  switch (prop_id)
314
    {
315
    case PROP_SHADOW:
316
    case PROP_SHADOW_TYPE:
317
      g_value_set_enum (value, handle_box->shadow_type);
318
      break;
319 320
    case PROP_HANDLE_POSITION:
      g_value_set_enum (value, handle_box->handle_position);
321
      break;
322
    case PROP_SNAP_EDGE:
323 324 325 326 327 328
      g_value_set_enum (value,
			(handle_box->snap_edge == -1 ?
			 GTK_POS_TOP : handle_box->snap_edge));
      break;
    case PROP_SNAP_EDGE_SET:
      g_value_set_boolean (value, handle_box->snap_edge != -1);
329
      break;
Tim Janik's avatar
Tim Janik committed
330 331 332
    case PROP_CHILD_DETACHED:
      g_value_set_boolean (value, handle_box->child_detached);
      break;
333
    default:
334
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
335 336 337 338
      break;
    }
}
 
339
GtkWidget*
340 341
gtk_handle_box_new (void)
{
Manish Singh's avatar
Manish Singh committed
342
  return g_object_new (GTK_TYPE_HANDLE_BOX, NULL);
343 344 345 346 347 348 349 350 351 352 353 354 355
}

static void
gtk_handle_box_map (GtkWidget *widget)
{
  GtkBin *bin;
  GtkHandleBox *hb;

  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);

  bin = GTK_BIN (widget);
  hb = GTK_HANDLE_BOX (widget);

356 357 358 359
  if (bin->child &&
      GTK_WIDGET_VISIBLE (bin->child) &&
      !GTK_WIDGET_MAPPED (bin->child))
    gtk_widget_map (bin->child);
360

361 362 363 364 365 366
  if (hb->child_detached && !hb->float_window_mapped)
    {
      gdk_window_show (hb->float_window);
      hb->float_window_mapped = TRUE;
    }

367 368
  gdk_window_show (hb->bin_window);
  gdk_window_show (widget->window);
369 370 371 372 373 374 375 376 377 378 379 380
}

static void
gtk_handle_box_unmap (GtkWidget *widget)
{
  GtkHandleBox *hb;

  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);

  hb = GTK_HANDLE_BOX (widget);

  gdk_window_hide (widget->window);
381 382 383 384 385
  if (hb->float_window_mapped)
    {
      gdk_window_hide (hb->float_window);
      hb->float_window_mapped = FALSE;
    }
386 387 388 389 390 391 392
}

static void
gtk_handle_box_realize (GtkWidget *widget)
{
  GdkWindowAttr attributes;
  gint attributes_mask;
393
  GtkHandleBox *hb;
394

395 396
  hb = GTK_HANDLE_BOX (widget);

397 398 399 400 401 402 403 404 405 406
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  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);
407 408
  attributes.event_mask = (gtk_widget_get_events (widget)
			   | GDK_EXPOSURE_MASK);
409
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
410 411
  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
  gdk_window_set_user_data (widget->window, widget);
412 413 414

  attributes.x = 0;
  attributes.y = 0;
415 416 417
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.window_type = GDK_WINDOW_CHILD;
Soeren Sandmann's avatar
Soeren Sandmann committed
418 419 420 421 422
  attributes.event_mask = (gtk_widget_get_events (widget) |
			   GDK_EXPOSURE_MASK |
			   GDK_BUTTON1_MOTION_MASK |
			   GDK_POINTER_MOTION_HINT_MASK |
			   GDK_BUTTON_PRESS_MASK |
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
			    GDK_BUTTON_RELEASE_MASK);
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
  hb->bin_window = gdk_window_new (widget->window, &attributes, attributes_mask);
  gdk_window_set_user_data (hb->bin_window, widget);
  if (GTK_BIN (hb)->child)
    gtk_widget_set_parent_window (GTK_BIN (hb)->child, hb->bin_window);
  
  attributes.x = 0;
  attributes.y = 0;
  attributes.width = widget->requisition.width;
  attributes.height = widget->requisition.height;
  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
444 445
  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;
446 447
  hb->float_window = gdk_window_new (gtk_widget_get_root_window (widget),
				     &attributes, attributes_mask);
448 449
  gdk_window_set_user_data (hb->float_window, widget);
  gdk_window_set_decorations (hb->float_window, 0);
450
  gdk_window_set_type_hint (hb->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
451
  
452
  widget->style = gtk_style_attach (widget->style, widget->window);
453 454 455
  gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (hb));
  gtk_style_set_background (widget->style, hb->bin_window, GTK_WIDGET_STATE (hb));
  gtk_style_set_background (widget->style, hb->float_window, GTK_WIDGET_STATE (hb));
456
  gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
457 458
}

459 460 461
static void
gtk_handle_box_unrealize (GtkWidget *widget)
{
462
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
463

464 465 466 467 468
  gdk_window_set_user_data (hb->bin_window, NULL);
  gdk_window_destroy (hb->bin_window);
  hb->bin_window = NULL;
  gdk_window_set_user_data (hb->float_window, NULL);
  gdk_window_destroy (hb->float_window);
469
  hb->float_window = NULL;
470

471
  GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize (widget);
472 473
}

Owen Taylor's avatar
Owen Taylor committed
474 475 476 477
static void
gtk_handle_box_style_set (GtkWidget *widget,
			  GtkStyle  *previous_style)
{
478
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
Owen Taylor's avatar
Owen Taylor committed
479 480 481 482 483

  if (GTK_WIDGET_REALIZED (widget) &&
      !GTK_WIDGET_NO_WINDOW (widget))
    {
      gtk_style_set_background (widget->style, widget->window,
Soeren Sandmann's avatar
Soeren Sandmann committed
484
				widget->state);
Owen Taylor's avatar
Owen Taylor committed
485 486 487 488 489
      gtk_style_set_background (widget->style, hb->bin_window, widget->state);
      gtk_style_set_background (widget->style, hb->float_window, widget->state);
    }
}

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
static int
effective_handle_position (GtkHandleBox *hb)
{
  int handle_position;

  if (gtk_widget_get_direction (GTK_WIDGET (hb)) == GTK_TEXT_DIR_LTR)
    handle_position = hb->handle_position;
  else
    {
      switch (hb->handle_position) 
	{
	case GTK_POS_LEFT:
	  handle_position = GTK_POS_RIGHT;
	  break;
	case GTK_POS_RIGHT:
	  handle_position = GTK_POS_LEFT;
	  break;
	default:
	  handle_position = hb->handle_position;
	  break;
	}
    }

  return handle_position;
}

516 517
static void
gtk_handle_box_size_request (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
518
			     GtkRequisition *requisition)
519 520
{
  GtkBin *bin;
521
  GtkHandleBox *hb;
522
  GtkRequisition child_requisition;
523
  gint handle_position;
524 525

  bin = GTK_BIN (widget);
526
  hb = GTK_HANDLE_BOX (widget);
527

528 529 530 531
  handle_position = effective_handle_position (hb);

  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
532 533 534 535 536
    {
      requisition->width = DRAG_HANDLE_SIZE;
      requisition->height = 0;
    }
  else
537
    {
538 539 540
      requisition->width = 0;
      requisition->height = DRAG_HANDLE_SIZE;
    }
541

542
  /* if our child is not visible, we still request its size, since we
543
   * won't have any useful hint for our size otherwise.
544 545
   */
  if (bin->child)
546
    gtk_widget_size_request (bin->child, &child_requisition);
547 548 549 550 551
  else
    {
      child_requisition.width = 0;
      child_requisition.height = 0;
    }      
552 553 554

  if (hb->child_detached)
    {
555
      /* FIXME: This doesn't work currently */
556 557
      if (!hb->shrink_on_detach)
	{
558 559
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
560
	    requisition->height += child_requisition.height;
561
	  else
562
	    requisition->width += child_requisition.width;
563
	}
564
      else
565
	{
566 567
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
568
	    requisition->height += widget->style->ythickness;
569
	  else
570
	    requisition->width += widget->style->xthickness;
571
	}
572
    }
573 574
  else
    {
575 576 577 578 579
      requisition->width += GTK_CONTAINER (widget)->border_width * 2;
      requisition->height += GTK_CONTAINER (widget)->border_width * 2;
      
      if (bin->child)
	{
580 581
	  requisition->width += child_requisition.width;
	  requisition->height += child_requisition.height;
582 583 584 585 586 587
	}
      else
	{
	  requisition->width += CHILDLESS_SIZE;
	  requisition->height += CHILDLESS_SIZE;
	}
588
    }
589 590 591 592
}

static void
gtk_handle_box_size_allocate (GtkWidget     *widget,
593
			      GtkAllocation *allocation)
594 595
{
  GtkBin *bin;
596
  GtkHandleBox *hb;
597
  GtkRequisition child_requisition;
598
  gint handle_position;
599
  
600
  bin = GTK_BIN (widget);
601
  hb = GTK_HANDLE_BOX (widget);
602
  
603 604
  handle_position = effective_handle_position (hb);

605 606 607 608 609 610
  if (bin->child)
    gtk_widget_get_child_requisition (bin->child, &child_requisition);
  else
    {
      child_requisition.width = 0;
      child_requisition.height = 0;
611
    }      
612
      
613
  widget->allocation = *allocation;
614

615 616 617 618 619 620
  if (GTK_WIDGET_REALIZED (hb))
    gdk_window_move_resize (widget->window,
			    widget->allocation.x,
			    widget->allocation.y,
			    widget->allocation.width,
			    widget->allocation.height);
621

622 623 624

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
    {
625
      GtkAllocation child_allocation;
626 627 628
      guint border_width;

      border_width = GTK_CONTAINER (widget)->border_width;
629

630 631
      child_allocation.x = border_width;
      child_allocation.y = border_width;
632
      if (handle_position == GTK_POS_LEFT)
633
	child_allocation.x += DRAG_HANDLE_SIZE;
634
      else if (handle_position == GTK_POS_TOP)
635
	child_allocation.y += DRAG_HANDLE_SIZE;
636

637
      if (hb->child_detached)
638
	{
639 640 641
	  guint float_width;
	  guint float_height;
	  
642 643
	  child_allocation.width = child_requisition.width;
	  child_allocation.height = child_requisition.height;
644 645 646 647
	  
	  float_width = child_allocation.width + 2 * border_width;
	  float_height = child_allocation.height + 2 * border_width;
	  
648 649
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
	    float_width += DRAG_HANDLE_SIZE;
	  else
	    float_height += DRAG_HANDLE_SIZE;

	  if (GTK_WIDGET_REALIZED (hb))
	    {
	      gdk_window_resize (hb->float_window,
				 float_width,
				 float_height);
	      gdk_window_move_resize (hb->bin_window,
				      0,
				      0,
				      float_width,
				      float_height);
	    }
665 666 667
	}
      else
	{
668 669
	  child_allocation.width = MAX (1, (gint)widget->allocation.width - 2 * border_width);
	  child_allocation.height = MAX (1, (gint)widget->allocation.height - 2 * border_width);
670

671 672
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
673 674 675 676 677 678 679 680 681 682
	    child_allocation.width -= DRAG_HANDLE_SIZE;
	  else
	    child_allocation.height -= DRAG_HANDLE_SIZE;
	  
	  if (GTK_WIDGET_REALIZED (hb))
	    gdk_window_move_resize (hb->bin_window,
				    0,
				    0,
				    widget->allocation.width,
				    widget->allocation.height);
683 684
	}

685
      gtk_widget_size_allocate (bin->child, &child_allocation);
686
    }
687
}
688

689
static void
690
gtk_handle_box_draw_ghost (GtkHandleBox *hb)
691
{
692 693 694 695 696
  GtkWidget *widget;
  guint x;
  guint y;
  guint width;
  guint height;
697
  gint handle_position;
698

699
  widget = GTK_WIDGET (hb);
700 701 702 703
  
  handle_position = effective_handle_position (hb);
  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
704
    {
705
      x = handle_position == GTK_POS_LEFT ? 0 : widget->allocation.width - DRAG_HANDLE_SIZE;
706 707 708
      y = 0;
      width = DRAG_HANDLE_SIZE;
      height = widget->allocation.height;
709
    }
710 711 712
  else
    {
      x = 0;
713
      y = handle_position == GTK_POS_TOP ? 0 : widget->allocation.height - DRAG_HANDLE_SIZE;
714 715 716
      width = widget->allocation.width;
      height = DRAG_HANDLE_SIZE;
    }
717
  gtk_paint_shadow (widget->style,
718 719
		    widget->window,
		    GTK_WIDGET_STATE (widget),
720 721 722 723 724 725
		    GTK_SHADOW_ETCHED_IN,
		    NULL, widget, "handle",
		    x,
		    y,
		    width,
		    height);
726 727
   if (handle_position == GTK_POS_LEFT ||
       handle_position == GTK_POS_RIGHT)
728 729 730 731
     gtk_paint_hline (widget->style,
		      widget->window,
		      GTK_WIDGET_STATE (widget),
		      NULL, widget, "handlebox",
732 733
		      handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
		      handle_position == GTK_POS_LEFT ? widget->allocation.width : widget->allocation.width - DRAG_HANDLE_SIZE,
734 735 736 737 738 739
		      widget->allocation.height / 2);
   else
     gtk_paint_vline (widget->style,
		      widget->window,
		      GTK_WIDGET_STATE (widget),
		      NULL, widget, "handlebox",
740 741
		      handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
		      handle_position == GTK_POS_TOP ? widget->allocation.height : widget->allocation.height - DRAG_HANDLE_SIZE,
742
		      widget->allocation.width / 2);
743 744
}

745
static void
746
draw_textured_frame (GtkWidget *widget, GdkWindow *window, GdkRectangle *rect, GtkShadowType shadow,
747
		     GdkRectangle *clip, GtkOrientation orientation)
748
{
749 750 751
   gtk_paint_handle (widget->style, window, GTK_STATE_NORMAL, shadow,
		     clip, widget, "handlebox",
		     rect->x, rect->y, rect->width, rect->height, 
752
		     orientation);
753 754
}

755 756 757 758 759 760 761 762 763
void
gtk_handle_box_set_shadow_type (GtkHandleBox  *handle_box,
				GtkShadowType  type)
{
  g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));

  if ((GtkShadowType) handle_box->shadow_type != type)
    {
      handle_box->shadow_type = type;
764
      g_object_notify (G_OBJECT (handle_box), "shadow-type");
765 766 767
      gtk_widget_queue_resize (GTK_WIDGET (handle_box));
    }
}
768

769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
/**
 * 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);

  return handle_box->shadow_type;
}

786 787 788 789
void        
gtk_handle_box_set_handle_position  (GtkHandleBox    *handle_box,
				     GtkPositionType  position)
{
790 791
  g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));

792 793 794
  if ((GtkPositionType) handle_box->handle_position != position)
    {
      handle_box->handle_position = position;
795
      g_object_notify (G_OBJECT (handle_box), "handle-position");
796 797 798 799
      gtk_widget_queue_resize (GTK_WIDGET (handle_box));
    }
}

800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
/**
 * 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);

  return handle_box->handle_position;
}

817 818 819 820 821 822
void        
gtk_handle_box_set_snap_edge        (GtkHandleBox    *handle_box,
				     GtkPositionType  edge)
{
  g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));

823 824 825
  if (handle_box->snap_edge != edge)
    {
      handle_box->snap_edge = edge;
826 827
      
      g_object_freeze_notify (G_OBJECT (handle_box));
828 829
      g_object_notify (G_OBJECT (handle_box), "snap-edge");
      g_object_notify (G_OBJECT (handle_box), "snap-edge-set");
830
      g_object_thaw_notify (G_OBJECT (handle_box));
831
    }
832 833
}

834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
/**
 * gtk_handle_box_get_snap_edge:
 * @handle_box: a #GtkHandleBox
 * 
 * Gets the edge used for determining reattachment of the handle box. See
 * gtk_handle_box_set_snap_edge().
 *
 * Return value: the edge used for determining reattachment, or (GtkPositionType)-1 if this
 *               is determined (as per default) from the handle position. 
 **/
GtkPositionType
gtk_handle_box_get_snap_edge (GtkHandleBox *handle_box)
{
  g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), (GtkPositionType)-1);

  return handle_box->snap_edge;
}

Tim Janik's avatar
Tim Janik committed
852 853 854 855 856 857 858 859
/**
 * gtk_handle_box_get_child_detached:
 * @handle_box: a #GtkHandleBox
 *
 * Whether the handlebox's child is currently detached.
 *
 * Return value: %TRUE if the child is currently detached, otherwise %FALSE
 *
860
 * Since: 2.14
Tim Janik's avatar
Tim Janik committed
861 862 863 864 865 866 867 868 869
 **/
gboolean
gtk_handle_box_get_child_detached (GtkHandleBox *handle_box)
{
  g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), FALSE);

  return handle_box->child_detached;
}

870
static void
871
gtk_handle_box_paint (GtkWidget      *widget,
872
                      GdkEventExpose *event,
873
		      GdkRectangle   *area)
Elliot Lee's avatar
Elliot Lee committed
874
{
875
  GtkBin *bin;
876
  GtkHandleBox *hb;
Yevgen Muntyan's avatar
Yevgen Muntyan committed
877
  gint width, height;
878
  GdkRectangle rect;
879
  GdkRectangle dest;
880
  gint handle_position;
881
  GtkOrientation handle_orientation;
882

883 884
  bin = GTK_BIN (widget);
  hb = GTK_HANDLE_BOX (widget);
885

886 887
  handle_position = effective_handle_position (hb);

Manish Singh's avatar
Manish Singh committed
888
  gdk_drawable_get_size (hb->bin_window, &width, &height);
889
  
890
  if (!event)
891 892 893 894 895 896
    gtk_paint_box (widget->style,
		   hb->bin_window,
		   GTK_WIDGET_STATE (widget),
		   hb->shadow_type,
		   area, widget, "handlebox_bin",
		   0, 0, -1, -1);
897
  else
898 899 900 901 902 903
   gtk_paint_box (widget->style,
		  hb->bin_window,
		  GTK_WIDGET_STATE (widget),
		  hb->shadow_type,
		  &event->area, widget, "handlebox_bin",
		  0, 0, -1, -1);
904 905 906 907 908 909 910 911

/* We currently draw the handle _above_ the relief of the handlebox.
 * it could also be drawn on the same level...

		 hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
		 hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
		 width,
		 height);*/
912

913
  switch (handle_position)
914 915 916 917 918 919
    {
    case GTK_POS_LEFT:
      rect.x = 0;
      rect.y = 0; 
      rect.width = DRAG_HANDLE_SIZE;
      rect.height = height;
920
      handle_orientation = GTK_ORIENTATION_VERTICAL;
921 922 923 924 925 926
      break;
    case GTK_POS_RIGHT:
      rect.x = width - DRAG_HANDLE_SIZE; 
      rect.y = 0;
      rect.width = DRAG_HANDLE_SIZE;
      rect.height = height;
927
      handle_orientation = GTK_ORIENTATION_VERTIC