gtkhandlebox.c 40.5 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>
Matthias Clasen's avatar
Matthias Clasen committed
30
#include "gtkalias.h"
31
#include "gtkhandlebox.h"
32
#include "gtkinvisible.h"
33
#include "gtkmain.h"
34
#include "gtkmarshalers.h"
35
#include "gtkwindow.h"
36
#include "gtkintl.h"
37

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

struct _GtkHandleBoxPrivate
{
  gint orig_x;
  gint orig_y;
};

46
enum {
47 48
  PROP_0,
  PROP_SHADOW,
49
  PROP_SHADOW_TYPE,
50
  PROP_HANDLE_POSITION,
51 52
  PROP_SNAP_EDGE,
  PROP_SNAP_EDGE_SET
53
};
54

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

60
enum {
61 62 63 64 65
  SIGNAL_CHILD_ATTACHED,
  SIGNAL_CHILD_DETACHED,
  SIGNAL_LAST
};

66 67 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
/* 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-------------------->
 */

106 107
static void gtk_handle_box_class_init     (GtkHandleBoxClass *klass);
static void gtk_handle_box_init           (GtkHandleBox      *handle_box);
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 148


static GtkBinClass *parent_class;
149
static guint        handle_box_signals[SIGNAL_LAST] = { 0 };
150 151


Manish Singh's avatar
Manish Singh committed
152
GType
153
gtk_handle_box_get_type (void)
154
{
Manish Singh's avatar
Manish Singh committed
155
  static GType handle_box_type = 0;
156

Elliot Lee's avatar
Elliot Lee committed
157
  if (!handle_box_type)
158
    {
Manish Singh's avatar
Manish Singh committed
159
      static const GTypeInfo handle_box_info =
160 161
      {
	sizeof (GtkHandleBoxClass),
Manish Singh's avatar
Manish Singh committed
162 163 164 165 166 167 168 169
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_handle_box_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkHandleBox),
	0,		/* n_preallocs */
	(GInstanceInitFunc) gtk_handle_box_init,
170 171
      };

Manish Singh's avatar
Manish Singh committed
172 173
      handle_box_type = g_type_register_static (GTK_TYPE_BIN, "GtkHandleBox",
						&handle_box_info, 0);
174 175
    }

176
  return handle_box_type;
177 178 179 180 181
}

static void
gtk_handle_box_class_init (GtkHandleBoxClass *class)
{
182
  GObjectClass *gobject_class;
183
  GtkObjectClass *object_class;
184 185
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;
186

187
  gobject_class = (GObjectClass *) class;
188 189
  object_class = (GtkObjectClass *) class;
  widget_class = (GtkWidgetClass *) class;
190
  container_class = (GtkContainerClass *) class;
191

Manish Singh's avatar
Manish Singh committed
192
  parent_class = g_type_class_peek_parent (class);
193

194 195 196 197 198
  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,
199
                                   g_param_spec_enum ("shadow", NULL,
200
                                                      P_("Deprecated property, use shadow_type instead"),
201 202 203 204 205 206
						      GTK_TYPE_SHADOW_TYPE,
						      GTK_SHADOW_ETCHED_OUT,
                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                                   PROP_SHADOW_TYPE,
                                   g_param_spec_enum ("shadow_type",
207 208
                                                      P_("Shadow type"),
                                                      P_("Appearance of the shadow that surrounds the container"),
209 210 211
						      GTK_TYPE_SHADOW_TYPE,
						      GTK_SHADOW_ETCHED_OUT,
                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
212
  
213 214 215
  g_object_class_install_property (gobject_class,
                                   PROP_HANDLE_POSITION,
                                   g_param_spec_enum ("handle_position",
216 217
                                                      P_("Handle position"),
                                                      P_("Position of the handle relative to the child widget"),
218 219 220
						      GTK_TYPE_POSITION_TYPE,
						      GTK_POS_LEFT,
                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
221
  
222 223 224
  g_object_class_install_property (gobject_class,
                                   PROP_SNAP_EDGE,
                                   g_param_spec_enum ("snap_edge",
225 226
                                                      P_("Snap edge"),
                                                      P_("Side of the handlebox that's lined up with the docking point to dock the handlebox"),
227
						      GTK_TYPE_POSITION_TYPE,
228
						      GTK_POS_TOP,
229
                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
Manish Singh's avatar
Manish Singh committed
230

231 232 233
  g_object_class_install_property (gobject_class,
                                   PROP_SNAP_EDGE_SET,
                                   g_param_spec_boolean ("snap_edge_set",
234 235
							 P_("Snap edge set"),
							 P_("Whether to use the value from the snap_edge property or a value derived from handle_position"),
236 237 238
							 FALSE,
							 G_PARAM_READABLE | G_PARAM_WRITABLE));

239 240 241 242
  object_class->destroy = gtk_handle_box_destroy;

  widget_class->map = gtk_handle_box_map;
  widget_class->unmap = gtk_handle_box_unmap;
243
  widget_class->realize = gtk_handle_box_realize;
244
  widget_class->unrealize = gtk_handle_box_unrealize;
Owen Taylor's avatar
Owen Taylor committed
245
  widget_class->style_set = gtk_handle_box_style_set;
246 247 248
  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
249
  widget_class->button_press_event = gtk_handle_box_button_changed;
250
  widget_class->delete_event = gtk_handle_box_delete_event;
251

252
  container_class->add = gtk_handle_box_add;
253 254
  container_class->remove = gtk_handle_box_remove;

255 256
  class->child_attached = NULL;
  class->child_detached = NULL;
257 258

  handle_box_signals[SIGNAL_CHILD_ATTACHED] =
Manish Singh's avatar
Manish Singh committed
259 260 261 262 263 264 265 266
    g_signal_new ("child_attached",
		  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);
267
  handle_box_signals[SIGNAL_CHILD_DETACHED] =
Manish Singh's avatar
Manish Singh committed
268 269 270 271 272 273 274 275
    g_signal_new ("child_detached",
		  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);
276 277
}

278
static GtkHandleBoxPrivate *
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
gtk_handle_box_get_private (GtkHandleBox *hb)
{
  GtkHandleBoxPrivate *private;
  static GQuark private_quark = 0;

  if (!private_quark)
    private_quark = g_quark_from_static_string ("gtk-handle-box-private");

  private = g_object_get_qdata (G_OBJECT (hb), private_quark);

  if (!private)
    {
      private = g_new0 (GtkHandleBoxPrivate, 1);
      g_object_set_qdata_full (G_OBJECT (hb), private_quark,
			       private, g_free);
    }

  return private;
}

299
static void
300
gtk_handle_box_init (GtkHandleBox *handle_box)
301
{
302
  GTK_WIDGET_UNSET_FLAGS (handle_box, GTK_NO_WINDOW);
303

304
  handle_box->bin_window = NULL;
305
  handle_box->float_window = NULL;
306
  handle_box->shadow_type = GTK_SHADOW_OUT;
307 308 309 310
  handle_box->handle_position = GTK_POS_LEFT;
  handle_box->float_window_mapped = FALSE;
  handle_box->child_detached = FALSE;
  handle_box->in_drag = FALSE;
311
  handle_box->shrink_on_detach = TRUE;
312
  handle_box->snap_edge = -1;
313 314
}

315 316 317 318 319
static void 
gtk_handle_box_set_property (GObject         *object,
			     guint            prop_id,
			     const GValue    *value,
			     GParamSpec      *pspec)
320
{
321
  GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
322

323
  switch (prop_id)
324
    {
325
    case PROP_SHADOW:
326
    case PROP_SHADOW_TYPE:
327 328 329 330
      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));
331
      break;
332 333
    case PROP_SNAP_EDGE:
      gtk_handle_box_set_snap_edge (handle_box, g_value_get_enum (value));
334
      break;
335 336 337 338
    case PROP_SNAP_EDGE_SET:
      if (!g_value_get_boolean (value))
	gtk_handle_box_set_snap_edge (handle_box, (GtkPositionType)-1);
      break;
339 340
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
341 342 343 344
      break;
    }
}

345 346 347 348 349
static void 
gtk_handle_box_get_property (GObject         *object,
			     guint            prop_id,
			     GValue          *value,
			     GParamSpec      *pspec)
350
{
351 352
  GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
  
353
  switch (prop_id)
354
    {
355
    case PROP_SHADOW:
356
    case PROP_SHADOW_TYPE:
357
      g_value_set_enum (value, handle_box->shadow_type);
358
      break;
359 360
    case PROP_HANDLE_POSITION:
      g_value_set_enum (value, handle_box->handle_position);
361
      break;
362
    case PROP_SNAP_EDGE:
363 364 365 366 367 368
      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);
369
      break;
370
    default:
371
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372 373 374 375
      break;
    }
}
 
376
GtkWidget*
377 378
gtk_handle_box_new (void)
{
Manish Singh's avatar
Manish Singh committed
379
  return g_object_new (GTK_TYPE_HANDLE_BOX, NULL);
380 381 382 383
}

static void
gtk_handle_box_destroy (GtkObject *object)
384
{
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}

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

400 401 402 403
  if (bin->child &&
      GTK_WIDGET_VISIBLE (bin->child) &&
      !GTK_WIDGET_MAPPED (bin->child))
    gtk_widget_map (bin->child);
404

405 406 407 408 409 410
  if (hb->child_detached && !hb->float_window_mapped)
    {
      gdk_window_show (hb->float_window);
      hb->float_window_mapped = TRUE;
    }

411 412
  gdk_window_show (hb->bin_window);
  gdk_window_show (widget->window);
413 414 415 416 417 418 419 420 421 422 423 424
}

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);
425 426 427 428 429
  if (hb->float_window_mapped)
    {
      gdk_window_hide (hb->float_window);
      hb->float_window_mapped = FALSE;
    }
430 431 432 433 434 435 436
}

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

439 440
  hb = GTK_HANDLE_BOX (widget);

441 442 443 444 445 446 447 448 449 450
  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);
451 452
  attributes.event_mask = (gtk_widget_get_events (widget)
			   | GDK_EXPOSURE_MASK);
453
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
454 455
  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
  gdk_window_set_user_data (widget->window, widget);
456 457 458

  attributes.x = 0;
  attributes.y = 0;
459 460 461
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.window_type = GDK_WINDOW_CHILD;
Soeren Sandmann's avatar
Soeren Sandmann committed
462 463 464 465 466
  attributes.event_mask = (gtk_widget_get_events (widget) |
			   GDK_EXPOSURE_MASK |
			   GDK_BUTTON1_MOTION_MASK |
			   GDK_POINTER_MOTION_HINT_MASK |
			   GDK_BUTTON_PRESS_MASK |
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
			    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);
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
489 490
  hb->float_window = gdk_window_new (gtk_widget_get_root_window (widget),
				     &attributes, attributes_mask);
491 492
  gdk_window_set_user_data (hb->float_window, widget);
  gdk_window_set_decorations (hb->float_window, 0);
493
  gdk_window_set_type_hint (hb->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
494
  
495
  widget->style = gtk_style_attach (widget->style, widget->window);
496 497 498
  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));
499
  gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
500 501
}

502 503 504
static void
gtk_handle_box_unrealize (GtkWidget *widget)
{
505
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
506

507 508 509 510 511
  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);
512
  hb->float_window = NULL;
513 514 515

  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
516 517
}

Owen Taylor's avatar
Owen Taylor committed
518 519 520 521
static void
gtk_handle_box_style_set (GtkWidget *widget,
			  GtkStyle  *previous_style)
{
522
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
Owen Taylor's avatar
Owen Taylor committed
523 524 525 526 527

  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
528
				widget->state);
Owen Taylor's avatar
Owen Taylor committed
529 530 531 532 533
      gtk_style_set_background (widget->style, hb->bin_window, widget->state);
      gtk_style_set_background (widget->style, hb->float_window, widget->state);
    }
}

534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
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;
}

560 561
static void
gtk_handle_box_size_request (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
562
			     GtkRequisition *requisition)
563 564
{
  GtkBin *bin;
565
  GtkHandleBox *hb;
566
  GtkRequisition child_requisition;
567
  gint handle_position;
568 569

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

572 573 574 575
  handle_position = effective_handle_position (hb);

  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
576 577 578 579 580
    {
      requisition->width = DRAG_HANDLE_SIZE;
      requisition->height = 0;
    }
  else
581
    {
582 583 584
      requisition->width = 0;
      requisition->height = DRAG_HANDLE_SIZE;
    }
585

586 587 588 589
  /* if our child is not visible, we still request its size, since we
   * won't have any usefull hint for our size otherwise.
   */
  if (bin->child)
590
    gtk_widget_size_request (bin->child, &child_requisition);
591 592 593 594 595
  else
    {
      child_requisition.width = 0;
      child_requisition.height = 0;
    }      
596 597 598

  if (hb->child_detached)
    {
599
      /* FIXME: This doesn't work currently */
600 601
      if (!hb->shrink_on_detach)
	{
602 603
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
604
	    requisition->height += child_requisition.height;
605
	  else
606
	    requisition->width += child_requisition.width;
607
	}
608
      else
609
	{
610 611
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
612
	    requisition->height += widget->style->ythickness;
613
	  else
614
	    requisition->width += widget->style->xthickness;
615
	}
616
    }
617 618
  else
    {
619 620 621 622 623
      requisition->width += GTK_CONTAINER (widget)->border_width * 2;
      requisition->height += GTK_CONTAINER (widget)->border_width * 2;
      
      if (bin->child)
	{
624 625
	  requisition->width += child_requisition.width;
	  requisition->height += child_requisition.height;
626 627 628 629 630 631
	}
      else
	{
	  requisition->width += CHILDLESS_SIZE;
	  requisition->height += CHILDLESS_SIZE;
	}
632
    }
633 634 635 636
}

static void
gtk_handle_box_size_allocate (GtkWidget     *widget,
637
			      GtkAllocation *allocation)
638 639
{
  GtkBin *bin;
640
  GtkHandleBox *hb;
641
  GtkRequisition child_requisition;
642
  gint handle_position;
643
  
644
  bin = GTK_BIN (widget);
645
  hb = GTK_HANDLE_BOX (widget);
646
  
647 648
  handle_position = effective_handle_position (hb);

649 650 651 652 653 654
  if (bin->child)
    gtk_widget_get_child_requisition (bin->child, &child_requisition);
  else
    {
      child_requisition.width = 0;
      child_requisition.height = 0;
655
    }      
656
      
657
  widget->allocation = *allocation;
658

659 660 661 662 663 664
  if (GTK_WIDGET_REALIZED (hb))
    gdk_window_move_resize (widget->window,
			    widget->allocation.x,
			    widget->allocation.y,
			    widget->allocation.width,
			    widget->allocation.height);
665

666 667 668

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
    {
669
      GtkWidget *child;
670
      GtkAllocation child_allocation;
671 672 673 674
      guint border_width;

      child = bin->child;
      border_width = GTK_CONTAINER (widget)->border_width;
675

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

683
      if (hb->child_detached)
684
	{
685 686 687
	  guint float_width;
	  guint float_height;
	  
688 689
	  child_allocation.width = child_requisition.width;
	  child_allocation.height = child_requisition.height;
690 691 692 693
	  
	  float_width = child_allocation.width + 2 * border_width;
	  float_height = child_allocation.height + 2 * border_width;
	  
694 695
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
	    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);
	    }
711 712 713
	}
      else
	{
714 715
	  child_allocation.width = MAX (1, (gint)widget->allocation.width - 2 * border_width);
	  child_allocation.height = MAX (1, (gint)widget->allocation.height - 2 * border_width);
716

717 718
	  if (handle_position == GTK_POS_LEFT ||
	      handle_position == GTK_POS_RIGHT)
719 720 721 722 723 724 725 726 727 728
	    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);
729 730
	}

731
      gtk_widget_size_allocate (bin->child, &child_allocation);
732
    }
733
}
734

735
static void
736
gtk_handle_box_draw_ghost (GtkHandleBox *hb)
737
{
738 739 740 741 742
  GtkWidget *widget;
  guint x;
  guint y;
  guint width;
  guint height;
743
  gint handle_position;
744

745
  widget = GTK_WIDGET (hb);
746 747 748 749
  
  handle_position = effective_handle_position (hb);
  if (handle_position == GTK_POS_LEFT ||
      handle_position == GTK_POS_RIGHT)
750
    {
751
      x = handle_position == GTK_POS_LEFT ? 0 : widget->allocation.width - DRAG_HANDLE_SIZE;
752 753 754
      y = 0;
      width = DRAG_HANDLE_SIZE;
      height = widget->allocation.height;
755
    }
756 757 758
  else
    {
      x = 0;
759
      y = handle_position == GTK_POS_TOP ? 0 : widget->allocation.height - DRAG_HANDLE_SIZE;
760 761 762
      width = widget->allocation.width;
      height = DRAG_HANDLE_SIZE;
    }
763
  gtk_paint_shadow (widget->style,
764 765
		    widget->window,
		    GTK_WIDGET_STATE (widget),
766 767 768 769 770 771
		    GTK_SHADOW_ETCHED_IN,
		    NULL, widget, "handle",
		    x,
		    y,
		    width,
		    height);
772 773
   if (handle_position == GTK_POS_LEFT ||
       handle_position == GTK_POS_RIGHT)
774 775 776 777
     gtk_paint_hline (widget->style,
		      widget->window,
		      GTK_WIDGET_STATE (widget),
		      NULL, widget, "handlebox",
778 779
		      handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
		      handle_position == GTK_POS_LEFT ? widget->allocation.width : widget->allocation.width - DRAG_HANDLE_SIZE,
780 781 782 783 784 785
		      widget->allocation.height / 2);
   else
     gtk_paint_vline (widget->style,
		      widget->window,
		      GTK_WIDGET_STATE (widget),
		      NULL, widget, "handlebox",
786 787
		      handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
		      handle_position == GTK_POS_TOP ? widget->allocation.height : widget->allocation.height - DRAG_HANDLE_SIZE,
788
		      widget->allocation.width / 2);
789 790
}

791
static void
792 793
draw_textured_frame (GtkWidget *widget, GdkWindow *window, GdkRectangle *rect, GtkShadowType shadow,
		     GdkRectangle *clip)
794
{
795 796 797 798
   gtk_paint_handle (widget->style, window, GTK_STATE_NORMAL, shadow,
		     clip, widget, "handlebox",
		     rect->x, rect->y, rect->width, rect->height, 
		     GTK_ORIENTATION_VERTICAL);
799 800
}

801 802 803 804 805 806 807 808 809
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;
810
      g_object_notify (G_OBJECT (handle_box), "shadow_type");
811 812 813
      gtk_widget_queue_resize (GTK_WIDGET (handle_box));
    }
}
814

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

832 833 834 835
void        
gtk_handle_box_set_handle_position  (GtkHandleBox    *handle_box,
				     GtkPositionType  position)
{
836 837
  g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));

838 839 840
  if ((GtkPositionType) handle_box->handle_position != position)
    {
      handle_box->handle_position = position;
841
      g_object_notify (G_OBJECT (handle_box), "handle_position");
842 843 844 845
      gtk_widget_queue_resize (GTK_WIDGET (handle_box));
    }
}

846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
/**
 * 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;
}

863 864 865 866 867 868
void        
gtk_handle_box_set_snap_edge        (GtkHandleBox    *handle_box,
				     GtkPositionType  edge)
{
  g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));

869 870 871
  if (handle_box->snap_edge != edge)
    {
      handle_box->snap_edge = edge;
872 873
      
      g_object_freeze_notify (G_OBJECT (handle_box));
874
      g_object_notify (G_OBJECT (handle_box), "snap_edge");
875 876
      g_object_notify (G_OBJECT (handle_box), "snap_edge_set");
      g_object_thaw_notify (G_OBJECT (handle_box));
877
    }
878 879
}

880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
/**
 * 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;
}

898
static void
899
gtk_handle_box_paint (GtkWidget      *widget,
900 901
		  
    GdkEventExpose *event,
902
		      GdkRectangle   *area)
Elliot Lee's avatar
Elliot Lee committed
903
{
904
  GtkBin *bin;
905
  GtkHandleBox *hb;
906 907
  guint width;
  guint height;
908
  GdkRectangle rect;
909
  GdkRectangle dest;
910
  gint handle_position;
911

912 913
  bin = GTK_BIN (widget);
  hb = GTK_HANDLE_BOX (widget);
914

915 916
  handle_position = effective_handle_position (hb);

Manish Singh's avatar
Manish Singh committed
917
  gdk_drawable_get_size (hb->bin_window, &width, &height);
918
  
919
  if (!event)
920 921 922 923 924 925
    gtk_paint_box (widget->style,
		   hb->bin_window,
		   GTK_WIDGET_STATE (widget),
		   hb->shadow_type,
		   area, widget, "handlebox_bin",
		   0, 0, -1, -1);
926
  else
927 928 929 930 931 932
   gtk_paint_box (widget->style,
		  hb->bin_window,
		  GTK_WIDGET_STATE (widget),
		  hb->shadow_type,
		  &event->area, widget, "handlebox_bin",
		  0, 0, -1, -1);
933 934 935 936 937 938 939 940

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

942
  switch (handle_position)
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
    {
    case GTK_POS_LEFT:
      rect.x = 0;
      rect.y = 0; 
      rect.width = DRAG_HANDLE_SIZE;
      rect.height = height;
      break;
    case GTK_POS_RIGHT:
      rect.x = width - DRAG_HANDLE_SIZE; 
      rect.y = 0;
      rect.width = DRAG_HANDLE_SIZE;
      rect.height = height;
      break;
    case GTK_POS_TOP:
      rect.x = 0;
      rect.y = 0; 
      rect.width = width;
      rect.height = DRAG_HANDLE_SIZE;
      break;
    case GTK_POS_BOTTOM:
      rect.x = 0;
      rect.y = height - DRAG_HANDLE_SIZE;
      rect.width = width;
      rect.height = DRAG_HANDLE_SIZE;
      break;
    }
969

970 971 972 973
  if (gdk_rectangle_intersect (event ? &event->area : area, &rect, &dest))
    draw_textured_frame (widget, hb->bin_window, &rect,
			 GTK_SHADOW_OUT,
			 event ? &event->area : area);
974 975

  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
976
    (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
Elliot Lee's avatar
Elliot Lee committed
977 978
}

979 980
static gint
gtk_handle_box_expose (GtkWidget      *widget,
Elliot Lee's avatar
Elliot Lee committed
981
		       GdkEventExpose *event)
982
{
983
  GtkHandleBox *hb;
984 985 986

  if (GTK_WIDGET_DRAWABLE (widget))
    {
987 988
      hb = GTK_HANDLE_BOX (widget);

989 990 991 992 993 994
      if (event->window == widget->window)
	{
	  if (hb->child_detached)
	    gtk_handle_box_draw_ghost (hb);
	}
      else
995
	gtk_handle_box_paint (widget, event, NULL);
996
    }
997
  
998 999 1000
  return FALSE;
}

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 1034 1035 1036 1037 1038 1039 1040
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;
}

1041
static gint
1042 1043
gtk_handle_box_button_changed (GtkWidget      *widget,
			       GdkEventButton *event)
1044 1045
{
  GtkHandleBox *hb;
1046
  gboolean event_handled;
1047
  GdkCursor *fleur;
1048
  gint handle_position;
1049

1050 1051
  hb = GTK_HANDLE_BOX (widget);

1052 1053
  handle_position = effective_handle_position (hb);

1054
  event_handled = FALSE;
1055 1056
  if ((event->button == 1) && 
      (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS))
1057
    {
1058 1059 1060
      GtkWidget *child;
      gboolean in_handle;
      
1061 1062 1063
      if (event->window != hb->bin_window)
	return FALSE;

1064
      child = GTK_BIN (hb)->child;
1065 1066

      if (child)
1067
	{
1068
	  switch (handle_position)
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
	    {
	    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;
	    }
1086
	}
1087
      else
1088 1089 1090 1091 1092 1093
	{
	  in_handle = FALSE;
	  event_handled = TRUE;
	}
      
      if (in_handle)
1094
	{
1095 1096
	  if (event->type == GDK_BUTTON_PRESS) /* Start a drag */
	    {
1097
	      GtkHandleBoxPrivate *private = gtk_handle_box_get_private (hb);
1098
	      GtkWidget *invisible = gtk_handle_box_get_invisible ();
1099 1100 1101
	      gint desk_x, desk_y;
	      gint root_x, root_y;
	      gint width, height;
1102

1103 1104
	      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
1105
	      gdk_drawable_get_size (hb->bin_window, &width, &height);
1106
		  
1107 1108
	      private->orig_x = event->x_root;
	      private->orig_y = event->y_root;
1109
		  
1110 1111 1112 1113 1114 1115 1116 1117
	      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;
	      
1118 1119 1120 1121
	      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);
1122
	      
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134
		  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;
		}
1135
	      hb->in_drag = TRUE;
1136 1137
	      fleur = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
						  GDK_FLEUR);
1138
	      if (gdk_pointer_grab (invisible->window,
1139 1140 1141 1142 1143 1144
				    FALSE,
				    (GDK_BUTTON1_MOTION_MASK |
				     GDK_POINTER_MOTION_HINT_MASK |
				     GDK_BUTTON_RELEASE_MASK),
				    NULL,
				    fleur,
1145
				    event->time) != 0)
1146 1147 1148
		{
		  hb->in_drag = FALSE;
		}
1149 1150 1151 1152 1153 1154
	      else
		{
		  gtk_grab_add (invisible);
		  g_signal_connect (invisible, "event",
				    G_CALLBACK (gtk_handle_box_grab_event), hb);
		}
1155
	      
Manish Singh's avatar
Manish Singh committed
1156
	      gdk_cursor_unref (fleur);
1157 1158 1159 1160 1161 1162
	      event_handled = TRUE;
	    }
	  else if (hb->child_detached) /* Double click */
	    {
	      gtk_handle_box_reattach (hb);
	    }
1163 1164
	}
    }
1165 1166
  
  return event_handled;
1167 1168
}

1169 1170 1171
static gint
gtk_handle_box_motion (GtkWidget      *widget,
		       GdkEventMotion *event)
1172
{
1173
  GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1174
  gint new_x, new_y;
1175 1176
  gint snap_edge;
  gboolean is_snapped = FALSE;
1177
  gint handle_position;
1178
  GdkGeometry geometry;
1179
  GdkScreen *screen, *pointer_screen;
1180

1181 1182
  if (!hb->in_drag)
    return FALSE;
1183 1184
  handle_position = effective_handle_position (hb);

1185 1186 1187
  /* Calculate the attachment point on the float, if the float
   * were detached
   */
1188 1189
  new_x = 0;
  new_y = 0;
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
  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;
    }
  
1202 1203 1204 1205 1206
  new_x += hb->float_allocation.x;
  new_y += hb->float_allocation.y;

  snap_edge = hb->snap_edge;
  if (snap_edge == -1)
1207 1208
    snap_edge = (handle_position == GTK_POS_LEFT ||
		 handle_position == GTK_POS_RIGHT) ?
1209 1210
      GTK_POS_TOP : GTK_POS_LEFT;

1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
  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;
      }

1224 1225 1226
  /* First, check if the snapped edge is aligned
   */
  switch (snap_edge)
1227
    {
1228
    case GTK_POS_TOP:
1229
      is_snapped = abs (hb->attach_allocation.y - new_y) < TOLERANCE;
1230 1231
      break;
    case GTK_POS_BOTTOM:
1232 1233
      is_snapped = abs (hb->attach_allocation.y + (gint)hb->attach_allocation.height -
			new_y - (gint)hb->float_allocation.height) < TOLERANCE;
1234
      break;
1235 1236 1237 1238 1239 1240
    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;
1241
      break;
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 1272 1273 1274 1275 1276 1277 1278
  /* 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)
1279
    {
1280 1281 1282 1283 1284 1285
      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
1286 1287 1288 1289
	  g_signal_emit (hb,
			 handle_box_signals[SIGNAL_CHILD_ATTACHED],
			 0,
			 GTK_BIN (hb)->child);
1290 1291 1292
	  
	  gtk_widget_queue_resize (widget);
	}
1293
    }
1294
  else
1295
    {
1296 1297
      gint width, height;

Manish Singh's avatar
Manish Singh committed
1298
      gdk_drawable_get_size (hb->float_window, &width, &height);
1299 1300 1301
      new_x += hb->deskoff_x;
      new_y += hb->deskoff_y;

1302
      switch (handle_position)
1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319
	{
	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;
	}

1320
      if (hb->child_detached)
1321
	{
1322 1323
	  gdk_window_move (hb->float_window, new_x, new_y);
	  gdk_window_raise (hb->float_window);
1324 1325 1326
	}
      else
	{
1327 1328
	  gint width;
	  gint height;
1329
	  GtkRequisition child_requisition;
1330

1331
	  hb->child_detached = TRUE;
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342

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

1344
	  if (handle_position == GTK_POS_LEFT || handle_position == GTK_POS_RIGHT)
1345 1346 1347 1348
	    width += DRAG_HANDLE_SIZE;
	  else
	    height += DRAG_HANDLE_SIZE;
	  
1349 1350
	  gdk_window_move_resize (hb->float_window, new_x, new_y, width, height);
	  gdk_window_reparent (hb->bin_window, hb->float_window, 0, 0);
1351
	  gdk_window_set_geometry_hints (hb->float_window, &geometry, GDK_HINT_POS);
1352 1353 1354
	  gdk_window_show (hb->float_window);
	  hb->float_window_mapped = TRUE;
#if	0
Kristian Rietveld's avatar
Kristian Rietveld committed
1355
	  /* this extra move is necessary if we use decorations, or our
1356 1357
	   * window manager insists on decorations.
	   */
1358
	  gdk_display_sync (gtk_widget_get_display (widget));
1359
	  gdk_window_move (hb->float_window, new_x, new_y);
1360
	  gdk_display_sync (gtk_widget_get_display (widget));
1361
#endif	/* 0 */
Manish Singh's avatar
Manish Singh committed
1362 1363 1364 1365
	  g_signal_emit (hb,
			 handle_box_signals[SIGNAL_CHILD_DETACHED],
			 0,
			 GTK_BIN (hb)->child);
1366 1367 1368 1369 1370
	  gtk_handle_box_draw_ghost (hb);
	  
	  gtk_widget_queue_resize (widget);
	}
    }
1371

1372 1373
  return TRUE;
}
1374

1375 1376 1377 1378 1379