gtkhandlebox.c 40.8 KB
Newer Older
1 2
/* GTK - The GIMP Toolkit
 * 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
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);
116 117 118 119 120
static void gtk_handle_box_destroy        (GtkObject         *object);
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);
Owen Taylor's avatar
Owen Taylor committed
121 122
static void gtk_handle_box_style_set      (GtkWidget         *widget,
					   GtkStyle          *previous_style);
123 124 125
static void gtk_handle_box_size_request   (GtkWidget         *widget,
					   GtkRequisition    *requisition);
static void gtk_handle_box_size_allocate  (GtkWidget         *widget,
126
					   GtkAllocation     *real_allocation);
127 128
static void gtk_handle_box_add            (GtkContainer      *container,
					   GtkWidget         *widget);
129 130
static void gtk_handle_box_remove         (GtkContainer      *container,
					   GtkWidget         *widget);
131
static void gtk_handle_box_draw_ghost     (GtkHandleBox      *hb);
132 133 134 135 136 137 138 139 140
static void gtk_handle_box_paint          (GtkWidget         *widget,
					   GdkEventExpose    *event,
					   GdkRectangle      *area);
static gint gtk_handle_box_expose         (GtkWidget         *widget,
					   GdkEventExpose    *event);
static gint gtk_handle_box_button_changed (GtkWidget         *widget,
					   GdkEventButton    *event);
static gint gtk_handle_box_motion         (GtkWidget         *widget,
					   GdkEventMotion    *event);
141 142
static gint gtk_handle_box_delete_event   (GtkWidget         *widget,
					   GdkEventAny       *event);
143
static void gtk_handle_box_reattach       (GtkHandleBox      *hb);
144 145
static void gtk_handle_box_end_drag       (GtkHandleBox      *hb,
					   guint32            time);
146

147
static guint        handle_box_signals[SIGNAL_LAST] = { 0 };
148

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

static void
gtk_handle_box_class_init (GtkHandleBoxClass *class)
{
154
  GObjectClass *gobject_class;
155
  GtkObjectClass *object_class;
156 157
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;
158

159
  gobject_class = (GObjectClass *) class;
160 161
  object_class = (GtkObjectClass *) class;
  widget_class = (GtkWidgetClass *) class;
162
  container_class = (GtkContainerClass *) class;
163

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

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

Tim Janik's avatar
Tim Janik committed
209 210 211 212 213 214 215 216
  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));

217 218 219 220
  object_class->destroy = gtk_handle_box_destroy;

  widget_class->map = gtk_handle_box_map;
  widget_class->unmap = gtk_handle_box_unmap;
221
  widget_class->realize = gtk_handle_box_realize;
222
  widget_class->unrealize = gtk_handle_box_unrealize;
Owen Taylor's avatar
Owen Taylor committed
223
  widget_class->style_set = gtk_handle_box_style_set;
224 225 226
  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;
Elliot Lee's avatar
Elliot Lee committed
227
  widget_class->button_press_event = gtk_handle_box_button_changed;
228
  widget_class->delete_event = gtk_handle_box_delete_event;
229

230
  container_class->add = gtk_handle_box_add;
231 232
  container_class->remove = gtk_handle_box_remove;

233 234
  class->child_attached = NULL;
  class->child_detached = NULL;
235 236

  handle_box_signals[SIGNAL_CHILD_ATTACHED] =
237
    g_signal_new (I_("child_attached"),
Manish Singh's avatar
Manish Singh committed
238 239 240 241 242 243 244
		  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);
245
  handle_box_signals[SIGNAL_CHILD_DETACHED] =
246
    g_signal_new (I_("child_detached"),
Manish Singh's avatar
Manish Singh committed
247 248 249 250 251 252 253
		  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);
254 255

  g_type_class_add_private (gobject_class, sizeof (GtkHandleBoxPrivate));    
256 257
}

258
static GtkHandleBoxPrivate *
259 260
gtk_handle_box_get_private (GtkHandleBox *hb)
{
261
  return G_TYPE_INSTANCE_GET_PRIVATE (hb, GTK_TYPE_HANDLE_BOX, GtkHandleBoxPrivate);
262 263
}

264
static void
265
gtk_handle_box_init (GtkHandleBox *handle_box)
266
{
267
  GTK_WIDGET_UNSET_FLAGS (handle_box, GTK_NO_WINDOW);
268

269
  handle_box->bin_window = NULL;
270
  handle_box->float_window = NULL;
271
  handle_box->shadow_type = GTK_SHADOW_OUT;
272 273 274 275
  handle_box->handle_position = GTK_POS_LEFT;
  handle_box->float_window_mapped = FALSE;
  handle_box->child_detached = FALSE;
  handle_box->in_drag = FALSE;
276
  handle_box->shrink_on_detach = TRUE;
277
  handle_box->snap_edge = -1;
278 279
}

280 281 282 283 284
static void 
gtk_handle_box_set_property (GObject         *object,
			     guint            prop_id,
			     const GValue    *value,
			     GParamSpec      *pspec)
285
{
286
  GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
287

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

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

static void
gtk_handle_box_destroy (GtkObject *object)
352
{
Matthias Clasen's avatar
Matthias Clasen committed
353 354
  if (GTK_OBJECT_CLASS (gtk_handle_box_parent_class)->destroy)
    (* GTK_OBJECT_CLASS (gtk_handle_box_parent_class)->destroy) (object);
355 356 357 358 359 360 361 362 363 364 365 366 367
}

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

368 369 370 371
  if (bin->child &&
      GTK_WIDGET_VISIBLE (bin->child) &&
      !GTK_WIDGET_MAPPED (bin->child))
    gtk_widget_map (bin->child);
372

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

379 380
  gdk_window_show (hb->bin_window);
  gdk_window_show (widget->window);
381 382 383 384 385 386 387 388 389 390 391 392
}

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);
393 394 395 396 397
  if (hb->float_window_mapped)
    {
      gdk_window_hide (hb->float_window);
      hb->float_window_mapped = FALSE;
    }
398 399 400 401 402 403 404
}

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

407 408
  hb = GTK_HANDLE_BOX (widget);

409 410 411 412 413 414 415 416 417 418
  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);
419 420
  attributes.event_mask = (gtk_widget_get_events (widget)
			   | GDK_EXPOSURE_MASK);
421
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
422 423
  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
  gdk_window_set_user_data (widget->window, widget);
424 425 426

  attributes.x = 0;
  attributes.y = 0;
427 428 429
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.window_type = GDK_WINDOW_CHILD;
Soeren Sandmann's avatar
Soeren Sandmann committed
430 431 432 433 434
  attributes.event_mask = (gtk_widget_get_events (widget) |
			   GDK_EXPOSURE_MASK |
			   GDK_BUTTON1_MOTION_MASK |
			   GDK_POINTER_MOTION_HINT_MASK |
			   GDK_BUTTON_PRESS_MASK |
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
			    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);
456 457
  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;
458 459
  hb->float_window = gdk_window_new (gtk_widget_get_root_window (widget),
				     &attributes, attributes_mask);
460 461
  gdk_window_set_user_data (hb->float_window, widget);
  gdk_window_set_decorations (hb->float_window, 0);
462
  gdk_window_set_type_hint (hb->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
463
  
464
  widget->style = gtk_style_attach (widget->style, widget->window);
465 466 467
  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));
468
  gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
469 470
}

471 472 473
static void
gtk_handle_box_unrealize (GtkWidget *widget)
{
474
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
475

476 477 478 479 480
  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);
481
  hb->float_window = NULL;
482

Matthias Clasen's avatar
Matthias Clasen committed
483 484
  if (GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize) (widget);
485 486
}

Owen Taylor's avatar
Owen Taylor committed
487 488 489 490
static void
gtk_handle_box_style_set (GtkWidget *widget,
			  GtkStyle  *previous_style)
{
491
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
Owen Taylor's avatar
Owen Taylor committed
492 493 494 495 496

  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
497
				widget->state);
Owen Taylor's avatar
Owen Taylor committed
498 499 500 501 502
      gtk_style_set_background (widget->style, hb->bin_window, widget->state);
      gtk_style_set_background (widget->style, hb->float_window, widget->state);
    }
}

503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
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;
}

529 530
static void
gtk_handle_box_size_request (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
531
			     GtkRequisition *requisition)
532 533
{
  GtkBin *bin;
534
  GtkHandleBox *hb;
535
  GtkRequisition child_requisition;
536
  gint handle_position;
537 538

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

541 542 543 544
  handle_position = effective_handle_position (hb);

  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
545 546 547 548 549
    {
      requisition->width = DRAG_HANDLE_SIZE;
      requisition->height = 0;
    }
  else
550
    {
551 552 553
      requisition->width = 0;
      requisition->height = DRAG_HANDLE_SIZE;
    }
554

555
  /* if our child is not visible, we still request its size, since we
556
   * won't have any useful hint for our size otherwise.
557 558
   */
  if (bin->child)
559
    gtk_widget_size_request (bin->child, &child_requisition);
560 561 562 563 564
  else
    {
      child_requisition.width = 0;
      child_requisition.height = 0;
    }      
565 566 567

  if (hb->child_detached)
    {
568
      /* FIXME: This doesn't work currently */
569 570
      if (!hb->shrink_on_detach)
	{
571 572
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
573
	    requisition->height += child_requisition.height;
574
	  else
575
	    requisition->width += child_requisition.width;
576
	}
577
      else
578
	{
579 580
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
581
	    requisition->height += widget->style->ythickness;
582
	  else
583
	    requisition->width += widget->style->xthickness;
584
	}
585
    }
586 587
  else
    {
588 589 590 591 592
      requisition->width += GTK_CONTAINER (widget)->border_width * 2;
      requisition->height += GTK_CONTAINER (widget)->border_width * 2;
      
      if (bin->child)
	{
593 594
	  requisition->width += child_requisition.width;
	  requisition->height += child_requisition.height;
595 596 597 598 599 600
	}
      else
	{
	  requisition->width += CHILDLESS_SIZE;
	  requisition->height += CHILDLESS_SIZE;
	}
601
    }
602 603 604 605
}

static void
gtk_handle_box_size_allocate (GtkWidget     *widget,
606
			      GtkAllocation *allocation)
607 608
{
  GtkBin *bin;
609
  GtkHandleBox *hb;
610
  GtkRequisition child_requisition;
611
  gint handle_position;
612
  
613
  bin = GTK_BIN (widget);
614
  hb = GTK_HANDLE_BOX (widget);
615
  
616 617
  handle_position = effective_handle_position (hb);

618 619 620 621 622 623
  if (bin->child)
    gtk_widget_get_child_requisition (bin->child, &child_requisition);
  else
    {
      child_requisition.width = 0;
      child_requisition.height = 0;
624
    }      
625
      
626
  widget->allocation = *allocation;
627

628 629 630 631 632 633
  if (GTK_WIDGET_REALIZED (hb))
    gdk_window_move_resize (widget->window,
			    widget->allocation.x,
			    widget->allocation.y,
			    widget->allocation.width,
			    widget->allocation.height);
634

635 636 637

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
    {
638
      GtkAllocation child_allocation;
639 640 641
      guint border_width;

      border_width = GTK_CONTAINER (widget)->border_width;
642

643 644
      child_allocation.x = border_width;
      child_allocation.y = border_width;
645
      if (handle_position == GTK_POS_LEFT)
646
	child_allocation.x += DRAG_HANDLE_SIZE;
647
      else if (handle_position == GTK_POS_TOP)
648
	child_allocation.y += DRAG_HANDLE_SIZE;
649

650
      if (hb->child_detached)
651
	{
652 653 654
	  guint float_width;
	  guint float_height;
	  
655 656
	  child_allocation.width = child_requisition.width;
	  child_allocation.height = child_requisition.height;
657 658 659 660
	  
	  float_width = child_allocation.width + 2 * border_width;
	  float_height = child_allocation.height + 2 * border_width;
	  
661 662
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
	    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);
	    }
678 679 680
	}
      else
	{
681 682
	  child_allocation.width = MAX (1, (gint)widget->allocation.width - 2 * border_width);
	  child_allocation.height = MAX (1, (gint)widget->allocation.height - 2 * border_width);
683

684 685
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
686 687 688 689 690 691 692 693 694 695
	    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);
696 697
	}

698
      gtk_widget_size_allocate (bin->child, &child_allocation);
699
    }
700
}
701

702
static void
703
gtk_handle_box_draw_ghost (GtkHandleBox *hb)
704
{
705 706 707 708 709
  GtkWidget *widget;
  guint x;
  guint y;
  guint width;
  guint height;
710
  gint handle_position;
711

712
  widget = GTK_WIDGET (hb);
713 714 715 716
  
  handle_position = effective_handle_position (hb);
  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
717
    {
718
      x = handle_position == GTK_POS_LEFT ? 0 : widget->allocation.width - DRAG_HANDLE_SIZE;
719 720 721
      y = 0;
      width = DRAG_HANDLE_SIZE;
      height = widget->allocation.height;
722
    }
723 724 725
  else
    {
      x = 0;
726
      y = handle_position == GTK_POS_TOP ? 0 : widget->allocation.height - DRAG_HANDLE_SIZE;
727 728 729
      width = widget->allocation.width;
      height = DRAG_HANDLE_SIZE;
    }
730
  gtk_paint_shadow (widget->style,
731 732
		    widget->window,
		    GTK_WIDGET_STATE (widget),
733 734 735 736 737 738
		    GTK_SHADOW_ETCHED_IN,
		    NULL, widget, "handle",
		    x,
		    y,
		    width,
		    height);
739 740
   if (handle_position == GTK_POS_LEFT ||
       handle_position == GTK_POS_RIGHT)
741 742 743 744
     gtk_paint_hline (widget->style,
		      widget->window,
		      GTK_WIDGET_STATE (widget),
		      NULL, widget, "handlebox",
745 746
		      handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
		      handle_position == GTK_POS_LEFT ? widget->allocation.width : widget->allocation.width - DRAG_HANDLE_SIZE,
747 748 749 750 751 752
		      widget->allocation.height / 2);
   else
     gtk_paint_vline (widget->style,
		      widget->window,
		      GTK_WIDGET_STATE (widget),
		      NULL, widget, "handlebox",
753 754
		      handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
		      handle_position == GTK_POS_TOP ? widget->allocation.height : widget->allocation.height - DRAG_HANDLE_SIZE,
755
		      widget->allocation.width / 2);
756 757
}

758
static void
759
draw_textured_frame (GtkWidget *widget, GdkWindow *window, GdkRectangle *rect, GtkShadowType shadow,
760
		     GdkRectangle *clip, GtkOrientation orientation)
761
{
762 763 764
   gtk_paint_handle (widget->style, window, GTK_STATE_NORMAL, shadow,
		     clip, widget, "handlebox",
		     rect->x, rect->y, rect->width, rect->height, 
765
		     orientation);
766 767
}

768 769 770 771 772 773 774 775 776
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;
777
      g_object_notify (G_OBJECT (handle_box), "shadow-type");
778 779 780
      gtk_widget_queue_resize (GTK_WIDGET (handle_box));
    }
}
781

782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
/**
 * 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;
}

799 800 801 802
void        
gtk_handle_box_set_handle_position  (GtkHandleBox    *handle_box,
				     GtkPositionType  position)
{
803 804
  g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));

805 806 807
  if ((GtkPositionType) handle_box->handle_position != position)
    {
      handle_box->handle_position = position;
808
      g_object_notify (G_OBJECT (handle_box), "handle-position");
809 810 811 812
      gtk_widget_queue_resize (GTK_WIDGET (handle_box));
    }
}

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
/**
 * 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;
}

830 831 832 833 834 835
void        
gtk_handle_box_set_snap_edge        (GtkHandleBox    *handle_box,
				     GtkPositionType  edge)
{
  g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));

836 837 838
  if (handle_box->snap_edge != edge)
    {
      handle_box->snap_edge = edge;
839 840
      
      g_object_freeze_notify (G_OBJECT (handle_box));
841 842
      g_object_notify (G_OBJECT (handle_box), "snap-edge");
      g_object_notify (G_OBJECT (handle_box), "snap-edge-set");
843
      g_object_thaw_notify (G_OBJECT (handle_box));
844
    }
845 846
}

847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
/**
 * 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
865 866 867 868 869 870 871 872
/**
 * 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
 *
873
 * Since: 2.14
Tim Janik's avatar
Tim Janik committed
874 875 876 877 878 879 880 881 882
 **/
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;
}

883
static void
884
gtk_handle_box_paint (GtkWidget      *widget,
885 886
		  
    GdkEventExpose *event,
887
		      GdkRectangle   *area)
Elliot Lee's avatar
Elliot Lee committed
888
{
889
  GtkBin *bin;
890
  GtkHandleBox *hb;
891
  gint width, height;
892
  GdkRectangle rect;
893
  GdkRectangle dest;
894
  gint handle_position;
895
  GtkOrientation handle_orientation;
896

897 898
  bin = GTK_BIN (widget);
  hb = GTK_HANDLE_BOX (widget);
899

900 901
  handle_position = effective_handle_position (hb);

Manish Singh's avatar
Manish Singh committed
902
  gdk_drawable_get_size (hb->bin_window, &width, &height);
903
  
904
  if (!event)
905 906 907 908 909 910
    gtk_paint_box (widget->style,
		   hb->bin_window,
		   GTK_WIDGET_STATE (widget),
		   hb->shadow_type,
		   area, widget, "handlebox_bin",
		   0, 0, -1, -1);
911
  else
912 913 914 915 916 917
   gtk_paint_box (widget->style,
		  hb->bin_window,
		  GTK_WIDGET_STATE (widget),
		  hb->shadow_type,
		  &event->area, widget, "handlebox_bin",
		  0, 0, -1, -1);
918 919 920 921 922 923 924 925

/* 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);*/
926

927
  switch (handle_position)
928 929 930 931 932 933
    {
    case GTK_POS_LEFT:
      rect.x = 0;
      rect.y = 0; 
      rect.width = DRAG_HANDLE_SIZE;
      rect.height = height;
934
      handle_orientation = GTK_ORIENTATION_VERTICAL;
935 936 937 938 939 940
      break;
    case GTK_POS_RIGHT:
      rect.x = width - DRAG_HANDLE_SIZE; 
      rect.y = 0;
      rect.width = DRAG_HANDLE_SIZE;
      rect.height = height;
941
      handle_orientation = GTK_ORIENTATION_VERTICAL;
942 943 944 945 946 947
      break;
    case GTK_POS_TOP:
      rect.x = 0;
      rect.y = 0; 
      rect.width = width;
      rect.height = DRAG_HANDLE_SIZE;
948
      handle_orientation = GTK_ORIENTATION_HORIZONTAL;
949 950 951 952 953 954
      break;
    case GTK_POS_BOTTOM:
      rect.x = 0;
      rect.y = height - DRAG_HANDLE_SIZE;
      rect.width = width;
      rect.height = DRAG_HANDLE_SIZE;
955
      handle_orientation = GTK_ORIENTATION_HORIZONTAL;
956
      break;
957 958 959
    default: 
      g_assert_not_reached ();
      break;
960
    }
961

962 963 964
  if (gdk_rectangle_intersect (event ? &event->area : area, &rect, &dest))
    draw_textured_frame (widget, hb->bin_window, &rect,
			 GTK_SHADOW_OUT,
965 966
			 event ? &event->area : area,
			 handle_orientation);
967 968

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
Matthias Clasen's avatar
Matthias Clasen committed
969
    (* GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->expose_event) (widget, event);
Elliot Lee's avatar
Elliot Lee committed
970 971
}

972 973
static gint
gtk_handle_box_expose (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
974
		       GdkEventExpose *event)
975
{
976
  GtkHandleBox *hb;
977 978 979

  if (GTK_WIDGET_DRAWABLE (widget))
    {
980 981
      hb = GTK_HANDLE_BOX (widget);

982 983 984 985 986 987
      if (event->window == widget->window)
	{
	  if (hb->child_detached)
	    gtk_handle_box_draw_ghost (hb);
	}
      else
988
	gtk_handle_box_paint (widget, event, NULL);
989
    }
990
  
991 992 993
  return FALSE;
}

994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
static GtkWidget *
gtk_handle_box_get_invisible (void)
{
  static GtkWidget *handle_box_invisible = NULL;

  if (!handle_box_invisible)
    {
      handle_box_invisible = gtk_invisible_new ();
      gtk_widget_show (handle_box_invisible);
    }
  
  return handle_box_invisible;
}

static gboolean
gtk_handle_box_grab_event (GtkWidget    *widget,
			   GdkEvent     *event,
			   GtkHandleBox *hb)
{
  switch (event->type)
    {
    case GDK_BUTTON_RELEASE:
      if (hb->in_drag)		/* sanity check */
	{
	  gtk_handle_box_end_drag (hb, event->button.time);
	  return TRUE;
	}
      break;

    case GDK_MOTION_NOTIFY:
      return gtk_handle_box_motion (GTK_WIDGET (hb), (GdkEventMotion *)event);
      break;

    default:
      break;
    }

  return FALSE;
}

1034
static gint
1035 1036
gtk_handle_box_button_changed (GtkWidget      *widget,
			       GdkEventButton *event)
1037 1038
{
  GtkHandleBox *hb;
1039
  gboolean event_handled;
1040
  GdkCursor *fleur;
1041
  gint handle_position;
1042

1043 1044
  hb = GTK_HANDLE_BOX (widget);

1045 1046
  handle_position = effective_handle_position (hb);

1047
  event_handled = FALSE;
1048 1049
  if ((event->button == 1) && 
      (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS))
1050
    {
1051 1052 1053
      GtkWidget *child;
      gboolean in_handle;
      
1054 1055 1056
      if (event->window != hb->bin_window)
	return FALSE;

1057
      child = GTK_BIN (hb)->child;
1058 1059

      if (child)
1060
	{
1061
	  switch (handle_position)
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
	    {
	    case GTK_POS_LEFT:
	      in_handle = event->x < DRAG_HANDLE_SIZE;
	      break;
	    case GTK_POS_TOP:
	      in_handle = event->y < DRAG_HANDLE_SIZE;
	      break;
	    case GTK_POS_RIGHT:
	      in_handle = event->x > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.width;
	      break;
	    case GTK_POS_BOTTOM:
	      in_handle = event->y > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.height;
	      break;
	    default:
	      in_handle = FALSE;
	      break;
	    }
1079
	}
1080
      else
1081 1082 1083 1084 1085 1086
	{
	  in_handle = FALSE;
	  event_handled = TRUE;
	}
      
      if (in_handle)
1087
	{
1088 1089
	  if (event->type == GDK_BUTTON_PRESS) /* Start a drag */
	    {
1090
	      GtkHandleBoxPrivate *private = gtk_handle_box_get_private (hb);
1091
	      GtkWidget *invisible = gtk_handle_box_get_invisible ();
1092 1093 1094
	      gint desk_x, desk_y;
	      gint root_x, root_y;
	      gint width, height;
1095

1096 1097
	      gdk_window_get_deskrelative_origin (hb->bin_window, &desk_x, &desk_y);
	      gdk_window_get_origin (hb->bin_window, &root_x, &root_y);
Manish Singh's avatar
Manish Singh committed
1098
	      gdk_drawable_get_size (hb->bin_window, &width, &height);
1099
		  
1100 1101
	      private->orig_x = event->x_root;
	      private->orig_y = event->y_root;
1102
		  
1103 1104 1105 1106 1107 1108 1109 1110
	      hb->float_allocation.x = root_x - event->x_root;
	      hb->float_allocation.y = root_y - event->y_root;
	      hb->float_allocation.width = width;
	      hb->float_allocation.height = height;
	      
	      hb->deskoff_x = desk_x - root_x;
	      hb->deskoff_y = desk_y - root_y;
	      
1111 1112 1113 1114
	      if (gdk_window_is_viewable (widget->window))
		{
		  gdk_window_get_origin (widget->window, &root_x, &root_y);
		  gdk_drawable_get_size (widget->window, &width, &height);
1115
	      
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
		  hb->attach_allocation.x = root_x;
		  hb->attach_allocation.y = root_y;
		  hb->attach_allocation.width = width;
		  hb->attach_allocation.height = height;
		}
	      else
		{
		  hb->attach_allocation.x = -1;
		  hb->attach_allocation.y = -1;
		  hb->attach_allocation.width = 0;
		  hb->attach_allocation.height = 0;
		}
1128
	      hb->in_drag = TRUE;
1129 1130
	      fleur = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
						  GDK_FLEUR);
1131
	      if (gdk_pointer_grab (invisible->window,
1132 1133 1134 1135 1136 1137
				    FALSE,
				    (GDK_BUTTON1_MOTION_MASK |
				     GDK_POINTER_MOTION_HINT_MASK |
				     GDK_BUTTON_RELEASE_MASK),
				    NULL,
				    fleur,
1138
				    event->time) != 0)
1139 1140 1141
		{
		  hb->in_drag = FALSE;
		}
1142 1143 1144 1145 1146 1147
	      else
		{
		  gtk_grab_add (invisible);
		  g_signal_connect (invisible, "event",
				    G_CALLBACK (gtk_handle_box_grab_event), hb);
		}
1148
	      
Manish Singh's avatar
Manish Singh committed
1149
	      gdk_cursor_unref (fleur);
1150 1151 1152 1153 1154 1155
	      event_handled = TRUE;
	    }
	  else if (hb->child_detached) /* Double click */
	    {
	      gtk_handle_box_reattach (hb);
	    }
1156 1157
	}
    }
1158 1159
  
  return event_handled;
1160 1161
}

1162 1163 1164
static gint
gtk_handle_box_motion (GtkWidget      *widget,
		       GdkEventMotion *event)
1165
{
1166
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1167
  gint new_x, new_y;
1168 1169
  gint snap_edge;
  gboolean is_snapped = FALSE;
1170
  gint handle_position;
1171
  GdkGeometry geometry;
1172
  GdkScreen *screen, *pointer_screen;
1173

1174 1175
  if (!hb->in_drag)
    return FALSE;
1176 1177
  handle_position = effective_handle_position (hb);

1178 1179 1180
  /* Calculate the attachment point on the float, if the float
   * were detached
   */
1181 1182
  new_x = 0;
  new_y = 0;
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
  screen = gtk_widget_get_screen (widget);
  gdk_display_get_pointer (gdk_screen_get_display (screen),
			   &pointer_screen, 
			   &new_x, &new_y, NULL);
  if (pointer_screen != screen)
    {
      GtkHandleBoxPrivate *private = gtk_handle_box_get_private (hb);

      new_x = private->orig_x;
      new_y = private->orig_y;
    }
  
1195 1196 1197 1198 1199
  new_x += hb->float_allocation.x;
  new_y += hb->float_allocation.y;

  snap_edge = hb->snap_edge;
  if (snap_edge == -1)
1200 1201
    snap_edge = (handle_position == GTK_POS_LEFT ||
		 handle_position == GTK_POS_RIGHT) ?
1202 1203
      GTK_POS_TOP : GTK_POS_LEFT;

1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) 
    switch (snap_edge) 
      {
      case GTK_POS_LEFT:
	snap_edge = GTK_POS_RIGHT;
	break;
      case GTK_POS_RIGHT:
	snap_edge = GTK_POS_LEFT;
	break;
      default:
	break;
      }

1217 1218 1219
  /* First, check if the snapped edge is aligned
   */
  switch (snap_edge)
1220
    {
1221
    case GTK_POS_TOP:
1222
      is_snapped = abs (hb->attach_allocation.y - new_y) < TOLERANCE;
1223 1224
      break;
    case GTK_POS_BOTTOM:
1225 1226
      is_snapped = abs (hb->attach_allocation.y + (gint)hb->attach_allocation.height -
			new_y - (gint)hb->float_allocation.height) < TOLERANCE;
1227
      break;
1228 1229 1230 1231 1232 1233
    case GTK_POS_LEFT:
      is_snapped = abs (hb->attach_allocation.x - new_x) < TOLERANCE;
      break;
    case GTK_POS_RIGHT:
      is_snapped = abs (hb->attach_allocation.x + (gint)hb->attach_allocation.width -
			new_x - (gint)hb->float_allocation.width) < TOLERANCE;
1234
      break;
1235
    }
1236

1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
  /* Next, check if coordinates in the other direction are sufficiently
   * aligned
   */
  if (is_snapped)
    {
      gint float_pos1 = 0;	/* Initialize to suppress warnings */
      gint float_pos2 = 0;
      gint attach_pos1 = 0;
      gint attach_pos2 = 0;
      
      switch (snap_edge)
	{
	case GTK_POS_TOP:
	case GTK_POS_BOTTOM:
	  attach_pos1 = hb->attach_allocation.x;
	  attach_pos2 = hb->attach_allocation.x + hb->attach_allocation.width;
	  float_pos1 = new_x;
	  float_pos2 = new_x + hb->float_allocation.width;
	  break;
	case GTK_POS_LEFT:
	case GTK_POS_RIGHT:
	  attach_pos1 = hb->attach_allocation.y;
	  attach_pos2 = hb->attach_allocation.y + hb->attach_allocation.height;
	  float_pos1 = new_y;
	  float_pos2 = new_y + hb->float_allocation.height;
	  break;
	}

      is_snapped = ((attach_pos1 - TOLERANCE < float_pos1) && 
		    (attach_pos2 + TOLERANCE > float_pos2)) ||
	           ((float_pos1 - TOLERANCE < attach_pos1) &&
		    (float_pos2 + TOLERANCE > attach_pos2));
    }

  if (is_snapped)
1272
    {
1273 1274 1275 1276 1277 1278
      if (hb->child_detached)
	{
	  hb->child_detached = FALSE;
	  gdk_window_hide (hb->float_window);
	  gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
	  hb->float_window_mapped = FALSE;
Manish Singh's avatar
Manish Singh committed
1279 1280 1281 1282
	  g_signal_emit (hb,
			 handle_box_signals[SIGNAL_CHILD_ATTACHED],
			 0,
			 GTK_BIN (hb)->child);
1283 1284 1285
	  
	  gtk_widget_queue_resize (widget);
	}
1286
    }
1287
  else
1288
    {
1289 1290
      gint width, height;

Manish Singh's avatar
Manish Singh committed
1291
      gdk_drawable_get_size (hb->float_window, &width, &height);
1292 1293 1294
      new_x += hb->deskoff_x;
      new_y += hb->deskoff_y;

1295
      switch (handle_position)
1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312
	{
	case GTK_POS_LEFT:
	  new_y += ((gint)hb->float_allocation.height - height) / 2;
	  break;
	case GTK_POS_RIGHT:
	  new_x += (gint)hb->float_allocation.width - width;
	  new_y += ((gint)hb->float_allocation.height - height) / 2;
	  break;
	case GTK_POS_TOP:
	  new_x += ((gint)hb->float_allocation.width - width) / 2;
	  break;
	case GTK_POS_BOTTOM:
	  new_x += ((gint)hb->float_allocation.width - width) / 2;
	  new_y += (gint)hb->float_allocation.height - height;
	  break;
	}

1313
      if (hb->child_detached)
1314
	{
1315 1316
	  gdk_window_move (hb->float_window, new_x, new_y);
	  gdk_window_raise (hb->float_window);
1317 1318 1319
	}
      else
	{
1320 1321
	  gint width;
	  gint height;
1322
	  GtkRequisition child_requisition;
1323

1324
	  hb->child_detached = TRUE;
1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335

	  if (GTK_BIN (hb)->child)
	    gtk_widget_get_child_requisition (GTK_BIN (hb)->child, &child_requisition);
	  else
	    {
	      child_requisition.width = 0;
	      child_requisition.height = 0;
	    }      

	  width = child_requisition.width + 2 * GTK_CONTAINER (hb)->border_width;
	  height = child_requisition.height + 2 * GTK_CONTAINER (hb)->border_width;
1336

1337
	  if (handle_position == GTK_POS_LEFT || handle_position == GTK_POS_RIGHT)