glade-design-layout.c 77.4 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
79
  GdkCursor *cursor;            /* Current cursor */
  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
233
234
235
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
265
266
267
268
static void
gdl_set_cursor (GladeDesignLayoutPrivate *priv, GdkCursor *cursor)
{
  if (cursor != priv->cursor)
    {
      priv->cursor = cursor;
      gdk_window_set_cursor (priv->window, cursor);
    }
}

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

269
static gboolean
270
glade_design_layout_leave_notify_event (GtkWidget *widget, GdkEventCrossing *ev)
271
{
272
273
274
  GtkWidget *child;
  GladeDesignLayoutPrivate *priv;

275
276
  if ((child = gtk_bin_get_child (GTK_BIN (widget))) == NULL ||
      ev->window != gtk_widget_get_window (widget))
277
    return FALSE;
278

279
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
280

281
  if (priv->activity == ACTIVITY_NONE)
282
    gdl_set_cursor (priv, NULL);
283

284
  return FALSE;
285
}
286

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

299
300
  get_margins (priv->selection, &left, &right, &top, &bottom);

301
  priv->max_width = width - (req.width - left - right);
302

303
  parent_w = gtk_widget_get_allocated_width (GTK_WIDGET (priv->view));
304
305
306
  layout_w = gtk_widget_get_allocated_width (GTK_WIDGET (layout));

  if (parent_w > layout_w)
307
    priv->max_width += parent_w - layout_w - (PADDING - OUTLINE_WIDTH);
308
309
310
  
  priv->max_height = height - (req.height - top - bottom) ;

311
  parent_h = gtk_widget_get_allocated_height (GTK_WIDGET (priv->view));
312
313
  layout_h = gtk_widget_get_allocated_height (GTK_WIDGET (layout));
  if (parent_h > layout_h)
314
    priv->max_height += parent_h - layout_h - (PADDING - OUTLINE_WIDTH);
315
316
}

317
static void
318
319
320
glade_design_layout_update_child (GladeDesignLayout *layout,
                                  GtkWidget         *child,
                                  GtkAllocation     *allocation)
321
{
322
  GladeWidget *gchild;
323

324
325
326
327
328
  /* Update GladeWidget metadata */
  gchild = glade_widget_get_from_gobject (child);
  g_object_set (gchild,
                "toplevel-width", allocation->width,
                "toplevel-height", allocation->height, NULL);
329

330
  if (layout->priv->selection)
331
    gdl_update_max_margins (layout, child, allocation->width, allocation->height);
332

333
  gtk_widget_queue_resize (GTK_WIDGET (layout));
334
335
}

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

348
  gtk_widget_get_allocation (selection, &alloc);
349

350
351
  w = alloc.width;
  h = alloc.height;
352

353
  gtk_widget_translate_coordinates (selection, parent, 0, 0, &x, &y);
354

355
  x1 = x - get_margin_left (selection);
356
  x2 = x + w/2;
357
358
  x3 = x + w + get_margin_right (selection);
  y1 = y - get_margin_top (selection);
359
  y2 = y + h/2;
360
  y3 = y + h + get_margin_bottom (selection);
361

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

376
377
378
379
380
381
382
383
384
385
  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);
386
387
    }

388
389
390
  gdk_window_invalidate_region (window, region, FALSE);

  cairo_region_destroy (region);
391
}
392

393
static gboolean
394
glade_design_layout_motion_notify_event (GtkWidget *widget, GdkEventMotion *ev)
395
{
396
397
  GladeDesignLayoutPrivate *priv;
  GtkAllocation allocation;
398
399
  GtkWidget *child;
  gint x, y;
400

401
  if ((child = gtk_bin_get_child (GTK_BIN (widget))) == NULL)
402
403
    return FALSE;

404
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
405

406
407
  x = ev->x;
  y = ev->y;
408

409
410
411
412
413
414
  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
415
        target = gtk_target_list_new (_glade_dnd_get_target (), 1);
416

Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
417
      gtk_drag_begin_with_coordinates (widget, target, 0, 1, (GdkEvent*)ev, x, y);
418
419
420
      return TRUE;
    }

421
422
  gtk_widget_get_allocation (child, &allocation);

423
424
  allocation.x += priv->child_offset;
  allocation.y += priv->child_offset;
425

426
  switch (priv->activity)
427
    {
428
      case ACTIVITY_RESIZE_WIDTH:
429
430
        allocation.width = MAX (0, x - priv->dx - PADDING - OUTLINE_WIDTH);
      break;
431
      case ACTIVITY_RESIZE_HEIGHT:
432
433
        allocation.height = MAX (0, y - priv->dy - PADDING - OUTLINE_WIDTH);
      break;
434
      case ACTIVITY_RESIZE_WIDTH_AND_HEIGHT:
435
436
437
        allocation.height = MAX (0, y - priv->dy - PADDING - OUTLINE_WIDTH);
        allocation.width = MAX (0, x - priv->dx - PADDING - OUTLINE_WIDTH);
      break;
438
      case ACTIVITY_MARGINS:
439
440
        {
          gboolean shift = ev->state & GDK_SHIFT_MASK;
441
          gboolean snap = ev->state & GDK_CONTROL_MASK;
442
443
          GtkWidget *selection = priv->selection;
          Margins margin = priv->margin;
444

445
446
          if (margin & MARGIN_TOP)
            {
447
              gint max_height = (shift) ? priv->max_height/2 : priv->max_height -
448
                get_margin_bottom (selection);
449
450
              gint val = MAX (0, MIN (priv->m_dy - y, max_height));
              
451
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
452
453
454
455
456
              gtk_widget_set_margin_top (selection, val);
              if (shift) gtk_widget_set_margin_bottom (selection, val);
            }
          else if (margin & MARGIN_BOTTOM)
            {
457
              gint max_height = (shift) ? priv->max_height/2 : priv->max_height -
458
                get_margin_top (selection);
459
460
              gint val = MAX (0, MIN (y - priv->m_dy, max_height));
              
461
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
462
463
464
              gtk_widget_set_margin_bottom (selection, val);
              if (shift) gtk_widget_set_margin_top (selection, val);
            }
465

466
467
          if (margin & MARGIN_LEFT)
            {
468
              gint max_width = (shift) ? priv->max_width/2 : priv->max_width -
469
                get_margin_right (selection);
470
471
              gint val = MAX (0, MIN (priv->m_dx - x, max_width));
              
472
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
473
474
              gtk_widget_set_margin_start (selection, val);
              if (shift) gtk_widget_set_margin_end (selection, val);
475
476
477
            }
          else if (margin & MARGIN_RIGHT)
            {
478
              gint max_width = (shift) ? priv->max_width/2 : priv->max_width -
479
                get_margin_left (selection);
480
481
              gint val = MAX (0, MIN (x - priv->m_dx, max_width));
              
482
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
483
484
              gtk_widget_set_margin_end (selection, val);
              if (shift) gtk_widget_set_margin_start (selection, val);
485
486
487
            }
        }
      break;
488
      default:
489
490
491
        {
          Activity activity = gdl_get_activity_from_pointer (GLADE_DESIGN_LAYOUT (widget), x, y);

492
493
          if (priv->node_over != priv->margin && (activity == ACTIVITY_ALIGNMENTS ||
              glade_project_get_pointer_mode (priv->project) == GLADE_POINTER_ALIGN_EDIT))
494
            {
495
496
497
498
499
              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);
500

501
              priv->node_over = priv->margin;
502
            }
503
           
504
505
506
507
508
509
510
511
          if (activity == ACTIVITY_MARGINS)
            activity = gdl_margin_get_activity (priv->margin);

          /* Only set the cursor if changed */
          gdl_set_cursor (priv, priv->cursors[activity]);
          return TRUE;
        }
      break;
512
513
    }

514
  glade_design_layout_update_child (GLADE_DESIGN_LAYOUT (widget), child, &allocation);
515
  return FALSE;
516
517
}

518
519
520
521
522
523
524
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))
    {
525
526
      if (GTK_IS_WIDGET (l->data) && 
	  gtk_widget_is_ancestor (l->data, toplevel)) return TRUE;
527
528
529
530
531
    }

  return FALSE;
}

532
static void
533
534
535
gdl_edit_mode_set_selection (GladeDesignLayout *layout,
                             GladePointerMode mode,
                             GtkWidget *selection)
536
{
537
538
539
540
541
  GladeDesignLayoutPrivate *priv = layout->priv;

  if ((selection && GTK_IS_WIDGET (selection) == FALSE) ||
      gtk_bin_get_child (GTK_BIN (layout)) == selection) selection = NULL;
  
542
543
544
545
546
547
  if (priv->selection == selection) return;

  priv->selection = selection;

  if (selection)
    {
548
549
550
      if (mode == GLADE_POINTER_MARGIN_EDIT)
        {
          GtkWidget *child = gtk_bin_get_child (GTK_BIN (layout));
551

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

555
556
557
558
559
560
561
562
563
564
565
          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);
566
567
568
569
570
    }
  else
    {
      gdl_set_cursor (priv, NULL);
    }
571
572

  glade_project_set_pointer_mode (priv->project, mode);
573
574
}

575
static gboolean
576
glade_design_layout_button_press_event (GtkWidget *widget, GdkEventButton *ev)
577
{
578
  GladeDesignLayoutPrivate *priv;
579
580
  GtkAllocation child_allocation;
  Activity activity;
581
  GtkWidget *child;
582
  gint x, y;
583

584
585
  if (ev->button != 1 ||
      (ev->type != GDK_BUTTON_PRESS && ev->type != GDK_2BUTTON_PRESS) ||
586
      (child = gtk_bin_get_child (GTK_BIN (widget))) == NULL)
587
588
    return FALSE;

589
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
590
591
592
593

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

594
595
596
597
  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)
598
    {
599
      GtkWidget *selection = priv->selection;
600

601
      switch (activity)
602
        {
603
604
605
606
607
          case ACTIVITY_NONE:
            gdl_edit_mode_set_selection (GLADE_DESIGN_LAYOUT (widget), GLADE_POINTER_SELECT, NULL);
            return FALSE;
          break;
          case ACTIVITY_ALIGNMENTS:
608
            {
609
610
611
612
              gboolean top, bottom, left, right;
              Margins node = priv->margin;
              GtkAlign valign, halign;
              GladeWidget *gwidget;
613

614
615
              valign = gtk_widget_get_valign (selection);
              halign = gtk_widget_get_halign (selection);
616

617
618
619
620
621
622
623
              if (valign == GTK_ALIGN_FILL)
                top = bottom = TRUE;
              else
                {
                  top = (valign == GTK_ALIGN_START);
                  bottom = (valign == GTK_ALIGN_END);
                }
624

625
626
627
628
629
630
631
              if (halign == GTK_ALIGN_FILL)
                left = right = TRUE;
              else
                {
                  left = (halign == GTK_ALIGN_START);
                  right = (halign == GTK_ALIGN_END);
                }
632

633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
              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 ();
                }
            }
667
          break;
668
669
          case ACTIVITY_MARGINS:
            priv->m_dx = x + ((priv->margin & MARGIN_LEFT) ? 
670
671
                              get_margin_left (selection) :
                                get_margin_right (selection) * -1);
672
            priv->m_dy = y + ((priv->margin & MARGIN_TOP) ?
673
674
                              get_margin_top (selection) :
                                get_margin_bottom (selection) * -1);
675
676
677

            gdl_set_cursor (priv, priv->cursors[gdl_margin_get_activity (priv->margin)]);
            return FALSE;
678
679
          break;
          default:
680
            gdl_set_cursor (priv, priv->cursors[priv->activity]);
681
682
          break;
        }
683
    }
684

685
  gtk_widget_get_allocation (child, &child_allocation);
686

687
688
  priv->dx = x - (child_allocation.x + child_allocation.width + priv->child_offset);
  priv->dy = y - (child_allocation.y + child_allocation.height + priv->child_offset);
689

690
691
692
  if (activity != ACTIVITY_NONE && 
      (!glade_project_is_toplevel_active (priv->project, child) ||
      ev->type == GDK_2BUTTON_PRESS))
693
694
695
696
    {
      _glade_design_view_freeze (priv->view);
      glade_project_selection_set (priv->project, G_OBJECT (child), TRUE);
      _glade_design_view_thaw (priv->view);
697
    }
698
699

  return FALSE;
700
701
}
static gboolean
702
703
glade_design_layout_button_release_event (GtkWidget *widget,
                                          GdkEventButton *ev)
704
{
705
706
  GladeDesignLayoutPrivate *priv;
  GtkWidget *child;
707

708
709
  if ((child = gtk_bin_get_child (GTK_BIN (widget))) == NULL)
    return FALSE;
710

711
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
712

713
714
715
716
717
718
719
  /* 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;

720
      get_margins (priv->selection, &left, &right, &top, &bottom);
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743

      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);
        }
744
745
746

      glade_command_pop_group ();
    }
747
748
749
750
751
  else if (priv->activity == ACTIVITY_ALIGNMENTS)
    {
      priv->node_over = 0;
      gdk_window_invalidate_rect (priv->window, NULL, FALSE);
    }
752
  
753
  priv->activity = ACTIVITY_NONE;
754
  gdl_set_cursor (priv, NULL);
755

756
  return FALSE;
757
758
759
}

static void
760
761
glade_design_layout_get_preferred_height (GtkWidget *widget,
                                          gint *minimum, gint *natural)
762
{
763
764
765
766
767
  GladeDesignLayoutPrivate *priv;
  GtkWidget *child;
  GladeWidget *gchild;
  gint child_height = 0;
  guint border_width = 0;
768

769
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
770

771
  *minimum = 0;
772

773
  child = gtk_bin_get_child (GTK_BIN (widget));
774

775
776
  if (child && gtk_widget_get_visible (child))
    {
777
      GtkRequisition req;
778
779
      gint height;

780
781
      gchild = glade_widget_get_from_gobject (child);
      g_assert (gchild);
782

783
      gtk_widget_get_preferred_size (child, &req, NULL);
784

785
      g_object_get (gchild, "toplevel-height", &child_height, NULL);
786

787
      child_height = MAX (child_height, req.height);
788

789
790
791
792
      if (priv->widget_name)
        pango_layout_get_pixel_size (priv->widget_name, NULL, &height);
      else
        height = PADDING;
793
794

      *minimum = MAX (*minimum, PADDING + 2.5*OUTLINE_WIDTH + height + child_height);
795
796
    }

797
  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
798
  *minimum += border_width * 2;
799
  *natural = *minimum;
800
}
801

802
static void
803
804
glade_design_layout_get_preferred_width (GtkWidget *widget,
                                         gint *minimum, gint *natural)
805
{
806
807
808
809
810
811
812
813
814
815
816
  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))
    {
817
818
      GtkRequisition req;
      
819
820
821
      gchild = glade_widget_get_from_gobject (child);
      g_assert (gchild);

822
      gtk_widget_get_preferred_size (child, &req, NULL);
823
824
825

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

826
      child_width = MAX (child_width, req.width);
827

828
      *minimum = MAX (*minimum, 2*PADDING + 2*OUTLINE_WIDTH + child_width);
829
830
831
832
    }

  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
  *minimum += border_width * 2;
833
  *natural = *minimum;
834
835
}

836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
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);
}

854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
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 */
870
871
872
873
874
  if (priv->widget_name)
    pango_layout_get_pixel_size (priv->widget_name, &width, &height);
  else
    width = height = 0;

875
876
877
878
879
880
881
882
883
884
885
886
  priv->layout_width = width + (OUTLINE_WIDTH*2);
  width = MIN (alloc->width, width);

  rect->x = alloc->x + priv->child_offset + alloc->width - width - OUTLINE_WIDTH/2;
  rect->y = alloc->y + priv->child_offset + alloc->height + OUTLINE_WIDTH/2;
  rect->width = width + (OUTLINE_WIDTH*2);
  rect->height = height + OUTLINE_WIDTH;

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

887
static void
888
889
glade_design_layout_size_allocate (GtkWidget *widget,
                                   GtkAllocation *allocation)
890
{
891
  GtkWidget *child;
892

893
  gtk_widget_set_allocation (widget, allocation);
894
    
895
  if (gtk_widget_get_realized (widget))
896
  {
897
    gdk_window_move_resize (gtk_widget_get_window (widget),
898
899
900
901
                            allocation->x, allocation->y,
                            allocation->width, allocation->height);
  }

902
  child = gtk_bin_get_child (GTK_BIN (widget));
903

904
905
  if (child && gtk_widget_get_visible (child))
    {
906
      GladeDesignLayoutPrivate *priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
907
908
      GtkAllocation alloc;
      gint height, offset;
909

910
      offset = gtk_container_get_border_width (GTK_CONTAINER (widget)) + PADDING + OUTLINE_WIDTH;
911
      priv->child_rect.x = priv->child_rect.y = priv->child_offset = offset;
912

913
914
915
916
917
918
      if (priv->widget_name)
        pango_layout_get_pixel_size (priv->widget_name, NULL, &height);
      else
        height = PADDING;
      
      alloc.x = alloc.y = 0;
919
920
      priv->child_rect.width = alloc.width = allocation->width - (offset * 2);
      priv->child_rect.height = alloc.height = allocation->height - (offset + OUTLINE_WIDTH * 1.5 + height);
921
      
922
923
      if (gtk_widget_get_realized (widget))
        gdk_window_move_resize (priv->offscreen_window,
924
                                0, 0, alloc.width, alloc.height);
925

926
      gtk_widget_size_allocate (child, &alloc);
927
      update_rectangles (priv, &alloc);
928
    }
929
930
}

931
932
933
934
935
936
937
938
939
940
941
942
static inline void
update_widget_name (GladeDesignLayout *layout, GladeWidget *gwidget) 
{
  GladeDesignLayoutPrivate *priv = layout->priv;

  if (priv->widget_name && gwidget)
    {
      pango_layout_set_text (priv->widget_name, glade_widget_get_name (gwidget), -1);
      gtk_widget_queue_resize (GTK_WIDGET (layout));
    }
}

943
944
945
static void
on_glade_widget_name_notify (GObject *gobject, GParamSpec *pspec, GladeDesignLayout *layout) 
{
946
  update_widget_name (layout, GLADE_WIDGET (gobject));
947
948
}

949
static void
950
glade_design_layout_add (GtkContainer *container, GtkWidget *widget)
951
{
952
  GladeDesignLayout *layout = GLADE_DESIGN_LAYOUT (container);
953
  GladeDesignLayoutPrivate *priv = layout->priv;
954

955
956
  priv->child_rect.width = 0;
  priv->child_rect.height = 0;
957

958
  gtk_widget_set_parent_window (widget, priv->offscreen_window);
959

960
961
  GTK_CONTAINER_CLASS (glade_design_layout_parent_class)->add (container,
                                                               widget);
962

963
964
  if (!priv->gchild &&
      (priv->gchild = glade_widget_get_from_gobject (G_OBJECT (widget))))
965
    {
966
967
968
969
      update_widget_name (layout, priv->gchild);
      g_signal_connect (priv->gchild, "notify::name",
                        G_CALLBACK (on_glade_widget_name_notify),
                        layout);
970
971
972
    }
    
  gtk_widget_queue_draw (GTK_WIDGET (container));