glade-design-layout.c 74 KB
Newer Older
1
2
3
/*
 * glade-design-layout.c
 *
4
 * Copyright (C) 2006-2007 Vincent Geddes
5
 *                    2011 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
39
40
#define GLADE_DESIGN_LAYOUT_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object),  \
						 GLADE_TYPE_DESIGN_LAYOUT,               \
						 GladeDesignLayoutPrivate))
41
#define GLADE_DESIGN_LAYOUT_PRIVATE(object) (((GladeDesignLayout*)object)->priv)
42

43
#define OUTLINE_WIDTH     4
44
#define PADDING           12
45

46
#define MARGIN_STEP       6
47

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

65
66
67
68
69
70
71
72
73

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

74
75
struct _GladeDesignLayoutPrivate
{
76
  GdkWindow *window, *offscreen_window;
77

78
79
  gint child_offset;
  GdkRectangle east, south, south_east;
80
81
  GdkCursor *cursor;            /* Current cursor */
  GdkCursor *cursors[N_ACTIVITY];
82
83

  gint current_width, current_height;
84
  PangoLayout *widget_name;
85
  gint layout_width;
86

87
88
  GtkStyleContext *default_context;

89
  /* Colors */
90
  GdkRGBA fg_color;
91
92
93
  GdkRGBA frame_color[2];
  GdkRGBA frame_color_active[2];

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

103
104
105
106
107
108
  /* 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 */
109

110
111
112
113
114
  /* Drag & Drop */
  GtkWidget *drag_source;
  GtkWidget *drag_icon;
  gint drag_x, drag_y;

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

enum
{
  PROP_0,
  PROP_DESIGN_VIEW
124
125
};

126
G_DEFINE_TYPE (GladeDesignLayout, glade_design_layout, GTK_TYPE_BIN)
127

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

130
static Margins
131
gdl_get_margins_from_pointer (GtkWidget *child, GtkWidget *widget, gint x, gint y)
132
{
133
  gint width, height, xx, yy, top, bottom, left, right;
134
135
  Margins margin = 0;
  GdkRectangle rec;
136
137
138
  
  width = gtk_widget_get_allocated_width (widget);
  height = gtk_widget_get_allocated_height (widget);
139

140
141
  gtk_widget_translate_coordinates (widget, child, 0, 0, &xx, &yy);
  
142
143
144
145
146
147
148
  top = gtk_widget_get_margin_top (widget);
  bottom = gtk_widget_get_margin_bottom (widget);
  left = gtk_widget_get_margin_left (widget);
  right = gtk_widget_get_margin_right (widget);

  rec.x = xx - left - OUTLINE_WIDTH;
  rec.y = yy - top - OUTLINE_WIDTH;
149
150
  rec.width = width + left + right + (OUTLINE_WIDTH * 2);
  rec.height = height + top + bottom + (OUTLINE_WIDTH * 2);
151
152
153
154

  if (RECTANGLE_POINT_IN (rec, x, y))
    {      
      if (y <= yy + OUTLINE_WIDTH) margin |= MARGIN_TOP;
155
      else if (y >= yy + height - OUTLINE_WIDTH) margin |= MARGIN_BOTTOM;
156
157
      
      if (x <= xx + OUTLINE_WIDTH) margin |= MARGIN_LEFT;
158
      else if (x >= xx + width - OUTLINE_WIDTH) margin |= MARGIN_RIGHT;
159
160
161
162
163
    }

  return margin;
}

164
static Activity
165
gdl_get_activity_from_pointer (GladeDesignLayout *layout, gint x, gint y)
166
{
167
  GladeDesignLayoutPrivate *priv = GLADE_DESIGN_LAYOUT_GET_PRIVATE (layout);
168
169
  
  if (priv->selection)
170
    {
171
      priv->margin = gdl_get_margins_from_pointer (GTK_WIDGET (layout),
172
                                                   priv->selection,
173
                                                   x, y);
174
      
175
176
177
178
179
      if (priv->margin)
        {
          GladePointerMode mode = glade_project_get_pointer_mode (priv->project);
          return (mode == GLADE_POINTER_ALIGN_EDIT) ? ACTIVITY_ALIGNMENTS : ACTIVITY_MARGINS;
        }
180
181
    }
  
182
  if (RECTANGLE_POINT_IN (priv->south_east, x, y)) return ACTIVITY_RESIZE_WIDTH_AND_HEIGHT;
183

184
  if (RECTANGLE_POINT_IN (priv->east, x, y)) return ACTIVITY_RESIZE_WIDTH;
185

186
  if (RECTANGLE_POINT_IN (priv->south, x, y)) return ACTIVITY_RESIZE_HEIGHT;
187

188
  return ACTIVITY_NONE;
189
190
}

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
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;
}

228
static gboolean
229
glade_design_layout_leave_notify_event (GtkWidget *widget, GdkEventCrossing *ev)
230
{
231
232
233
  GtkWidget *child;
  GladeDesignLayoutPrivate *priv;

234
235
  if ((child = gtk_bin_get_child (GTK_BIN (widget))) == NULL ||
      ev->window != gtk_widget_get_window (widget))
236
    return FALSE;
237

238
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
239

240
  if (priv->activity == ACTIVITY_NONE)
241
    gdl_set_cursor (priv, NULL);
242

243
  return FALSE;
244
}
245

246
static void
247
gdl_update_max_margins (GladeDesignLayout *layout,
248
249
250
                        GtkWidget *child,
                        gint width, gint height)
{
251
252
  GladeDesignLayoutPrivate *priv = layout->priv;
  gint parent_w, parent_h, layout_w, layout_h;
253
254
255
256
257
258
259
260
261
  gint top, bottom, left, right;
  GtkRequisition req;
  
  gtk_widget_get_preferred_size (child, &req, NULL);

  top = gtk_widget_get_margin_top (priv->selection);
  bottom = gtk_widget_get_margin_bottom (priv->selection);
  left = gtk_widget_get_margin_left (priv->selection);
  right = gtk_widget_get_margin_right (priv->selection);
262
  
263
  priv->max_width = width - (req.width - left - right);
264

265
  parent_w = gtk_widget_get_allocated_width (GTK_WIDGET (priv->view));
266
267
268
  layout_w = gtk_widget_get_allocated_width (GTK_WIDGET (layout));

  if (parent_w > layout_w)
269
    priv->max_width += parent_w - layout_w - (PADDING - OUTLINE_WIDTH);
270
271
272
  
  priv->max_height = height - (req.height - top - bottom) ;

273
  parent_h = gtk_widget_get_allocated_height (GTK_WIDGET (priv->view));
274
275
  layout_h = gtk_widget_get_allocated_height (GTK_WIDGET (layout));
  if (parent_h > layout_h)
276
    priv->max_height += parent_h - layout_h - (PADDING - OUTLINE_WIDTH);
277
278
}

279
static void
280
281
282
glade_design_layout_update_child (GladeDesignLayout *layout,
                                  GtkWidget         *child,
                                  GtkAllocation     *allocation)
283
{
284
  GladeWidget *gchild;
285

286
287
288
289
290
  /* Update GladeWidget metadata */
  gchild = glade_widget_get_from_gobject (child);
  g_object_set (gchild,
                "toplevel-width", allocation->width,
                "toplevel-height", allocation->height, NULL);
291

292
  if (layout->priv->selection)
293
    gdl_update_max_margins (layout, child, allocation->width, allocation->height);
294

295
  gtk_widget_queue_resize (GTK_WIDGET (layout));
296
297
}

298
299
300
301
302
static inline void
gdl_alignments_invalidate (GdkWindow *window,
                           GtkWidget *parent,
                           GtkWidget *selection,
                           Margins nodes)
303
{
304
305
306
  cairo_region_t *region = cairo_region_create ();
  cairo_rectangle_int_t rect = {0, 0, 16, 16};
  gint x1, x2, x3, y1, y2, y3;
307
  GtkAllocation alloc;
308
  gint x, y, w, h;
309

310
  gtk_widget_get_allocation (selection, &alloc);
311

312
313
  w = alloc.width;
  h = alloc.height;
314

315
  gtk_widget_translate_coordinates (selection, parent, 0, 0, &x, &y);
316

317
318
319
320
321
322
  x1 = x - gtk_widget_get_margin_left (selection);
  x2 = x + w/2;
  x3 = x + w + gtk_widget_get_margin_right (selection);
  y1 = y - gtk_widget_get_margin_top (selection);
  y2 = y + h/2;
  y3 = y + h + gtk_widget_get_margin_bottom (selection);
323

324
325
326
  /* Only invalidate node area */
  if (nodes & MARGIN_TOP)
    {
327
      rect.x = x2 - 5;
328
329
      rect.y = y1 - 10;
      cairo_region_union_rectangle (region, &rect);
330
    }
331
  if (nodes & MARGIN_BOTTOM)
332
    {
333
      rect.x = x2 - 8;
334
335
336
      rect.y = y3 - 13;
      cairo_region_union_rectangle (region, &rect);
    }
337

338
339
340
341
342
343
344
345
346
347
  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);
348
349
    }

350
351
352
  gdk_window_invalidate_region (window, region, FALSE);

  cairo_region_destroy (region);
353
}
354

355
static gboolean
356
glade_design_layout_motion_notify_event (GtkWidget *widget, GdkEventMotion *ev)
357
{
358
359
  GladeDesignLayoutPrivate *priv;
  GtkAllocation allocation;
360
361
  GtkWidget *child;
  gint x, y;
362

363
  if ((child = gtk_bin_get_child (GTK_BIN (widget))) == NULL)
364
365
    return FALSE;

366
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
367

368
369
  x = ev->x;
  y = ev->y;
370

371
372
373
374
375
376
377
378
379
380
381
382
  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)
        target = gtk_target_list_new (_glade_design_layout_get_dnd_target (), 1);

      gtk_drag_begin (widget, target, GDK_ACTION_COPY, 1, (GdkEvent*)ev);
      return TRUE;
    }

383
384
  gtk_widget_get_allocation (child, &allocation);

385
386
  allocation.x += priv->child_offset;
  allocation.y += priv->child_offset;
387

388
  switch (priv->activity)
389
    {
390
      case ACTIVITY_RESIZE_WIDTH:
391
392
        allocation.width = MAX (0, x - priv->dx - PADDING - OUTLINE_WIDTH);
      break;
393
      case ACTIVITY_RESIZE_HEIGHT:
394
395
        allocation.height = MAX (0, y - priv->dy - PADDING - OUTLINE_WIDTH);
      break;
396
      case ACTIVITY_RESIZE_WIDTH_AND_HEIGHT:
397
398
399
        allocation.height = MAX (0, y - priv->dy - PADDING - OUTLINE_WIDTH);
        allocation.width = MAX (0, x - priv->dx - PADDING - OUTLINE_WIDTH);
      break;
400
      case ACTIVITY_MARGINS:
401
402
        {
          gboolean shift = ev->state & GDK_SHIFT_MASK;
403
          gboolean snap = ev->state & GDK_CONTROL_MASK;
404
405
          GtkWidget *selection = priv->selection;
          Margins margin = priv->margin;
406

407
408
          if (margin & MARGIN_TOP)
            {
409
410
411
412
              gint max_height = (shift) ? priv->max_height/2 : priv->max_height -
                gtk_widget_get_margin_bottom (selection);
              gint val = MAX (0, MIN (priv->m_dy - y, max_height));
              
413
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
414
415
416
417
418
              gtk_widget_set_margin_top (selection, val);
              if (shift) gtk_widget_set_margin_bottom (selection, val);
            }
          else if (margin & MARGIN_BOTTOM)
            {
419
420
421
422
              gint max_height = (shift) ? priv->max_height/2 : priv->max_height -
                gtk_widget_get_margin_top (selection);
              gint val = MAX (0, MIN (y - priv->m_dy, max_height));
              
423
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
424
425
426
              gtk_widget_set_margin_bottom (selection, val);
              if (shift) gtk_widget_set_margin_top (selection, val);
            }
427

428
429
          if (margin & MARGIN_LEFT)
            {
430
431
432
433
              gint max_width = (shift) ? priv->max_width/2 : priv->max_width -
                gtk_widget_get_margin_right (selection);
              gint val = MAX (0, MIN (priv->m_dx - x, max_width));
              
434
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
435
436
437
438
439
              gtk_widget_set_margin_left (selection, val);
              if (shift) gtk_widget_set_margin_right (selection, val);
            }
          else if (margin & MARGIN_RIGHT)
            {
440
441
442
443
              gint max_width = (shift) ? priv->max_width/2 : priv->max_width -
                gtk_widget_get_margin_left (selection);
              gint val = MAX (0, MIN (x - priv->m_dx, max_width));
              
444
              if (snap) val = (val/MARGIN_STEP)*MARGIN_STEP;
445
446
447
448
449
              gtk_widget_set_margin_right (selection, val);
              if (shift) gtk_widget_set_margin_left (selection, val);
            }
        }
      break;
450
      default:
451
452
453
        {
          Activity activity = gdl_get_activity_from_pointer (GLADE_DESIGN_LAYOUT (widget), x, y);

454
455
          if (priv->node_over != priv->margin && (activity == ACTIVITY_ALIGNMENTS ||
              glade_project_get_pointer_mode (priv->project) == GLADE_POINTER_ALIGN_EDIT))
456
            {
457
458
459
460
461
              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);
462

463
              priv->node_over = priv->margin;
464
            }
465
           
466
467
468
469
470
471
472
473
          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;
474
475
    }

476
  glade_design_layout_update_child (GLADE_DESIGN_LAYOUT (widget), child, &allocation);
477
  return FALSE;
478
479
}

480
481
482
483
484
typedef struct
{
  GtkWidget *toplevel;
  gint x;
  gint y;
485
  GtkWidget *placeholder;
486
487
488
489
  GladeWidget *gwidget;
} GladeFindInContainerData;

static void
490
491
glade_design_layout_find_inside_container (GtkWidget                *widget,
                                           GladeFindInContainerData *data)
492
{
493
  gint x, y, w, h;
494
495
496
497

  if (data->gwidget || !gtk_widget_get_mapped (widget))
    return;

498
499
  gtk_widget_translate_coordinates (data->toplevel, widget, data->x, data->y,
                                    &x, &y);
500
  
501
  /* Margins are not part of the widget allocation */
502
503
  w = gtk_widget_get_allocated_width (widget) + gtk_widget_get_margin_right (widget);
  h = gtk_widget_get_allocated_height (widget) + gtk_widget_get_margin_bottom (widget);
504

505
506
  if (x >= (0 - gtk_widget_get_margin_left (widget)) && x < w &&
      y >= (0 - gtk_widget_get_margin_top (widget)) && y < h)
507
    {
508
509
510
511
512
513
514
515
      if (GLADE_IS_PLACEHOLDER (widget))
        data->placeholder = widget;
      else
        {
          if (GTK_IS_CONTAINER (widget))
            gtk_container_forall (GTK_CONTAINER (widget), (GtkCallback)
                                  glade_design_layout_find_inside_container,
                                  data);
516

517
518
519
          if (!data->gwidget)
            data->gwidget = glade_widget_get_from_gobject (widget);
        }
520
521
522
    }
}

523
524
525
526
527
528
529
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))
    {
530
531
      if (GTK_IS_WIDGET (l->data) && 
	  gtk_widget_is_ancestor (l->data, toplevel)) return TRUE;
532
533
534
535
536
    }

  return FALSE;
}

537
static void
538
539
540
gdl_edit_mode_set_selection (GladeDesignLayout *layout,
                             GladePointerMode mode,
                             GtkWidget *selection)
541
{
542
543
544
545
546
  GladeDesignLayoutPrivate *priv = layout->priv;

  if ((selection && GTK_IS_WIDGET (selection) == FALSE) ||
      gtk_bin_get_child (GTK_BIN (layout)) == selection) selection = NULL;
  
547
548
549
550
551
552
  if (priv->selection == selection) return;

  priv->selection = selection;

  if (selection)
    {
553
554
555
      if (mode == GLADE_POINTER_MARGIN_EDIT)
        {
          GtkWidget *child = gtk_bin_get_child (GTK_BIN (layout));
556

557
558
559
560
561
          /* Save initital margins to know which one where edited */
          priv->top = gtk_widget_get_margin_top (selection);
          priv->bottom = gtk_widget_get_margin_bottom (selection);
          priv->left = gtk_widget_get_margin_left (selection);
          priv->right = gtk_widget_get_margin_right (selection);
562

563
564
565
566
567
568
569
570
571
572
573
          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);
574
575
576
577
578
    }
  else
    {
      gdl_set_cursor (priv, NULL);
    }
579
580

  glade_project_set_pointer_mode (priv->project, mode);
581
582
}

583
static gboolean
584
glade_design_layout_button_press_event (GtkWidget *widget, GdkEventButton *ev)
585
{
586
  GladeDesignLayoutPrivate *priv;
587
588
  GtkAllocation child_allocation;
  Activity activity;
589
  GtkWidget *child;
590
  gint x, y;
591

592
  if (ev->button != 1 || ev->type != GDK_BUTTON_PRESS ||
593
      (child = gtk_bin_get_child (GTK_BIN (widget))) == NULL)
594
595
    return FALSE;

596
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
597
598
599
600

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

601
602
603
604
  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)
605
    {
606
      GtkWidget *selection = priv->selection;
607

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

621
622
              valign = gtk_widget_get_valign (selection);
              halign = gtk_widget_get_halign (selection);
623

624
625
626
627
628
629
630
              if (valign == GTK_ALIGN_FILL)
                top = bottom = TRUE;
              else
                {
                  top = (valign == GTK_ALIGN_START);
                  bottom = (valign == GTK_ALIGN_END);
                }
631

632
633
634
635
636
637
638
              if (halign == GTK_ALIGN_FILL)
                left = right = TRUE;
              else
                {
                  left = (halign == GTK_ALIGN_START);
                  right = (halign == GTK_ALIGN_END);
                }
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
667
668
669
670
671
672
673
              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 ();
                }
            }
674
          break;
675
676
677
678
679
680
681
682
683
684
          case ACTIVITY_MARGINS:
            priv->m_dx = x + ((priv->margin & MARGIN_LEFT) ? 
                              gtk_widget_get_margin_left (selection) :
                                gtk_widget_get_margin_right (selection) * -1);
            priv->m_dy = y + ((priv->margin & MARGIN_TOP) ?
                              gtk_widget_get_margin_top (selection) :
                                gtk_widget_get_margin_bottom (selection) * -1);

            gdl_set_cursor (priv, priv->cursors[gdl_margin_get_activity (priv->margin)]);
            return FALSE;
685
686
          break;
          default:
687
            gdl_set_cursor (priv, priv->cursors[priv->activity]);
688
689
          break;
        }
690
    }
691

692
  gtk_widget_get_allocation (child, &child_allocation);
693

694
695
  priv->dx = x - (child_allocation.x + child_allocation.width + priv->child_offset);
  priv->dy = y - (child_allocation.y + child_allocation.height + priv->child_offset);
696

697
698
699
700
701
702
  if (activity != ACTIVITY_NONE &&
      !glade_project_is_toplevel_active (priv->project, child))
    {
      _glade_design_view_freeze (priv->view);
      glade_project_selection_set (priv->project, G_OBJECT (child), TRUE);
      _glade_design_view_thaw (priv->view);
703
    }
704
705

  return FALSE;
706
707
708
}

static gboolean
709
710
glade_design_layout_button_release_event (GtkWidget *widget,
                                          GdkEventButton *ev)
711
{
712
713
  GladeDesignLayoutPrivate *priv;
  GtkWidget *child;
714

715
716
  if ((child = gtk_bin_get_child (GTK_BIN (widget))) == NULL)
    return FALSE;
717

718
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
719

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
  /* 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;

      top = gtk_widget_get_margin_top (priv->selection);
      bottom = gtk_widget_get_margin_bottom (priv->selection);
      left = gtk_widget_get_margin_left (priv->selection);
      right = gtk_widget_get_margin_right (priv->selection);

      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);
        }
754
755
756

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

766
  return FALSE;
767
768
769
}

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

779
  priv = GLADE_DESIGN_LAYOUT_PRIVATE (widget);
780

781
  *minimum = 0;
782

783
  child = gtk_bin_get_child (GTK_BIN (widget));
784

785
786
  if (child && gtk_widget_get_visible (child))
    {
787
      GtkRequisition req;
788
789
      gint height;

790
791
      gchild = glade_widget_get_from_gobject (child);
      g_assert (gchild);
792

793
      gtk_widget_get_preferred_size (child, &req, NULL);
794

795
      g_object_get (gchild, "toplevel-height", &child_height, NULL);
796

797
      child_height = MAX (child_height, req.height);
798

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

      *minimum = MAX (*minimum, PADDING + 2.5*OUTLINE_WIDTH + height + child_height);
805
806
    }

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

812
static void
813
814
glade_design_layout_get_preferred_width (GtkWidget *widget,
                                         gint *minimum, gint *natural)
815
{
816
817
818
819
820
821
822
823
824
825
826
  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))
    {
827
828
      GtkRequisition req;
      
829
830
831
      gchild = glade_widget_get_from_gobject (child);
      g_assert (gchild);

832
      gtk_widget_get_preferred_size (child, &req, NULL);
833
834
835

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

836
      child_width = MAX (child_width, req.width);
837

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

  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
  *minimum += border_width * 2;
843
  *natural = *minimum;
844
845
}

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

864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
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 */
880
881
882
883
884
  if (priv->widget_name)
    pango_layout_get_pixel_size (priv->widget_name, &width, &height);
  else
    width = height = 0;

885
886
887
888
889
890
891
892
893
894
895
896
  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;
}

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

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

912
  child = gtk_bin_get_child (GTK_BIN (widget));
913

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

920
921
      offset = gtk_container_get_border_width (GTK_CONTAINER (widget)) + PADDING + OUTLINE_WIDTH;
      priv->child_offset = offset;
922

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

936
      gtk_widget_size_allocate (child, &alloc);
937
      update_rectangles (priv, &alloc);
938
    }
939
940
}

941
942
943
944
945
946
947
948
949
950
951
952
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));
    }
}

953
954
955
static void
on_glade_widget_name_notify (GObject *gobject, GParamSpec *pspec, GladeDesignLayout *layout) 
{
956
  update_widget_name (layout, GLADE_WIDGET (gobject));
957
958
}

959
static void
960
glade_design_layout_add (GtkContainer *container, GtkWidget *widget)
961
{
962
  GladeDesignLayout *layout = GLADE_DESIGN_LAYOUT (container);
963
  GladeDesignLayoutPrivate *priv = layout->priv;
964
  GladeWidget *gchild;
965

966
967
  layout->priv->current_width = 0;
  layout->priv->current_height = 0;
968

969
  gtk_widget_set_parent_window (widget, priv->offscreen_window);
970

971
972
  GTK_CONTAINER_CLASS (glade_design_layout_parent_class)->add (container,
                                                               widget);
973

974
975
  if ((gchild = glade_widget_get_from_gobject (G_OBJECT (widget))))
    {
976
      update_widget_name (layout, gchild);
977
978
979
980
      g_signal_connect (gchild, "notify::name", G_CALLBACK (on_glade_widget_name_notify), layout);
    }
    
  gtk_widget_queue_draw (GTK_WIDGET (container)); 
981
982
983
}

static void
984
glade_design_layout_remove (GtkContainer *container, GtkWidget *widget)
985
{
986
987
988
989
990
991
  GladeWidget *gchild;

  if ((gchild = glade_widget_get_from_gobject (G_OBJECT (widget))))
    g_signal_handlers_disconnect_by_func (gchild, on_glade_widget_name_notify,
                                          GLADE_DESIGN_LAYOUT (container));

992
  GTK_CONTAINER_CLASS (glade_design_layout_parent_class)->remove (container, widget);
993
  gtk_widget_queue_draw (GTK_WIDGET (container));
994
995
}

996
997
998
999
1000
1001
1002
static gboolean
glade_design_layout_damage (GtkWidget *widget, GdkEventExpose *event)
{
  gdk_window_invalidate_rect (gtk_widget_get_window (widget), NULL, TRUE);
  return TRUE;
}

1003
static inline void
1004
1005
draw_frame (cairo_t *cr, GladeDesignLayoutPrivate *priv, gboolean selected,
            int x, int y, int w, int h)