glade-design-layout.c 78.1 KB
Newer Older
1 2 3
/*
 * glade-design-layout.c
 *
4
 * Copyright (C) 2006-2007 Vincent Geddes
Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
5
 *               2011-2013 Juan Pablo Ugarte
6 7
 *
 * Authors:
8
 *   Vincent Geddes <vgeddes@gnome.org>
9
 *   Juan Pablo Ugarte <juanpablougarte@gmail.com>
10
 *
11 12 13 14
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
15
 *
16 17 18 19
 * 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
 * Lesser General Public License for more details.
20
 *
21 22
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this program; if not, write to the Free Software
23 24 25 26
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

Christian Persch's avatar
Christian Persch committed
27 28
#include "config.h"

29
#include "glade.h"
30
#include "glade-design-layout.h"
31
#include "glade-design-private.h"
32 33
#include "glade-accumulators.h"
#include "glade-marshallers.h"
34

35
#include <glib/gi18n-lib.h>
36 37
#include <gtk/gtk.h>

38
#define GLADE_DESIGN_LAYOUT_PRIVATE(object) (((GladeDesignLayout*)object)->priv)
39

40
#define OUTLINE_WIDTH     4
41
#define PADDING           12
42

43
#define MARGIN_STEP       6
44

45
typedef enum
46
{
47 48 49
  ACTIVITY_NONE,
  ACTIVITY_RESIZE_WIDTH,
  ACTIVITY_RESIZE_HEIGHT,
50
  ACTIVITY_RESIZE_WIDTH_AND_HEIGHT,
51
  ACTIVITY_ALIGNMENTS,
52 53 54 55 56 57 58 59
  ACTIVITY_MARGINS,
  ACTIVITY_MARGINS_VERTICAL, /* These activities are only used to set the cursor */
  ACTIVITY_MARGINS_HORIZONTAL,
  ACTIVITY_MARGINS_TOP_LEFT,
  ACTIVITY_MARGINS_TOP_RIGHT,
  ACTIVITY_MARGINS_BOTTOM_LEFT,
  ACTIVITY_MARGINS_BOTTOM_RIGHT,
  N_ACTIVITY
60
} Activity;
61

62 63 64 65 66 67 68 69 70

typedef enum
{
  MARGIN_TOP    = 1 << 0,
  MARGIN_BOTTOM = 1 << 1,
  MARGIN_LEFT   = 1 << 2,
  MARGIN_RIGHT  = 1 << 3
} Margins;

71 72
struct _GladeDesignLayoutPrivate
{
73
  GladeWidget *gchild;
74
  GdkWindow *window, *offscreen_window;
75

76 77
  gint child_offset;
  GdkRectangle east, south, south_east;
78
  GdkCursor *cursors[N_ACTIVITY];
79

80
  GdkRectangle child_rect;
81
  PangoLayout *widget_name;
82
  gint layout_width;
83

84 85
  GtkStyleContext *default_context;

86
  /* Colors */
87
  GdkRGBA fg_color;
88 89 90
  GdkRGBA frame_color[2];
  GdkRGBA frame_color_active[2];

91 92
  /* Margin edit mode */
  GtkWidget *selection;
93
  gint top, bottom, left, right;
94
  gint m_dy, m_dx;
95
  gint max_width, max_height;
96
  Margins margin;
97
  GtkAlign valign, halign;
98
  Margins node_over;
99

100 101 102 103 104 105
  /* state machine */
  Activity activity;            /* the current activity */
  gint dx;                      /* child.width - event.pointer.x   */
  gint dy;                      /* child.height - event.pointer.y  */
  gint new_width;               /* user's new requested width */
  gint new_height;              /* user's new requested height */
106

107 108 109
  /* Drag & Drop */
  GtkWidget *drag_source;
  gint drag_x, drag_y;
Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
110
  GladeWidget *drag_dest;
111

112 113
  /* Properties */
  GladeDesignView *view;
114
  GladeProject *project;
115 116 117 118 119 120
};

enum
{
  PROP_0,
  PROP_DESIGN_VIEW
121 122
};

123
G_DEFINE_TYPE_WITH_PRIVATE (GladeDesignLayout, glade_design_layout, GTK_TYPE_BIN)
124

125
#define RECTANGLE_POINT_IN(rect,x,y) (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y && y <= (rect.y + rect.height))
126

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
static inline gint
get_margin_left (GtkWidget *widget)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  return gtk_widget_get_margin_left (widget);
G_GNUC_END_IGNORE_DEPRECATIONS
}

static inline gint
get_margin_right (GtkWidget *widget)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  return gtk_widget_get_margin_right (widget);
G_GNUC_END_IGNORE_DEPRECATIONS
}

static inline gint
get_margin_top (GtkWidget *widget)
{
  return gtk_widget_get_margin_top (widget);
}

static inline gint
get_margin_bottom (GtkWidget *widget)
{
  return gtk_widget_get_margin_bottom (widget);
}

static inline void
get_margins (GtkWidget *widget, gint *l, gint *r, gint *t, gint *b)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  *l = gtk_widget_get_margin_left (widget);
  *r = gtk_widget_get_margin_right (widget);
G_GNUC_END_IGNORE_DEPRECATIONS
  *t = gtk_widget_get_margin_top (widget);
  *b = gtk_widget_get_margin_bottom (widget);
}

166
static Margins
167
gdl_get_margins_from_pointer (GladeDesignLayout *layout, GtkWidget *widget, gint x, gint y)
168
{
169
  GladeDesignLayoutPrivate *priv = GLADE_DESIGN_LAYOUT_PRIVATE (layout);
170
  gint width, height, xx, yy, top, bottom, left, right;
171
  GdkRectangle rec, child_rec;
172
  Margins margin = 0;
173

174 175
  width = gtk_widget_get_allocated_width (widget);
  height = gtk_widget_get_allocated_height (widget);
176

177
  gtk_widget_translate_coordinates (widget, GTK_WIDGET (layout), 0, 0, &xx, &yy);
178 179

  get_margins (widget, &left, &right, &top, &bottom);
180 181 182

  rec.x = xx - left - OUTLINE_WIDTH;
  rec.y = yy - top - OUTLINE_WIDTH;
183 184
  rec.width = width + left + right + (OUTLINE_WIDTH * 2);
  rec.height = height + top + bottom + (OUTLINE_WIDTH * 2);
185

186 187 188 189 190 191 192 193
  gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN (layout)), &child_rec);
  child_rec.x = (child_rec.x + priv->child_offset) - OUTLINE_WIDTH;
  child_rec.y = (child_rec.y + priv->child_offset) - OUTLINE_WIDTH;
  child_rec.width += OUTLINE_WIDTH * 2;
  child_rec.height += OUTLINE_WIDTH * 2;

  gdk_rectangle_intersect (&rec, &child_rec, &rec);

194 195 196
  if (RECTANGLE_POINT_IN (rec, x, y))
    {      
      if (y <= yy + OUTLINE_WIDTH) margin |= MARGIN_TOP;
197
      else if (y >= yy + height - OUTLINE_WIDTH) margin |= MARGIN_BOTTOM;
198 199
      
      if (x <= xx + OUTLINE_WIDTH) margin |= MARGIN_LEFT;
200
      else if (x >= xx + width - OUTLINE_WIDTH) margin |= MARGIN_RIGHT;
201 202 203 204 205
    }

  return margin;
}

206
static Activity
207
gdl_get_activity_from_pointer (GladeDesignLayout *layout, gint x, gint y)
208
{
209
  GladeDesignLayoutPrivate *priv = GLADE_DESIGN_LAYOUT_PRIVATE (layout);
210 211
  
  if (priv->selection)
212
    {
213
      priv->margin = gdl_get_margins_from_pointer (layout, priv->selection, x, y);
214
      
215 216 217 218 219
      if (priv->margin)
        {
          GladePointerMode mode = glade_project_get_pointer_mode (priv->project);
          return (mode == GLADE_POINTER_ALIGN_EDIT) ? ACTIVITY_ALIGNMENTS : ACTIVITY_MARGINS;
        }
220 221
    }
  
222
  if (RECTANGLE_POINT_IN (priv->south_east, x, y)) return ACTIVITY_RESIZE_WIDTH_AND_HEIGHT;
223

224
  if (RECTANGLE_POINT_IN (priv->east, x, y)) return ACTIVITY_RESIZE_WIDTH;
225

226
  if (RECTANGLE_POINT_IN (priv->south, x, y)) return ACTIVITY_RESIZE_HEIGHT;
227

228
  return ACTIVITY_NONE;
229 230
}

231
static inline void
232 233
gdl_set_cursor (GladeDesignLayoutPrivate *priv, GdkCursor *cursor)
{
234 235
  if (cursor != gdk_window_get_cursor (priv->window))
    gdk_window_set_cursor (priv->window, cursor);
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
}

static Activity
gdl_margin_get_activity (Margins margin)
{
  if (margin & MARGIN_TOP)
    {
      if (margin & MARGIN_LEFT)
        return ACTIVITY_MARGINS_TOP_LEFT;
      else if (margin & MARGIN_RIGHT)
        return ACTIVITY_MARGINS_TOP_RIGHT;
      else
        return ACTIVITY_MARGINS_VERTICAL;
    }
  else if (margin & MARGIN_BOTTOM)
    {
      if (margin & MARGIN_LEFT)
        return ACTIVITY_MARGINS_BOTTOM_LEFT;
      else if (margin & MARGIN_RIGHT)
        return ACTIVITY_MARGINS_BOTTOM_RIGHT;
      else
        return ACTIVITY_MARGINS_VERTICAL;
    }
  else if (margin & MARGIN_LEFT || margin & MARGIN_RIGHT)
    return ACTIVITY_MARGINS_HORIZONTAL;

  return ACTIVITY_NONE;
}

265
static gboolean
266
glade_design_layout_enter_leave_notify_event (GtkWidget *widget, GdkEventCrossing *ev)
267
{
268 269 270
  GtkWidget *child;
  GladeDesignLayoutPrivate *priv;

271 272
  if ((child = gtk_bin_get_child (GTK_BIN (widget))) == NULL ||
      ev->window != gtk_widget_get_window (widget))
273
    return FALSE;
274

275
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
276

277 278 279 280 281 282 283 284 285 286
  if (ev->type == GDK_ENTER_NOTIFY)
    {
      Activity activity = priv->activity;

      if (priv->activity == ACTIVITY_MARGINS)
        activity = gdl_margin_get_activity (priv->margin);

      gdl_set_cursor (priv, priv->cursors[activity]);
    }
  else if (priv->activity == ACTIVITY_NONE)
287
    gdl_set_cursor (priv, NULL);
288

289
  return FALSE;
290
}
291

292
static void
293
gdl_update_max_margins (GladeDesignLayout *layout,
294 295 296
                        GtkWidget *child,
                        gint width, gint height)
{
297 298
  GladeDesignLayoutPrivate *priv = layout->priv;
  gint parent_w, parent_h, layout_w, layout_h;
299 300 301 302 303
  gint top, bottom, left, right;
  GtkRequisition req;
  
  gtk_widget_get_preferred_size (child, &req, NULL);

304 305
  get_margins (priv->selection, &left, &right, &top, &bottom);

306
  priv->max_width = width - (req.width - left - right);
307

308
  parent_w = gtk_widget_get_allocated_width (GTK_WIDGET (priv->view));
309 310 311
  layout_w = gtk_widget_get_allocated_width (GTK_WIDGET (layout));

  if (parent_w > layout_w)
312
    priv->max_width += parent_w - layout_w - (PADDING - OUTLINE_WIDTH);
313 314 315
  
  priv->max_height = height - (req.height - top - bottom) ;

316
  parent_h = gtk_widget_get_allocated_height (GTK_WIDGET (priv->view));
317 318
  layout_h = gtk_widget_get_allocated_height (GTK_WIDGET (layout));
  if (parent_h > layout_h)
319
    priv->max_height += parent_h - layout_h - (PADDING - OUTLINE_WIDTH);
320 321
}

322
static void
323 324 325
glade_design_layout_update_child (GladeDesignLayout *layout,
                                  GtkWidget         *child,
                                  GtkAllocation     *allocation)
326
{
327
  GladeWidget *gchild;
328

329 330 331 332 333
  /* Update GladeWidget metadata */
  gchild = glade_widget_get_from_gobject (child);
  g_object_set (gchild,
                "toplevel-width", allocation->width,
                "toplevel-height", allocation->height, NULL);
334

335
  if (layout->priv->selection)
336
    gdl_update_max_margins (layout, child, allocation->width, allocation->height);
337

338
  gtk_widget_queue_resize (GTK_WIDGET (layout));
339 340
}

341 342 343 344 345
static inline void
gdl_alignments_invalidate (GdkWindow *window,
                           GtkWidget *parent,
                           GtkWidget *selection,
                           Margins nodes)
346
{
347 348 349
  cairo_region_t *region = cairo_region_create ();
  cairo_rectangle_int_t rect = {0, 0, 16, 16};
  gint x1, x2, x3, y1, y2, y3;
350
  GtkAllocation alloc;
351
  gint x, y, w, h;
352

353
  gtk_widget_get_allocation (selection, &alloc);
354

355 356
  w = alloc.width;
  h = alloc.height;
357

358
  gtk_widget_translate_coordinates (selection, parent, 0, 0, &x, &y);
359

360
  x1 = x - get_margin_left (selection);
361
  x2 = x + w/2;
362 363
  x3 = x + w + get_margin_right (selection);
  y1 = y - get_margin_top (selection);
364
  y2 = y + h/2;
365
  y3 = y + h + get_margin_bottom (selection);
366

367 368 369
  /* Only invalidate node area */
  if (nodes & MARGIN_TOP)
    {
370
      rect.x = x2 - 5;
371 372
      rect.y = y1 - 10;
      cairo_region_union_rectangle (region, &rect);
373
    }
374
  if (nodes & MARGIN_BOTTOM)
375
    {
376
      rect.x = x2 - 8;
377 378 379
      rect.y = y3 - 13;
      cairo_region_union_rectangle (region, &rect);
    }
380

381 382 383 384 385 386 387 388 389 390
  rect.y = y2 - 10;
  if (nodes & MARGIN_LEFT)
    {
      rect.x = x1 - 8;
      cairo_region_union_rectangle (region, &rect);
    }
  if (nodes & MARGIN_RIGHT)
    {
      rect.x = x3 - 5;
      cairo_region_union_rectangle (region, &rect);
391 392
    }

393 394 395
  gdk_window_invalidate_region (window, region, FALSE);

  cairo_region_destroy (region);
396
}
397

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
static void
gdl_update_cursor_for_position (GtkWidget *widget, gint x, gint y)
{
  GladeDesignLayout *layout = GLADE_DESIGN_LAYOUT (widget);
  Activity activity = gdl_get_activity_from_pointer (layout, x, y);
  GladeDesignLayoutPrivate *priv = layout->priv;

  if (priv->node_over != priv->margin &&
      (activity == ACTIVITY_ALIGNMENTS ||
       glade_project_get_pointer_mode (priv->project) == GLADE_POINTER_ALIGN_EDIT))
    {
      if (priv->selection)
        gdl_alignments_invalidate (priv->window, widget, priv->selection,
                                   priv->node_over | priv->margin);
      else
        gdk_window_invalidate_rect (priv->window, NULL, FALSE);

      priv->node_over = priv->margin;
    }

  if (activity == ACTIVITY_MARGINS)
    activity = gdl_margin_get_activity (priv->margin);
  
  /* Only set the cursor if changed */
  gdl_set_cursor (priv, priv->cursors[activity]);
}

425
static gboolean
426
glade_design_layout_motion_notify_event (GtkWidget *widget, GdkEventMotion *ev)
427
{
428 429
  GladeDesignLayout *layout = GLADE_DESIGN_LAYOUT (widget);
  GladeDesignLayoutPrivate *priv = layout->priv;
430
  GtkAllocation allocation;
431 432
  GtkWidget *child;
  gint x, y;
433

434
  if ((child = gtk_bin_get_child (GTK_BIN (widget))) == NULL)
435 436
    return FALSE;

437 438
  x = ev->x;
  y = ev->y;
439

440 441 442 443 444 445
  if (ev->state & GDK_BUTTON1_MASK && priv->drag_source &&
      gtk_drag_check_threshold (priv->drag_source, priv->drag_x, priv->drag_y, x, y))
    {
      static GtkTargetList *target = NULL;

      if (target == NULL)
Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
446
        target = gtk_target_list_new (_glade_dnd_get_target (), 1);
447

Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
448
      gtk_drag_begin_with_coordinates (widget, target, 0, 1, (GdkEvent*)ev, x, y);
449 450 451
      return TRUE;
    }

452 453
  gtk_widget_get_allocation (child, &allocation);

454 455
  allocation.x += priv->child_offset;
  allocation.y += priv->child_offset;
456

457
  switch (priv->activity)
458
    {
459
      case ACTIVITY_RESIZE_WIDTH:
460
        allocation.width = MAX (0, x - priv->dx - PADDING - OUTLINE_WIDTH);
461
        glade_design_layout_update_child (layout, child, &allocation);
462
      break;
463
      case ACTIVITY_RESIZE_HEIGHT:
464
        allocation.height = MAX (0, y - priv->dy - PADDING - OUTLINE_WIDTH);
465
        glade_design_layout_update_child (layout, child, &allocation);
466
      break;
467
      case ACTIVITY_RESIZE_WIDTH_AND_HEIGHT:
468 469
        allocation.height = MAX (0, y - priv->dy - PADDING - OUTLINE_WIDTH);
        allocation.width = MAX (0, x - priv->dx - PADDING - OUTLINE_WIDTH);
470
        glade_design_layout_update_child (layout, child, &allocation);
471
      break;
472
      case ACTIVITY_MARGINS:
473 474
        {
          gboolean shift = ev->state & GDK_SHIFT_MASK;
475
          gboolean snap = ev->state & GDK_CONTROL_MASK;
476 477
          GtkWidget *selection = priv->selection;
          Margins margin = priv->margin;
478

479 480
          if (margin & MARGIN_TOP)
            {
481
              gint max_height = (shift) ? priv->max_height/2 : priv->max_height -
482
                get_margin_bottom (selection);
483 484
              gint val = MAX (0, MIN (priv->m_dy - y, max_height));
              
485
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
486 487 488 489 490
              gtk_widget_set_margin_top (selection, val);
              if (shift) gtk_widget_set_margin_bottom (selection, val);
            }
          else if (margin & MARGIN_BOTTOM)
            {
491
              gint max_height = (shift) ? priv->max_height/2 : priv->max_height -
492
                get_margin_top (selection);
493 494
              gint val = MAX (0, MIN (y - priv->m_dy, max_height));
              
495
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
496 497 498
              gtk_widget_set_margin_bottom (selection, val);
              if (shift) gtk_widget_set_margin_top (selection, val);
            }
499

500 501
          if (margin & MARGIN_LEFT)
            {
502
              gint max_width = (shift) ? priv->max_width/2 : priv->max_width -
503
                get_margin_right (selection);
504 505
              gint val = MAX (0, MIN (priv->m_dx - x, max_width));
              
506
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
507 508
              gtk_widget_set_margin_start (selection, val);
              if (shift) gtk_widget_set_margin_end (selection, val);
509 510 511
            }
          else if (margin & MARGIN_RIGHT)
            {
512
              gint max_width = (shift) ? priv->max_width/2 : priv->max_width -
513
                get_margin_left (selection);
514 515
              gint val = MAX (0, MIN (x - priv->m_dx, max_width));
              
516
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
517 518
              gtk_widget_set_margin_end (selection, val);
              if (shift) gtk_widget_set_margin_start (selection, val);
519 520 521
            }
        }
      break;
522
      default:
523
        gdl_update_cursor_for_position (widget, x, y);
524
      break;
525 526
    }

527
  return (priv->activity != ACTIVITY_NONE);
528 529
}

530 531 532 533 534 535 536
static gboolean
glade_project_is_toplevel_active (GladeProject *project, GtkWidget *toplevel)
{
  GList *l;

  for (l = glade_project_selection_get (project); l; l = g_list_next (l))
    {
537 538
      if (GTK_IS_WIDGET (l->data) && 
	  gtk_widget_is_ancestor (l->data, toplevel)) return TRUE;
539 540 541 542 543
    }

  return FALSE;
}

544
static void
545 546 547
gdl_edit_mode_set_selection (GladeDesignLayout *layout,
                             GladePointerMode mode,
                             GtkWidget *selection)
548
{
549 550 551 552 553
  GladeDesignLayoutPrivate *priv = layout->priv;

  if ((selection && GTK_IS_WIDGET (selection) == FALSE) ||
      gtk_bin_get_child (GTK_BIN (layout)) == selection) selection = NULL;
  
554 555 556 557 558 559
  if (priv->selection == selection) return;

  priv->selection = selection;

  if (selection)
    {
560 561 562
      if (mode == GLADE_POINTER_MARGIN_EDIT)
        {
          GtkWidget *child = gtk_bin_get_child (GTK_BIN (layout));
563

564
          /* Save initital margins to know which one where edited */
565
          get_margins (selection, &priv->left, &priv->right, &priv->top, &priv->bottom);
566

567 568 569 570 571 572 573 574 575 576 577
          gdl_update_max_margins (layout, child,
                                  gtk_widget_get_allocated_width (child),
                                  gtk_widget_get_allocated_height (child));
        }
      else if (mode == GLADE_POINTER_ALIGN_EDIT)
        {
          priv->valign = gtk_widget_get_valign (selection);
          priv->halign = gtk_widget_get_halign (selection);
        }

      gdk_window_invalidate_rect (priv->window, NULL, FALSE);
578 579 580 581 582
    }
  else
    {
      gdl_set_cursor (priv, NULL);
    }
583 584

  glade_project_set_pointer_mode (priv->project, mode);
585 586
}

587
static gboolean
588
glade_design_layout_button_press_event (GtkWidget *widget, GdkEventButton *ev)
589
{
590
  GladeDesignLayoutPrivate *priv;
591 592
  GtkAllocation child_allocation;
  Activity activity;
593
  GtkWidget *child;
594
  gint x, y;
595

596 597
  if (ev->button != 1 ||
      (ev->type != GDK_BUTTON_PRESS && ev->type != GDK_2BUTTON_PRESS) ||
598
      (child = gtk_bin_get_child (GTK_BIN (widget))) == NULL)
599 600
    return FALSE;

601
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
602 603 604 605

  x = ev->x;
  y = ev->y;

606 607 608 609
  priv->activity = activity = gdl_get_activity_from_pointer (GLADE_DESIGN_LAYOUT (widget), x, y);

  /* Check if we are in margin edit mode */
  if (priv->selection)
610
    {
611
      GtkWidget *selection = priv->selection;
612

613
      switch (activity)
614
        {
615 616 617 618 619
          case ACTIVITY_NONE:
            gdl_edit_mode_set_selection (GLADE_DESIGN_LAYOUT (widget), GLADE_POINTER_SELECT, NULL);
            return FALSE;
          break;
          case ACTIVITY_ALIGNMENTS:
620
            {
621 622 623 624
              gboolean top, bottom, left, right;
              Margins node = priv->margin;
              GtkAlign valign, halign;
              GladeWidget *gwidget;
625

626 627
              valign = gtk_widget_get_valign (selection);
              halign = gtk_widget_get_halign (selection);
628

629 630 631 632 633 634 635
              if (valign == GTK_ALIGN_FILL)
                top = bottom = TRUE;
              else
                {
                  top = (valign == GTK_ALIGN_START);
                  bottom = (valign == GTK_ALIGN_END);
                }
636

637 638 639 640 641 642 643
              if (halign == GTK_ALIGN_FILL)
                left = right = TRUE;
              else
                {
                  left = (halign == GTK_ALIGN_START);
                  right = (halign == GTK_ALIGN_END);
                }
644

645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
              if (node & MARGIN_TOP)
                valign = (top) ? ((bottom) ? GTK_ALIGN_END : GTK_ALIGN_CENTER) : 
                                 ((bottom) ? GTK_ALIGN_FILL : GTK_ALIGN_START);
              else if (node & MARGIN_BOTTOM)
                valign = (bottom) ? ((top) ? GTK_ALIGN_START : GTK_ALIGN_CENTER) :
                                    ((top) ? GTK_ALIGN_FILL : GTK_ALIGN_END);

              if (node & MARGIN_LEFT)
                halign = (left) ? ((right) ? GTK_ALIGN_END : GTK_ALIGN_CENTER) :
                                  ((right) ? GTK_ALIGN_FILL : GTK_ALIGN_START);
              else if (node & MARGIN_RIGHT)
                halign = (right) ? ((left) ? GTK_ALIGN_START : GTK_ALIGN_CENTER) :
                                   ((left) ? GTK_ALIGN_FILL : GTK_ALIGN_END);

              if ((gwidget = glade_widget_get_from_gobject (selection)))
                {
                  GladeProperty *property;

                  glade_command_push_group (_("Editing alignments of %s"),
                                            glade_widget_get_name (gwidget));

                  if (gtk_widget_get_valign (selection) != valign)
                    {
                      if ((property = glade_widget_get_property (gwidget, "valign")))
                        glade_command_set_property (property, valign);
                    }
                  if (gtk_widget_get_halign (selection) != halign)
                    {
                      if ((property = glade_widget_get_property (gwidget, "halign")))
                        glade_command_set_property (property, halign);
                    }
                  glade_command_pop_group ();
                }
            }
679
          break;
680 681
          case ACTIVITY_MARGINS:
            priv->m_dx = x + ((priv->margin & MARGIN_LEFT) ? 
682 683
                              get_margin_left (selection) :
                                get_margin_right (selection) * -1);
684
            priv->m_dy = y + ((priv->margin & MARGIN_TOP) ?
685 686
                              get_margin_top (selection) :
                                get_margin_bottom (selection) * -1);
687 688

            gdl_set_cursor (priv, priv->cursors[gdl_margin_get_activity (priv->margin)]);
689
            return TRUE;
690 691
          break;
          default:
692
            gdl_set_cursor (priv, priv->cursors[priv->activity]);
693 694
          break;
        }
695
    }
696

697
  gtk_widget_get_allocation (child, &child_allocation);
698

699 700
  priv->dx = x - (child_allocation.x + child_allocation.width + priv->child_offset);
  priv->dy = y - (child_allocation.y + child_allocation.height + priv->child_offset);
701

702 703 704
  if (activity != ACTIVITY_NONE && 
      (!glade_project_is_toplevel_active (priv->project, child) ||
      ev->type == GDK_2BUTTON_PRESS))
705 706 707 708
    {
      _glade_design_view_freeze (priv->view);
      glade_project_selection_set (priv->project, G_OBJECT (child), TRUE);
      _glade_design_view_thaw (priv->view);
709
    }
710

711
  return (activity != ACTIVITY_NONE);
712
}
713

714
static gboolean
715 716
glade_design_layout_button_release_event (GtkWidget *widget,
                                          GdkEventButton *ev)
717
{
718 719
  GladeDesignLayoutPrivate *priv;
  GtkWidget *child;
720

721 722
  if ((child = gtk_bin_get_child (GTK_BIN (widget))) == NULL)
    return FALSE;
723

724
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
725

726 727 728 729 730 731 732
  /* Check if margins where edited and execute corresponding glade command */
  if (priv->selection && priv->activity == ACTIVITY_MARGINS)
    {
      GladeWidget *gwidget = glade_widget_get_from_gobject (priv->selection);
      gint top, bottom, left, right;
      GladeProperty *property;

733
      get_margins (priv->selection, &left, &right, &top, &bottom);
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756

      glade_command_push_group (_("Editing margins of %s"),
                                glade_widget_get_name (gwidget));
      if (priv->top != top)
        {
          if ((property = glade_widget_get_property (gwidget, "margin-top")))
            glade_command_set_property (property, top);
        }
      if (priv->bottom != bottom)
        {
          if ((property = glade_widget_get_property (gwidget, "margin-bottom")))
            glade_command_set_property (property, bottom);
        }
      if (priv->left != left)
        {
          if ((property = glade_widget_get_property (gwidget, "margin-left")))
            glade_command_set_property (property, left);
        }
      if (priv->right != right)
        {
          if ((property = glade_widget_get_property (gwidget, "margin-right")))
            glade_command_set_property (property, right);
        }
757 758 759

      glade_command_pop_group ();
    }
760 761 762 763 764
  else if (priv->activity == ACTIVITY_ALIGNMENTS)
    {
      priv->node_over = 0;
      gdk_window_invalidate_rect (priv->window, NULL, FALSE);
    }
765

766
  priv->activity = ACTIVITY_NONE;
767
  gdl_update_cursor_for_position (widget, ev->x, ev->y);
768

769
  return TRUE;
770 771 772
}

static void
773 774
glade_design_layout_get_preferred_height (GtkWidget *widget,
                                          gint *minimum, gint *natural)
775
{
776 777 778 779 780
  GladeDesignLayoutPrivate *priv;
  GtkWidget *child;
  GladeWidget *gchild;
  gint child_height = 0;
  guint border_width = 0;
781

782
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
783

784
  *minimum = 0;
785

786
  child = gtk_bin_get_child (GTK_BIN (widget));
787

788 789
  if (child && gtk_widget_get_visible (child))
    {
790
      GtkRequisition req;
791 792
      gint height;

793 794
      gchild = glade_widget_get_from_gobject (child);
      g_assert (gchild);
795

796
      gtk_widget_get_preferred_size (child, &req, NULL);
797

798
      g_object_get (gchild, "toplevel-height", &child_height, NULL);
799

800
      child_height = MAX (child_height, req.height);
801

802 803 804 805
      if (priv->widget_name)
        pango_layout_get_pixel_size (priv->widget_name, NULL, &height);
      else
        height = PADDING;
806 807

      *minimum = MAX (*minimum, PADDING + 2.5*OUTLINE_WIDTH + height + child_height);
808 809
    }

810
  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
811
  *minimum += border_width * 2;
812
  *natural = *minimum;
813
}
814

815
static void
816 817
glade_design_layout_get_preferred_width (GtkWidget *widget,
                                         gint *minimum, gint *natural)
818
{