glade-design-layout.c 78 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-private.h"
31
#include "glade-design-layout.h"
32
#include "glade-design-private.h"
33 34
#include "glade-accumulators.h"
#include "glade-marshallers.h"
35

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

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

41
#define OUTLINE_WIDTH     4
42
#define PADDING           12
43

44
#define MARGIN_STEP       6
45

46
typedef enum
47
{
48 49 50
  ACTIVITY_NONE,
  ACTIVITY_RESIZE_WIDTH,
  ACTIVITY_RESIZE_HEIGHT,
51
  ACTIVITY_RESIZE_WIDTH_AND_HEIGHT,
52
  ACTIVITY_ALIGNMENTS,
53 54 55 56 57 58 59 60
  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
61
} Activity;
62

63 64 65 66 67 68 69 70 71

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

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

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

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

85 86
  GtkStyleContext *default_context;

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

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

101 102 103 104 105 106
  /* 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 */
107

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

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

enum
{
  PROP_0,
  PROP_DESIGN_VIEW
122 123
};

124
G_DEFINE_TYPE_WITH_PRIVATE (GladeDesignLayout, glade_design_layout, GTK_TYPE_BIN)
125

126
#define RECTANGLE_POINT_IN(rect,x,y) (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y && y <= (rect.y + rect.height))
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 166
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);
}

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

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

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

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

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

187 188 189 190 191 192 193 194
  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);

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

  return margin;
}

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

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

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

229
  return ACTIVITY_NONE;
230 231
}

232
static inline void
233 234
gdl_set_cursor (GladeDesignLayoutPrivate *priv, GdkCursor *cursor)
{
235 236
  if (cursor != gdk_window_get_cursor (priv->window))
    gdk_window_set_cursor (priv->window, cursor);
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 265
}

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

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

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

276
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
277

278 279 280 281 282 283 284 285 286 287
  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)
288
    gdl_set_cursor (priv, NULL);
289

290
  return FALSE;
291
}
292

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

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

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

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

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

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

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

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

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

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

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

354
  gtk_widget_get_allocation (selection, &alloc);
355

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

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

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

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

382 383 384 385 386 387 388 389 390 391
  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);
392 393
    }

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

  cairo_region_destroy (region);
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 425
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]);
}

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

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

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

441 442 443 444 445 446
  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
447
        target = gtk_target_list_new (_glade_dnd_get_target (), 1);
448

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

453 454
  gtk_widget_get_allocation (child, &allocation);

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

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

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

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

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

531 532 533 534 535 536 537
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))
    {
538
      if (GTK_IS_WIDGET (l->data) && 
539
          gtk_widget_is_ancestor (l->data, toplevel)) return TRUE;
540 541 542 543 544
    }

  return FALSE;
}

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

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

  priv->selection = selection;

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

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

568 569 570 571 572 573 574 575 576 577 578
          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);
579 580 581 582 583
    }
  else
    {
      gdl_set_cursor (priv, NULL);
    }
584 585

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

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

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

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

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

607 608 609 610
  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)
611
    {
612
      GtkWidget *selection = priv->selection;
613

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

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

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

638 639 640 641 642 643 644
              if (halign == GTK_ALIGN_FILL)
                left = right = TRUE;
              else
                {
                  left = (halign == GTK_ALIGN_START);
                  right = (halign == GTK_ALIGN_END);
                }
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 679
              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 ();
                }
            }
680
          break;
681 682
          case ACTIVITY_MARGINS:
            priv->m_dx = x + ((priv->margin & MARGIN_LEFT) ? 
683 684
                              get_margin_left (selection) :
                                get_margin_right (selection) * -1);
685
            priv->m_dy = y + ((priv->margin & MARGIN_TOP) ?
686 687
                              get_margin_top (selection) :
                                get_margin_bottom (selection) * -1);
688 689

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

698
  gtk_widget_get_allocation (child, &child_allocation);
699

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

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

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

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

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

723
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
724

725 726 727 728 729 730 731
  /* 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;

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

      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);
        }
756 757 758

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

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

768
  return TRUE;
769 770 771
}

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

781
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
782

783
  *minimum = 0;
784

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

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

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

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

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

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

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

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

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

814
static void
815 816
glade_design_layout_get_preferred_width (GtkWidget *widget,
                                         gint *minimum, gint *natural)
817
{
818 819 820 821 822 823 824 825 826 827 828
  GtkWidget *child;
  GladeWidget *gchild;
  gint child_width = 0;
  guint border_width = 0;

  *minimum = 0;

  child = gtk_bin_get_child (GTK_BIN (widget));

  if (child && gtk_widget_get_visible (child))
    {
829 830
      GtkRequisition req;
      
831 832 833
      gchild = glade_widget_get_from_gobject (child);
      g_assert (gchild);

834
      gtk_widget_get_preferred_size (child, &req, NULL);
835 836 837

      g_object_get (gchild, "toplevel-width", &child_width, NULL);

838
      child_width = MAX (child_width, req.width);
839

840
      *minimum = MAX (*minimum, 2*PADDING + 2*OUTLINE_WIDTH + child_width);
841 842 843 844
    }

  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
  *minimum += border_width * 2;
845
  *natural = *minimum;
846 847
}

848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
static void
glade_design_layout_get_preferred_width_for_height (GtkWidget       *widget,
                                                    gint             height,
                                                    gint            *minimum_width,
                                                    gint            *natural_width)
{
  glade_design_layout_get_preferred_width (widget, minimum_width, natural_width);
}

static void
glade_design_layout_get_preferred_height_for_width (GtkWidget       *widget,
                                                    gint             width,
                                                    gint            *minimum_height,
                                                    gint            *natural_height)
{
  glade_design_layout_get_preferred_height (widget, minimum_height, natural_height);
}

866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
static void
update_rectangles (GladeDesignLayoutPrivate *priv, GtkAllocation *alloc)
{
  GdkRectangle *rect = &priv->south_east;
  gint width, height;

  /* Update rectangles used to resize the children */
  priv->east.x = alloc->width + priv->child_offset;
  priv->east.y = priv->child_offset;
  priv->east.height = alloc->height;

  priv->south.x = priv->child_offset;
  priv->south.y = alloc->height + priv->child_offset;
  priv->south.width = alloc->width;
  
  /* Update south east rectangle width */
882 883 884 885 886
  if (priv->widget_name)
    pango_layout_get_pixel_size (priv->widget_name, &width, &height);
  else
    width = height = 0;

887 888 889
  priv->layout_width = width + (OUTLINE_WIDTH*2);
  width = MIN (alloc->width, width);

890 891
  rect->x = alloc->x + priv->child_offset + alloc->width - width - OUTLINE_WIDTH;
  rect->y = alloc->y + priv->child_offset + alloc->height;
892
  rect->width = width + (OUTLINE_WIDTH*2);
893
  rect->height = height + (OUTLINE_WIDTH*1.5);
894 895 896 897 898

  /* Update south rectangle width */
  priv->south.width = rect->x - priv->south.x;
}

899
static void
900 901
glade_design_layout_size_allocate (GtkWidget *widget,
                                   GtkAllocation *allocation)
902
{
903
  GtkWidget *child;
904

905
  gtk_widget_set_allocation (widget, allocation);
906
    
907
  if (gtk_widget_get_realized (widget))
908
  {
909
    gdk_window_move_resize (gtk_widget_get_window (widget),
910 911 912 913
                            allocation->x, allocation->y,
                            allocation->width, allocation->height);
  }

914
  child = gtk_bin_get_child (GTK_BIN (widget));
915

916 917
  if (child && gtk_widget_get_visible (child))
    {
918
      GladeDesignLayoutPrivate *priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
919 920
      GtkAllocation alloc;
      gint height, offset;
921

922
      offset = gtk_container_get_border_width (GTK_CONTAINER (widget)) + PADDING + OUTLINE_WIDTH;
923
      priv->child_rect.x = priv->child_rect.y = priv->child_offset = offset;
924

925 926 927 928 929 930
      if (priv->widget_name)
        pango_layout_get_pixel_size (priv->widget_name, NULL, &height);
      else
        height = PADDING;
      
      alloc.x = alloc.y = 0;
931 932
      priv->child_rect.width = alloc.width = allocation->width - (offset * 2);
      priv->child_rect.height = alloc.height = allocation->height - (offset + OUTLINE_WIDTH * 1.5 + height);
933
      
934 935
      if (gtk_widget_get_realized (widget))
        gdk_window_move_resize (priv->offscreen_window,
936
                                0, 0, alloc.width, alloc.height);
937

938
      gtk_widget_size_allocate (child, &alloc);
939
      update_rectangles (priv, &alloc);
940
    }
941 942
}

943 944 945 946 947 948 949
static inline void
update_widget_name (GladeDesignLayout *layout, GladeWidget *gwidget) 
{
  GladeDesignLayoutPrivate *priv = layout->priv;

  if (priv->widget_name && gwidget)
    {
950 951 952 953 954
      if (glade_widget_has_name (gwidget))
        {
          pango_layout_set_text (priv->widget_name, glade_widget_get_display_name (gwidget), -1);
        }
      else
955 956
        {
          GladeWidgetAdaptor *adaptor = glade_widget_get_adaptor (gwidget);
957
          pango_layout_set_text (priv->widget_name, glade_widget_adaptor_get_display_name (adaptor), -1);
958 959
        }

960 961 962 963
      gtk_widget_queue_resize (GTK_WIDGET (layout));
    }
}

964 965 966
static void
on_glade_widget_name_notify (GObject *gobject, GParamSpec *pspec, GladeDesignLayout *layout) 
{
967
  update_widget_name (layout, GLADE_WIDGET (gobject));
968 969
}

970
static void
971
glade_design_layout_add (GtkContainer *container, GtkWidget *widget)
972
{
973
  GladeDesignLayout *layout = GLADE_DESIGN_LAYOUT (container);
974
  GladeDesignLayoutPrivate *priv = layout->priv;
975
  GtkStyleContext *context = gtk_widget_get_style_context (widget);
976

977 978
  priv->child_rect.width = 0;
  priv->child_rect.height = 0;
979

980 981
  gtk_style_context_add_class (context, "background");

982
  gtk_widget_set_parent_window (widget, priv->offscreen_window);
983

984 985
  GTK_CONTAINER_CLASS (glade_design_layout_parent_class)->add (container,
                                                               widget);
986

987 988
  if (!priv->gchild &&
      (priv->gchild = glade_widget_get_from_gobject (G_OBJECT (widget))))
989
    {
990 991 992 993
      update_widget_name (layout, priv->gchild);
      g_signal_connect (priv->gchild, "notify::name",
                        G_CALLBACK (on_glade_widget_name_notify),
                        layout);
994 995 996
    }
    
  gtk_widget_queue_draw (GTK_WIDGET (container)); 
997 998 999
}

static void
1000
glade_design_layout_remove (GtkContainer *container, GtkWidget *widget)
1001
{
1002 1003 1004
  GladeWidget *gchild;

  if ((gchild = glade_widget_get_from_gobject (G_OBJECT (widget))))
1005 1006 1007 1008 1009 1010 1011 1012
    {
      GladeDesignLayoutPrivate *priv = GLADE_DESIGN_LAYOUT_PRIVATE (container);
      
      g_signal_handlers_disconnect_by_func (gchild, on_glade_widget_name_notify,
                                            GLADE_DESIGN_LAYOUT (container));
      if (gchild == priv->gchild)
        priv->gchild = NULL;
    }
1013

1014
  GTK_CONTAINER_CLASS (glade_design_layout_parent_class)->remove (container, widget);
1015
  gtk_widget_queue_draw (GTK_WIDGET (container));
1016 1017
}

1018 1019 1020 1021 1022 1023 1024
static gboolean
glade_design_layout_damage (GtkWidget *widget, GdkEventExpose *event)
{
  gdk_window_invalidate_rect (gtk_widget_get_window (widget), NULL, TRUE);
  return TRUE;
}

1025
static inline void
1026
draw_frame (GtkWidget *widget, cairo_t *cr, gboolean selected,
1027
            int x, int y, int w, int h)
1028
{
1029 1030
  GladeDesignLayoutPrivate *priv = ((GladeDesignLayout *)widget)->priv;
  GtkStyleContext *context = gtk_widget_get_style_context (widget);
1031

1032 1033
  gtk_render_background (context, cr, x, y, w, h);
  gtk_render_frame (context, cr, x, y, w, h);
1034

1035
  if (priv->widget_name)
1036
    {
1037
      GdkRectangle *rect = &priv->south_east;
1038
      gtk_style_context_save (context);
1039 1040 1041 1042 1043
      gtk_style_context_add_class (context, "handle");
      gtk_render_background (context, cr, rect->x, rect->y, rect->width, rect->height);
      gtk_render_frame (context, cr, rect->x, rect->y, rect->width, rect->height);
      gtk_render_layout (context, cr, rect->x + OUTLINE_WIDTH, rect->y + OUTLINE_WIDTH,
                         priv->widget_name);
1044
      gtk_style_context_restore (context);
1045
    }
1046
}
1047

1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
static void
draw_margin_selection (cairo_t *cr,
                       gint x1, gint x2, gint x3, gint x4, 
                       gint y1, gint y2, gint y3, gint y4,
                       gdouble r, gdouble g, gdouble b,
                       gint x5, gint y5)
{
  cairo_pattern_t *gradient = cairo_pattern_create_linear (x1, y1, x5, y5);

  cairo_pattern_add_color_stop_rgba (gradient, 0, r+.24, g+.24, b+.24, .08);
  cairo_pattern_add_color_stop_rgba (gradient, 1, r, g, b, .16);
  
  cairo_set_source (cr, gradient);
  
  cairo_move_to (cr, x1, y1);
  cairo_line_to (cr, x2, y2);
  cairo_line_to (cr, x3, y3);
  cairo_line_to (cr, x4, y4);
  cairo_close_path (cr);
  cairo_fill (cr);

  cairo_pattern_destroy (gradient);
}

1072
static inline void
1073 1074 1075 1076
draw_selection (cairo_t *cr,
                GtkWidget *parent,
                GtkWidget *widget,
                GdkRGBA *color)
1077
{
1078 1079
  gint x, y, w, h, xw, yh, y_top, yh_bottom, x_left, xw_right;
  gint top, bottom, left, right;
1080
  gdouble r, g, b;
1081
  GtkAllocation alloc;
1082
  GtkStyleContext *context;
1083 1084

  gtk_widget_get_allocation (widget, &alloc);
1085 1086

  if (alloc.x < 0 || alloc.y < 0) return;
1087 1088

  context = gtk_widget_get_style_context (parent);
1089
  gtk_style_context_save (context);
1090
  gtk_style_context_add_class (context, "selection");
1091
  r = color->red; g = color->green; b = color->blue;
1092
  gtk_widget_translate_coordinates (widget, parent, 0, 0, &x, &y);
1093

1094 1095 1096 1097 1098
  w = alloc.width;
  h = alloc.height;
  xw = x + w;
  yh = y + h;

1099
  get_margins (widget, &left, &right, &top, &bottom);
1100 1101 1102 1103 1104
  
  y_top = y - top;
  yh_bottom = yh + bottom;
  x_left = x - left;
  xw_right = xw + right;
1105

1106 1107
  /* Draw widget area overlay */
  gtk_render_background (context, cr, x, y, w, h);
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125

  /* Draw margins overlays */
  if (top)
    draw_margin_selection (cr, x, xw, xw_right, x_left, y, y, y_top, y_top,
                           r, g, b, x, y_top);

  if (bottom)
    draw_margin_selection (cr, x, xw, xw_right, x_left, yh, yh, yh_bottom, yh_bottom,
                           r, g, b, x, yh_bottom);

  if (left)
    draw_margin_selection (cr, x, x, x_left, x_left, y, yh, yh_bottom, y_top,
                           r, g, b, x_left, y);

  if (right)
    draw_margin_selection (cr, xw, xw, xw_right, xw_right, y, yh, yh_bottom, y_top,
                           r, g, b, xw_right, y);

1126
  /* Draw Selection box */
1127
  gtk_render_frame (context, cr, x - left, y - top, w + left + right, h + top + bottom);
1128
  gtk_style_context_restore (context);
1129 1130
}

1131
#define DIMENSION_OFFSET 9
1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
#define DIMENSION_LINE_OFFSET 4

static void
draw_hmark (cairo_t *cr, gdouble x, gdouble y)
{
  cairo_move_to (cr, x + 2, y - 2);
  cairo_line_to (cr, x - 2, y + 2);
}

static void
draw_vmark (cairo_t *cr, gdouble x, gdouble y)
{
  cairo_move_to (cr, x - 2, y - 2);
  cairo_line_to (cr, x + 2, y + 2);
}

static void
draw_vguide (cairo_t *cr, gdouble x, gdouble y, gint len)
{
  cairo_move_to (cr, x, y - DIMENSION_LINE_OFFSET);
  cairo_line_to (cr, x, y + len);
}

static void
draw_hguide (cairo_t *cr, gdouble x, gdouble y, gint len)
{
  cairo_move_to (cr, x + DIMENSION_LINE_OFFSET, y);
  cairo_line_to (cr, x - len, y);
}

static void
draw_pixel_value (cairo_t *cr, 
1164
                  GdkRGBA *bg, GdkRGBA *fg,
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
                  gdouble x, gdouble y,
                  gboolean rotate,
                  gboolean draw_border,
                  gint val)
{
  cairo_text_extents_t extents;
  gchar pixel_str[8];

  g_snprintf (pixel_str, 8, "%d", val);

  cairo_text_extents (cr, pixel_str, &extents);

1177 1178
  cairo_save (cr);
  
1179 1180
  if (rotate)
    {
1181
      cairo_translate (cr, x - 1.5, y + .5 + extents.width/2);
1182 1183 1184
      cairo_rotate (cr, G_PI/-2);
    }
  else
1185
    cairo_translate (cr, x - (extents.width+extents.x_bearing)/2, y - 2);
1186

1187 1188
  cairo_move_to (cr, 0, 0);
  
1189 1190
  if (draw_border || extents.width + 4 >= val)
    {
1191
      cairo_set_source_rgba (cr, bg->red, bg->green, bg->blue, .9);
1192 1193 1194 1195 1196 1197

      cairo_text_path (cr, pixel_str);
      cairo_set_line_width (cr, 3);
      cairo_stroke (cr);

      cairo_set_line_width (cr, 1);
1198
      gdk_cairo_set_source_rgba (cr, fg);
1199