]> git.sur5r.net Git - glabels/blob - glabels1/src/display.c
9440820d3843c3a71cc397d549cbd5c1d3b07172
[glabels] / glabels1 / src / display.c
1 /*
2  *  (GLABELS) Label and Business Card Creation program for GNOME
3  *
4  *  display.c:  GLabels Display module
5  *
6  *  Copyright (C) 2001-2002  Jim Evins <evins@snaught.com>.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21  */
22
23 #include <config.h>
24 #include <gtk/gtkinvisible.h>
25
26 #include <string.h>
27 #include <math.h>
28
29 #include "display.h"
30 #include "item.h"
31
32 #include "debug.h"
33
34 /*===========================================*/
35 /* Private globals                           */
36 /*===========================================*/
37
38 static GtkContainerClass *parent_class;
39
40 /* "CLIPBOARD" selection */
41 static GdkAtom clipboard_atom = GDK_NONE;
42
43 #define HOME_SCALE 2.0
44 static gdouble scales[] = { 8.0, 6.0, 4.0, 3.0,
45         2.0,
46         1.5, 1.0, 0.5, 0.25,
47         0.0
48 };
49
50 /*===========================================*/
51 /* Local function prototypes                 */
52 /*===========================================*/
53
54 static void gl_display_class_init (glDisplayClass * class);
55 static void gl_display_init (glDisplay * display);
56 static void gl_display_destroy (GtkObject * object);
57
58 static void gl_display_construct (glDisplay * display);
59 static GtkWidget *gl_display_construct_canvas (glDisplay * display);
60 static void gl_display_construct_selection (glDisplay * display);
61
62 static gdouble get_apropriate_scale (glLabel * label);
63
64 static void draw_rect_bg (glDisplay * display);
65 static void draw_rounded_rect_bg (glDisplay * display);
66 static void draw_round_bg (glDisplay * display);
67 static void draw_cd_bg (glDisplay * display);
68
69 static int canvas_event (GnomeCanvas * canvas,
70                          GdkEvent * event, gpointer data);
71 static int canvas_event_arrow_mode (GnomeCanvas * canvas,
72                                     GdkEvent * event, gpointer data);
73
74 static GnomeCanvasItem *display_item_at (glDisplay * display,
75                                          gdouble x, gdouble y);
76 static gboolean item_selected (glDisplay * display,
77                                GnomeCanvasItem * item);
78 static gboolean multiple_items_selected (glDisplay * display);
79
80 static int item_event_arrow_mode (GnomeCanvasItem * item,
81                                   GdkEvent * event, gpointer data);
82
83 static void popup_selection_menu (glDisplay * display,
84                                   GnomeCanvasItem * item, GdkEvent * event);
85
86 static void delete_item_cb (GtkWidget * widget, GnomeCanvasItem * item);
87 static void raise_item_cb (GtkWidget * widget, GnomeCanvasItem * item);
88 static void lower_item_cb (GtkWidget * widget, GnomeCanvasItem * item);
89
90 static void move_selected_items (glDisplay * display, gdouble dx, gdouble dy);
91 static void move_item (GnomeCanvasItem * item, gdouble dx, gdouble dy);
92
93 static void delete_selection_cb (GtkWidget * widget, glDisplay * display);
94 static void raise_selection_cb (GtkWidget * widget, glDisplay * display);
95 static void lower_selection_cb (GtkWidget * widget, glDisplay * display);
96
97 static void selection_clear_cb (GtkWidget * widget,
98                                 GdkEventSelection * event, gpointer data);
99
100 static void selection_get_cb (GtkWidget * widget,
101                               GtkSelectionData * selection_data, guint info,
102                               guint time, gpointer data);
103
104 static void selection_received_cb (GtkWidget * widget,
105                                    GtkSelectionData * selection_data,
106                                    guint time, gpointer data);
107 \f
108 /****************************************************************************/
109 /* Boilerplate Object stuff.                                                */
110 /****************************************************************************/
111 guint
112 gl_display_get_type (void)
113 {
114         static guint display_type = 0;
115
116         if (!display_type) {
117                 GtkTypeInfo display_info = {
118                         "glDisplay",
119                         sizeof (glDisplay),
120                         sizeof (glDisplayClass),
121                         (GtkClassInitFunc) gl_display_class_init,
122                         (GtkObjectInitFunc) gl_display_init,
123                         (GtkArgSetFunc) NULL,
124                         (GtkArgGetFunc) NULL,
125                 };
126
127                 display_type =
128                     gtk_type_unique (gtk_vbox_get_type (), &display_info);
129         }
130
131         return display_type;
132 }
133
134 static void
135 gl_display_class_init (glDisplayClass * class)
136 {
137         GtkObjectClass *object_class;
138         GtkWidgetClass *widget_class;
139
140         object_class = (GtkObjectClass *) class;
141         widget_class = (GtkWidgetClass *) class;
142
143         parent_class = gtk_type_class (gtk_vbox_get_type ());
144
145         object_class->destroy = gl_display_destroy;
146 }
147
148 static void
149 gl_display_init (glDisplay * display)
150 {
151         display->label = NULL;
152 }
153
154 static void
155 gl_display_destroy (GtkObject * object)
156 {
157         glDisplay *display;
158         glDisplayClass *class;
159
160         g_return_if_fail (object != NULL);
161         g_return_if_fail (GL_IS_DISPLAY (object));
162
163         display = GL_DISPLAY (object);
164         class = GL_DISPLAY_CLASS (GTK_OBJECT (display)->klass);
165
166         display->label = NULL;
167
168         GTK_OBJECT_CLASS (parent_class)->destroy (object);
169 }
170
171 GtkWidget *
172 gl_display_new (glLabel * label)
173 {
174         glDisplay *display = gtk_type_new (gl_display_get_type ());
175
176         display->label = label;
177
178         gl_display_construct (display);
179
180         gl_display_clear_modified (display);
181
182         return GTK_WIDGET (display);
183 }
184
185 /*---------------------------------------------------------------------------*/
186 /* PRIVATE.  Construct composite widget.                                     */
187 /*---------------------------------------------------------------------------*/
188 static void
189 gl_display_construct (glDisplay * display)
190 {
191         GtkWidget *wvbox, *wscroll;
192
193         g_return_if_fail (GL_IS_DISPLAY (display));
194
195         wvbox = GTK_WIDGET (display);
196
197         display->state = GL_DISPLAY_STATE_ARROW;
198         display->item_list = NULL;
199
200         gl_display_construct_canvas (display);
201         wscroll = gtk_scrolled_window_new (NULL, NULL);
202         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (wscroll),
203                                         GTK_POLICY_AUTOMATIC,
204                                         GTK_POLICY_AUTOMATIC);
205         gtk_box_pack_start (GTK_BOX (wvbox), wscroll, TRUE, TRUE, 0);
206         gtk_container_add (GTK_CONTAINER (wscroll), display->canvas);
207
208         gl_display_construct_selection (display);
209
210         display->menu = gl_display_new_selection_menu (display);
211
212         display->modified = FALSE;
213 }
214
215 /*---------------------------------------------------------------------------*/
216 /* PRIVATE.  Create canvas w/ a background in the shape of the label/card.   */
217 /*---------------------------------------------------------------------------*/
218 static GtkWidget *
219 gl_display_construct_canvas (glDisplay * display)
220 {
221         gdouble scale;
222         glLabel *label = display->label;
223         GList *p_obj;
224         glLabelObject *object;
225         GnomeCanvasItem *item;
226
227         g_return_val_if_fail (GL_IS_DISPLAY (display), NULL);
228         g_return_val_if_fail (label != NULL, NULL);
229
230 #ifdef AA_CANVAS
231         gtk_widget_push_visual (gdk_rgb_get_visual ());
232         gtk_widget_push_colormap (gdk_rgb_get_cmap ());
233         display->canvas = gnome_canvas_new_aa ();
234         gtk_widget_pop_colormap ();
235         gtk_widget_pop_visual ();
236 #else
237         gtk_widget_push_visual (gdk_imlib_get_visual ());
238         gtk_widget_push_colormap (gdk_imlib_get_colormap ());
239         display->canvas = gnome_canvas_new ();
240         gtk_widget_pop_colormap ();
241         gtk_widget_pop_visual ();
242 #endif
243
244         scale = get_apropriate_scale (label);
245
246         gtk_widget_set_usize (display->canvas,
247                               scale * label->width + 40,
248                               scale * label->height + 40);
249         gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (display->canvas),
250                                           scale);
251         display->scale = scale;
252
253         gnome_canvas_set_scroll_region (GNOME_CANVAS (display->canvas),
254                                         0.0, 0.0, label->width, label->height);
255
256         /* Draw background shape of label/card */
257         switch (label->template->style) {
258
259         case GL_TEMPLATE_STYLE_RECT:
260                 if (label->template->label_round == 0.0) {
261                         /* Square corners. */
262                         draw_rect_bg (display);
263                 } else {
264                         /* Rounded corners. */
265                         draw_rounded_rect_bg (display);
266                 }
267                 break;
268
269         case GL_TEMPLATE_STYLE_ROUND:
270                 draw_round_bg (display);
271                 break;
272
273         case GL_TEMPLATE_STYLE_CD:
274                 draw_cd_bg (display);
275                 break;
276
277         default:
278                 WARN ("Unknown template label style");
279                 break;
280         }
281
282         gtk_signal_connect (GTK_OBJECT (display->canvas), "event",
283                             GTK_SIGNAL_FUNC (canvas_event), display);
284
285         for (p_obj = label->objects; p_obj != NULL; p_obj = p_obj->next) {
286                 object = (glLabelObject *) p_obj->data;
287                 item = gl_item_new (object, display);
288                 gl_display_add_item (display, item);
289         }
290
291         return display->canvas;
292 }
293
294 /*---------------------------------------------------------------------------*/
295 /* PRIVATE.  Create selection targets.                                       */
296 /*---------------------------------------------------------------------------*/
297 static void
298 gl_display_construct_selection (glDisplay * display)
299 {
300         g_return_if_fail (GL_IS_DISPLAY (display));
301
302         display->have_selection = FALSE;
303         display->selection_data = NULL;
304         display->invisible = gtk_invisible_new ();
305
306         display->selected_item_list = NULL;
307
308         if (!clipboard_atom) {
309                 clipboard_atom = gdk_atom_intern ("GLABELS_CLIPBOARD", FALSE);
310         }
311
312         gtk_selection_add_target (display->invisible,
313                                   clipboard_atom, GDK_SELECTION_TYPE_STRING, 1);
314
315         gtk_signal_connect (GTK_OBJECT (display->invisible),
316                             "selection_clear_event",
317                             GTK_SIGNAL_FUNC (selection_clear_cb), display);
318
319         gtk_signal_connect (GTK_OBJECT (display->invisible), "selection_get",
320                             GTK_SIGNAL_FUNC (selection_get_cb), display);
321
322         gtk_signal_connect (GTK_OBJECT (display->invisible),
323                             "selection_received",
324                             GTK_SIGNAL_FUNC (selection_received_cb), display);
325
326 }
327
328 /*---------------------------------------------------------------------------*/
329 /* PRIVATE.  Determine an apropriate scale for given label & screen size     */
330 /*---------------------------------------------------------------------------*/
331 static gdouble
332 get_apropriate_scale (glLabel * label)
333 {
334         gdouble w, h;
335         gdouble w_screen, h_screen;
336         gint i;
337         gdouble k;
338
339         g_return_val_if_fail (label != NULL, 1.0);
340
341         w = label->width;
342         h = label->height;
343         w_screen = (gdouble) gdk_screen_width ();
344         h_screen = (gdouble) gdk_screen_height ();
345
346         for (i = 0; scales[i] > 0.0; i++) {
347                 k = scales[i];
348                 if (k <= HOME_SCALE) {
349                         if ((k * w < (w_screen - 256))
350                             && (k * h < (h_screen - 256)))
351                                 return k;
352                 }
353         }
354
355         return 0.25;
356 }
357
358 /*---------------------------------------------------------------------------*/
359 /* PRIVATE.  Draw simple recangular background.                              */
360 /*---------------------------------------------------------------------------*/
361 static void
362 draw_rect_bg (glDisplay * display)
363 {
364         glLabel *label = display->label;
365         GnomeCanvasItem *item;
366         GnomeCanvasGroup *group;
367         gdouble margin;
368
369         g_return_if_fail (GL_IS_DISPLAY (display));
370         g_return_if_fail (label != NULL);
371
372         display->n_bg_items = 0;
373         display->bg_item_list = NULL;
374
375         group = gnome_canvas_root (GNOME_CANVAS (display->canvas));
376
377         item = gnome_canvas_item_new (group,
378                                       gnome_canvas_rect_get_type (),
379                                       "x1", 0.0,
380                                       "y1", 0.0,
381                                       "x2", label->width,
382                                       "y2", label->height,
383                                       "fill_color", "white",
384                                       NULL);
385         display->n_bg_items++;
386         display->bg_item_list = g_list_append (display->bg_item_list, item);
387
388         /* Bounding box @ margin */
389         margin = label->template->label_margin;
390         gnome_canvas_item_new (group,
391                                gnome_canvas_rect_get_type (),
392                                "x1", margin,
393                                "y1", margin,
394                                "x2", label->width - margin,
395                                "y2", label->height - margin,
396                                "width_pixels", 1,
397                                "outline_color", "light blue",
398                                NULL);
399         display->n_bg_items++;
400         display->bg_item_list = g_list_append (display->bg_item_list, item);
401 }
402
403 /*---------------------------------------------------------------------------*/
404 /* PRIVATE.  Draw rounded recangular background.                             */
405 /*---------------------------------------------------------------------------*/
406 static void
407 draw_rounded_rect_bg (glDisplay * display)
408 {
409         glLabel *label = display->label;
410         GnomeCanvasPoints *points;
411         gint i_coords, i_theta;
412         gdouble r, w, h, m;
413         GnomeCanvasItem *item;
414         GnomeCanvasGroup *group;
415
416         g_return_if_fail (GL_IS_DISPLAY (display));
417         g_return_if_fail (label != NULL);
418
419         group = gnome_canvas_root (GNOME_CANVAS (display->canvas));
420
421         display->n_bg_items = 0;
422         display->bg_item_list = NULL;
423
424         r = label->template->label_round;
425         w = label->width;
426         h = label->height;
427         m = label->template->label_margin;
428
429         points = gnome_canvas_points_new (4 * (1 + 90 / 5));
430         i_coords = 0;
431         for (i_theta = 0; i_theta <= 90; i_theta += 5) {
432                 points->coords[i_coords++] =
433                     r - r * sin (i_theta * M_PI / 180.0);
434                 points->coords[i_coords++] =
435                     r - r * cos (i_theta * M_PI / 180.0);
436         }
437         for (i_theta = 0; i_theta <= 90; i_theta += 5) {
438                 points->coords[i_coords++] =
439                     r - r * cos (i_theta * M_PI / 180.0);
440                 points->coords[i_coords++] =
441                     (h - r) + r * sin (i_theta * M_PI / 180.0);
442         }
443         for (i_theta = 0; i_theta <= 90; i_theta += 5) {
444                 points->coords[i_coords++] =
445                     (w - r) + r * sin (i_theta * M_PI / 180.0);
446                 points->coords[i_coords++] =
447                     (h - r) + r * cos (i_theta * M_PI / 180.0);
448         }
449         for (i_theta = 0; i_theta <= 90; i_theta += 5) {
450                 points->coords[i_coords++] =
451                     (w - r) + r * cos (i_theta * M_PI / 180.0);
452                 points->coords[i_coords++] =
453                     r - r * sin (i_theta * M_PI / 180.0);
454         }
455         item = gnome_canvas_item_new (group,
456                                       gnome_canvas_polygon_get_type (),
457                                       "points", points,
458                                       "fill_color", "white",
459                                       NULL);
460         gnome_canvas_points_free (points);
461         display->n_bg_items++;
462         display->bg_item_list = g_list_append (display->bg_item_list, item);
463
464         /* Bounding box @ margin */
465         if (label->template->label_margin >= label->template->label_round) {
466                 /* simple rectangle */
467                 item = gnome_canvas_item_new (group,
468                                               gnome_canvas_rect_get_type (),
469                                               "x1", m,
470                                               "y1", m,
471                                               "x2", w - m,
472                                               "y2", h - m,
473                                               "width_pixels", 1,
474                                               "outline_color", "light blue",
475                                               NULL);
476                 display->n_bg_items++;
477                 display->bg_item_list =
478                     g_list_append (display->bg_item_list, item);
479         } else {
480                 r = label->template->label_round - m;
481                 w = label->width - 2 * label->template->label_margin;
482                 h = label->height - 2 * label->template->label_margin;
483
484                 /* rectangle with rounded corners */
485                 points = gnome_canvas_points_new (4 * (1 + 90 / 5));
486                 i_coords = 0;
487                 for (i_theta = 0; i_theta <= 90; i_theta += 5) {
488                         points->coords[i_coords++] =
489                             m + r - r * sin (i_theta * M_PI / 180.0);
490                         points->coords[i_coords++] =
491                             m + r - r * cos (i_theta * M_PI / 180.0);
492                 }
493                 for (i_theta = 0; i_theta <= 90; i_theta += 5) {
494                         points->coords[i_coords++] =
495                             m + r - r * cos (i_theta * M_PI / 180.0);
496                         points->coords[i_coords++] =
497                             m + (h - r) + r * sin (i_theta * M_PI / 180.0);
498                 }
499                 for (i_theta = 0; i_theta <= 90; i_theta += 5) {
500                         points->coords[i_coords++] =
501                             m + (w - r) + r * sin (i_theta * M_PI / 180.0);
502                         points->coords[i_coords++] =
503                             m + (h - r) + r * cos (i_theta * M_PI / 180.0);
504                 }
505                 for (i_theta = 0; i_theta <= 90; i_theta += 5) {
506                         points->coords[i_coords++] =
507                             m + (w - r) + r * cos (i_theta * M_PI / 180.0);
508                         points->coords[i_coords++] =
509                             m + r - r * sin (i_theta * M_PI / 180.0);
510                 }
511                 item = gnome_canvas_item_new (group,
512                                               gnome_canvas_polygon_get_type (),
513                                               "points", points,
514                                               "width_pixels", 1,
515                                               "outline_color", "light blue",
516                                               NULL);
517                 gnome_canvas_points_free (points);
518                 display->n_bg_items++;
519                 display->bg_item_list =
520                     g_list_append (display->bg_item_list, item);
521         }
522 }
523
524 /*---------------------------------------------------------------------------*/
525 /* PRIVATE.  Draw round background.                                          */
526 /*---------------------------------------------------------------------------*/
527 static void
528 draw_round_bg (glDisplay * display)
529 {
530         glLabel *label = display->label;
531         GnomeCanvasPoints *points;
532         gint i_coords, i_theta;
533         gdouble r, r1;
534         GnomeCanvasItem *item;
535         GnomeCanvasGroup *group;
536
537         g_return_if_fail (GL_IS_DISPLAY (display));
538         g_return_if_fail (label != NULL);
539
540         group = gnome_canvas_root (GNOME_CANVAS (display->canvas));
541
542         display->n_bg_items = 0;
543         display->bg_item_list = NULL;
544
545         r1 = label->template->label_radius;
546         points = gnome_canvas_points_new (1 + 360/2);
547         i_coords = 0;
548         for (i_theta = 0; i_theta <= 360; i_theta += 2) {
549                 points->coords[i_coords++] =
550                     r1 - r1 * sin (i_theta * M_PI / 180.0);
551                 points->coords[i_coords++] =
552                     r1 - r1 * cos (i_theta * M_PI / 180.0);
553         }
554         item = gnome_canvas_item_new (group,
555                                       gnome_canvas_polygon_get_type (),
556                                       "points", points,
557                                       "fill_color", "white",
558                                       NULL);
559         gnome_canvas_points_free (points);
560         display->n_bg_items++;
561         display->bg_item_list = g_list_append (display->bg_item_list, item);
562
563         /* Bounding box @ margin */
564         r = label->template->label_radius - label->template->label_margin;
565         points = gnome_canvas_points_new (360 / 2);
566         i_coords = 0;
567         for (i_theta = 0; i_theta < 360; i_theta += 2) {
568                 points->coords[i_coords++] =
569                     r1 - r * sin (i_theta * M_PI / 180.0);
570                 points->coords[i_coords++] =
571                     r1 - r * cos (i_theta * M_PI / 180.0);
572         }
573         item = gnome_canvas_item_new (group,
574                                       gnome_canvas_polygon_get_type (),
575                                       "points", points,
576                                       "width_pixels", 1,
577                                       "outline_color", "light blue", NULL);
578         gnome_canvas_points_free (points);
579         display->n_bg_items++;
580         display->bg_item_list = g_list_append (display->bg_item_list, item);
581 }
582
583 /*---------------------------------------------------------------------------*/
584 /* PRIVATE.  Draw CD style background, circular w/ concentric hole.          */
585 /*---------------------------------------------------------------------------*/
586 static void
587 draw_cd_bg (glDisplay * display)
588 {
589         glLabel *label = display->label;
590         GnomeCanvasPoints *points;
591         gint i_coords, i_theta;
592         gdouble r, r1, r2;
593         GnomeCanvasItem *item;
594         GnomeCanvasGroup *group;
595
596         g_return_if_fail (GL_IS_DISPLAY (display));
597         g_return_if_fail (label != NULL);
598
599         group = gnome_canvas_root (GNOME_CANVAS (display->canvas));
600
601         display->n_bg_items = 0;
602         display->bg_item_list = NULL;
603
604         r1 = label->template->label_radius;
605         r2 = label->template->label_hole;
606         points = gnome_canvas_points_new (2 * (1 + 360 / 2));
607         i_coords = 0;
608         for (i_theta = 0; i_theta <= 360; i_theta += 2) {
609                 points->coords[i_coords++] =
610                     r1 - r1 * sin (i_theta * M_PI / 180.0);
611                 points->coords[i_coords++] =
612                     r1 - r1 * cos (i_theta * M_PI / 180.0);
613         }
614         for (i_theta = 0; i_theta <= 360; i_theta += 2) {
615                 points->coords[i_coords++] =
616                     r1 - r2 * sin (i_theta * M_PI / 180.0);
617                 points->coords[i_coords++] =
618                     r1 - r2 * cos (i_theta * M_PI / 180.0);
619         }
620         item = gnome_canvas_item_new (group,
621                                       gnome_canvas_polygon_get_type (),
622                                       "points", points,
623                                       "fill_color", "white",
624                                       NULL);
625         gnome_canvas_points_free (points);
626         display->n_bg_items++;
627         display->bg_item_list = g_list_append (display->bg_item_list, item);
628
629         /* Bounding box @ margin */
630         /* outer margin */
631         r = label->template->label_radius - label->template->label_margin;
632         points = gnome_canvas_points_new (360 / 2);
633         i_coords = 0;
634         for (i_theta = 0; i_theta < 360; i_theta += 2) {
635                 points->coords[i_coords++] =
636                     r1 - r * sin (i_theta * M_PI / 180.0);
637                 points->coords[i_coords++] =
638                     r1 - r * cos (i_theta * M_PI / 180.0);
639         }
640         item = gnome_canvas_item_new (group,
641                                       gnome_canvas_polygon_get_type (),
642                                       "points", points,
643                                       "width_pixels", 1,
644                                       "outline_color", "light blue", NULL);
645         gnome_canvas_points_free (points);
646         display->n_bg_items++;
647         display->bg_item_list = g_list_append (display->bg_item_list, item);
648
649         /* inner margin */
650         r = label->template->label_hole + label->template->label_margin;
651         points = gnome_canvas_points_new (360 / 2);
652         i_coords = 0;
653         for (i_theta = 0; i_theta < 360; i_theta += 2) {
654                 points->coords[i_coords++] =
655                     r1 - r * sin (i_theta * M_PI / 180.0);
656                 points->coords[i_coords++] =
657                     r1 - r * cos (i_theta * M_PI / 180.0);
658         }
659         item = gnome_canvas_item_new (group,
660                                       gnome_canvas_polygon_get_type (),
661                                       "points", points,
662                                       "width_pixels", 1,
663                                       "outline_color", "light blue",
664                                       NULL);
665         gnome_canvas_points_free (points);
666         display->n_bg_items++;
667         display->bg_item_list = g_list_append (display->bg_item_list, item);
668 }
669
670 /*****************************************************************************/
671 /* Set arrow mode.                                                           */
672 /*****************************************************************************/
673 void
674 gl_display_arrow_mode (glDisplay * display)
675 {
676         static GdkCursor *cursor = NULL;
677
678         g_return_if_fail (GL_IS_DISPLAY (display));
679
680         if (!cursor) {
681                 cursor = gdk_cursor_new (GDK_LEFT_PTR);
682         }
683
684         gdk_window_set_cursor (display->canvas->window, cursor);
685
686         display->state = GL_DISPLAY_STATE_ARROW;
687 }
688
689 /*****************************************************************************/
690 /* Set create text object mode.                                              */
691 /*****************************************************************************/
692 void
693 gl_display_object_create_mode (glDisplay * display,
694                                glLabelObjectType type)
695 {
696         GdkCursor *cursor;
697
698         g_return_if_fail (GL_IS_DISPLAY (display));
699
700         cursor = gl_item_get_create_cursor (type);
701         gdk_window_set_cursor (display->canvas->window, cursor);
702
703         display->state = GL_DISPLAY_STATE_OBJECT_CREATE;
704         display->create_type = type;
705 }
706
707 /*****************************************************************************/
708 /* Add canvas item to list of display items.                                 */
709 /*****************************************************************************/
710 void
711 gl_display_add_item (glDisplay * display,
712                      GnomeCanvasItem * item)
713 {
714         g_return_if_fail (GL_IS_DISPLAY (display));
715
716         display->item_list = g_list_prepend (display->item_list, item);
717 }
718
719 /*****************************************************************************/
720 /* Select all items.                                                         */
721 /*****************************************************************************/
722 void
723 gl_display_select_all (glDisplay * display)
724 {
725         GList *p;
726
727         g_return_if_fail (GL_IS_DISPLAY (display));
728
729         gl_display_unselect_all (display);
730
731         for (p = display->item_list; p != NULL; p = p->next) {
732                 gl_display_select_item (display, GNOME_CANVAS_ITEM (p->data));
733         }
734 }
735
736 /*****************************************************************************/
737 /* Select all items within given rectangular region                          */
738 /*****************************************************************************/
739 void
740 gl_display_select_region (glDisplay * display,
741                           gdouble x1,
742                           gdouble y1,
743                           gdouble x2,
744                           gdouble y2)
745 {
746         GList *p;
747         GnomeCanvasItem *item;
748         gdouble i_x1, i_y1, i_x2, i_y2;
749
750         g_return_if_fail (GL_IS_DISPLAY (display));
751         g_return_if_fail ((x1 <= x2) && (y1 <= y2));
752
753         for (p = display->item_list; p != NULL; p = p->next) {
754                 item = GNOME_CANVAS_ITEM (p->data);
755                 if (!item_selected (display, item)) {
756
757                         gl_item_get_bounds (item, &i_x1, &i_y1, &i_x2, &i_y2);
758                         if ((i_x1 >= x1) && (i_x2 <= x2) && (i_y1 >= y1)
759                             && (i_y2 <= y2)) {
760                                 gl_display_select_item (display, item);
761                         }
762
763                 }
764         }
765 }
766
767 /*****************************************************************************/
768 /* Remove all selections                                                     */
769 /*****************************************************************************/
770 void
771 gl_display_unselect_all (glDisplay * display)
772 {
773         GList *p, *p_next;
774
775         g_return_if_fail (GL_IS_DISPLAY (display));
776
777         for (p = display->selected_item_list; p != NULL; p = p_next) {
778                 p_next = p->next;
779                 gl_display_unselect_item (display, GNOME_CANVAS_ITEM (p->data));
780         }
781 }
782
783 /*****************************************************************************/
784 /* Select an item.                                                           */
785 /*****************************************************************************/
786 void
787 gl_display_select_item (glDisplay * display,
788                         GnomeCanvasItem * item)
789 {
790         g_return_if_fail (GL_IS_DISPLAY (display));
791         g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
792
793         if (!item_selected (display, item)) {
794                 display->selected_item_list =
795                     g_list_prepend (display->selected_item_list, item);
796         }
797         gl_item_highlight (item);
798         gtk_widget_grab_focus (GTK_WIDGET (display->canvas));
799 }
800
801 /*****************************************************************************/
802 /* Un-select items.                                                          */
803 /*****************************************************************************/
804 void
805 gl_display_unselect_item (glDisplay * display,
806                           GnomeCanvasItem * item)
807 {
808         g_return_if_fail (GL_IS_DISPLAY (display));
809         g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
810
811         gl_item_unhighlight (item);
812
813         display->selected_item_list =
814             g_list_remove (display->selected_item_list, item);
815 }
816
817 /*****************************************************************************/
818 /* Has display been modified?                                                */
819 /*****************************************************************************/
820 gboolean
821 gl_display_modified (glDisplay * display)
822 {
823         g_return_val_if_fail (GL_IS_DISPLAY (display), FALSE);
824
825         return display->modified;
826 }
827
828 /*****************************************************************************/
829 /* Set to modified state.                                                    */
830 /*****************************************************************************/
831 void
832 gl_display_set_modified (glDisplay * display)
833 {
834         g_return_if_fail (GL_IS_DISPLAY (display));
835
836         display->modified = TRUE;
837 }
838
839 /*****************************************************************************/
840 /* Reset to un-modified state.                                               */
841 /*****************************************************************************/
842 void
843 gl_display_clear_modified (glDisplay * display)
844 {
845         g_return_if_fail (GL_IS_DISPLAY (display));
846
847         display->modified = FALSE;
848 }
849
850 /*****************************************************************************/
851 /* "Cut" selected items and place in clipboard selections.                   */
852 /*****************************************************************************/
853 void
854 gl_display_cut (glDisplay * display)
855 {
856         g_return_if_fail (GL_IS_DISPLAY (display));
857
858         gl_display_copy (display);
859         delete_selection_cb (GTK_WIDGET (display), display);
860 }
861
862 /*****************************************************************************/
863 /* "Copy" selected items to clipboard selections.                            */
864 /*****************************************************************************/
865 void
866 gl_display_copy (glDisplay * display)
867 {
868         GList *p;
869         GnomeCanvasItem *item;
870         glLabelObject *object;
871
872         g_return_if_fail (GL_IS_DISPLAY (display));
873
874         if (display->selected_item_list) {
875
876                 gl_label_free (&display->selection_data);
877                 display->selection_data =
878                     gl_label_new_with_template (display->label->
879                                                 template_name,
880                                                 display->label->rotate_flag);
881
882                 for (p = display->selected_item_list; p != NULL; p = p->next) {
883
884                         item = GNOME_CANVAS_ITEM (p->data);
885                         object = gl_item_get_object (item);
886                         gl_label_object_new_from_object (display->
887                                                          selection_data,
888                                                          object);
889
890                 }
891
892                 gtk_selection_owner_set (display->invisible,
893                                          clipboard_atom, GDK_CURRENT_TIME);
894                 display->have_selection = TRUE;
895
896         }
897 }
898
899 /*****************************************************************************/
900 /* "Paste" from private clipboard selection.                                 */
901 /*****************************************************************************/
902 void
903 gl_display_paste (glDisplay * display)
904 {
905         g_return_if_fail (GL_IS_DISPLAY (display));
906
907         gtk_selection_convert (GTK_WIDGET (display->invisible),
908                                clipboard_atom, GDK_SELECTION_TYPE_STRING,
909                                GDK_CURRENT_TIME);
910 }
911
912 /*---------------------------------------------------------------------------*/
913 /* PRIVATE.  Canvas event handler.                                           */
914 /*---------------------------------------------------------------------------*/
915 static int
916 canvas_event (GnomeCanvas * canvas,
917               GdkEvent * event,
918               gpointer data)
919 {
920         glDisplay *display = GL_DISPLAY (data);
921
922         switch (display->state) {
923
924         case GL_DISPLAY_STATE_ARROW:
925                 return canvas_event_arrow_mode (canvas, event, data);
926
927         case GL_DISPLAY_STATE_OBJECT_CREATE:
928                 return gl_item_create_event_handler (canvas, event, data);
929
930         default:
931                 WARN ("Invalid display state.");        /* Should not happen! */
932                 return FALSE;
933
934         }
935
936 }
937
938 /*---------------------------------------------------------------------------*/
939 /* PRIVATE.  Canvas event handler (arrow mode)                               */
940 /*---------------------------------------------------------------------------*/
941 static int
942 canvas_event_arrow_mode (GnomeCanvas * canvas,
943                          GdkEvent * event,
944                          gpointer data)
945 {
946         static gdouble x0, y0;
947         static gboolean dragging = FALSE;
948         static GnomeCanvasItem *item;
949         glDisplay *display = GL_DISPLAY (data);
950         gdouble x, y, x1, y1, x2, y2;
951         GnomeCanvasGroup *group;
952         GdkCursor *cursor;
953
954         switch (event->type) {
955
956         case GDK_BUTTON_PRESS:
957                 switch (event->button.button) {
958                 case 1:
959                         gnome_canvas_window_to_world (canvas,
960                                                       event->button.x,
961                                                       event->button.y, &x, &y);
962
963                         if (display_item_at (display, x, y) == NULL) {
964                                 if (!(event->button.state & GDK_CONTROL_MASK)) {
965                                         gl_display_unselect_all (display);
966                                 }
967
968                                 dragging = TRUE;
969                                 gdk_pointer_grab (GTK_WIDGET (display->canvas)->
970                                                   window, FALSE,
971                                                   GDK_POINTER_MOTION_MASK |
972                                                   GDK_BUTTON_RELEASE_MASK |
973                                                   GDK_BUTTON_PRESS_MASK, NULL,
974                                                   NULL, event->button.time);
975                                 group =
976                                     gnome_canvas_root (GNOME_CANVAS
977                                                        (display->canvas));
978                                 item =
979                                     gnome_canvas_item_new (group,
980                                                            gnome_canvas_rect_get_type (),
981                                                            "x1", x, "y1", y,
982                                                            "x2", x, "y2", y,
983                                                            "width_pixels", 2,
984                                                            "outline_color_rgba",
985                                                            GNOME_CANVAS_COLOR_A
986                                                            (0, 0, 255, 128),
987                                                            NULL);
988                                 x0 = x;
989                                 y0 = y;
990
991                         }
992                         return FALSE;
993
994                 default:
995                         return FALSE;
996                 }
997
998         case GDK_BUTTON_RELEASE:
999                 switch (event->button.button) {
1000                 case 1:
1001                         if (dragging) {
1002                                 dragging = FALSE;
1003                                 gdk_pointer_ungrab (event->button.time);
1004                                 gnome_canvas_window_to_world (canvas,
1005                                                               event->button.x,
1006                                                               event->button.y,
1007                                                               &x, &y);
1008                                 x1 = MIN (x, x0);
1009                                 y1 = MIN (y, y0);
1010                                 x2 = MAX (x, x0);
1011                                 y2 = MAX (y, y0);
1012                                 gl_display_select_region (display, x1, y1, x2,
1013                                                           y2);
1014                                 gtk_object_destroy (GTK_OBJECT (item));
1015                                 return TRUE;
1016                         }
1017                         return FALSE;
1018
1019                 default:
1020                         return FALSE;
1021                 }
1022
1023         case GDK_MOTION_NOTIFY:
1024                 if (dragging && (event->motion.state & GDK_BUTTON1_MASK)) {
1025                         gnome_canvas_window_to_world (canvas,
1026                                                       event->motion.x,
1027                                                       event->motion.y, &x, &y);
1028
1029                         gnome_canvas_item_set (item,
1030                                                "x1", MIN (x, x0),
1031                                                "y1", MIN (y, y0),
1032                                                "x2", MAX (x, x0),
1033                                                "y2", MAX (y, y0), NULL);
1034                         return TRUE;
1035                 } else {
1036                         return FALSE;
1037                 }
1038
1039         case GDK_KEY_PRESS:
1040                 if (!dragging) {
1041                         switch (event->key.keyval) {
1042                         case GDK_Left:
1043                         case GDK_KP_Left:
1044                                 move_selected_items (display,
1045                                                      -1.0 / (display->scale),
1046                                                      0.0);
1047                                 break;
1048                         case GDK_Up:
1049                         case GDK_KP_Up:
1050                                 move_selected_items (display, 0.0,
1051                                                      -1.0 / (display->scale));
1052                                 break;
1053                         case GDK_Right:
1054                         case GDK_KP_Right:
1055                                 move_selected_items (display,
1056                                                      1.0 / (display->scale),
1057                                                      0.0);
1058                                 break;
1059                         case GDK_Down:
1060                         case GDK_KP_Down:
1061                                 move_selected_items (display, 0.0,
1062                                                      1.0 / (display->scale));
1063                                 break;
1064                         case GDK_Delete:
1065                         case GDK_KP_Delete:
1066                                 delete_selection_cb (GTK_WIDGET (canvas),
1067                                                      display);
1068                                 cursor = gdk_cursor_new (GDK_LEFT_PTR);
1069                                 gdk_window_set_cursor (display->canvas->window,
1070                                                        cursor);
1071                                 gdk_cursor_destroy (cursor);
1072                                 break;
1073                         default:
1074                                 return FALSE;
1075                         }
1076                 }
1077                 return TRUE;    /* We handled this or we were dragging. */
1078
1079         default:
1080                 return FALSE;
1081         }
1082
1083 }
1084
1085 /*****************************************************************************/
1086 /* Item event handler.                                                       */
1087 /*****************************************************************************/
1088 gint
1089 gl_display_item_event_handler (GnomeCanvasItem * item,
1090                                GdkEvent * event,
1091                                gpointer data)
1092 {
1093         glDisplay *display = GL_DISPLAY (data);
1094
1095         switch (display->state) {
1096
1097         case GL_DISPLAY_STATE_ARROW:
1098                 return item_event_arrow_mode (item, event, data);
1099
1100         default:
1101                 return FALSE;
1102
1103         }
1104
1105 }
1106
1107 /*---------------------------------------------------------------------------*/
1108 /* PRIVATE.  Item event handler (arrow mode)                                 */
1109 /*---------------------------------------------------------------------------*/
1110 static int
1111 item_event_arrow_mode (GnomeCanvasItem * item,
1112                        GdkEvent * event,
1113                        gpointer data)
1114 {
1115         glDisplay *display = GL_DISPLAY (data);
1116         static gdouble x, y;
1117         static gboolean dragging = FALSE;
1118         GdkCursor *cursor;
1119         gdouble item_x, item_y;
1120         gdouble new_x, new_y;
1121         gboolean control_key_pressed;
1122
1123         item_x = event->button.x;
1124         item_y = event->button.y;
1125         gnome_canvas_item_w2i (item->parent, &item_x, &item_y);
1126
1127         switch (event->type) {
1128
1129         case GDK_BUTTON_PRESS:
1130                 control_key_pressed = event->button.state & GDK_CONTROL_MASK;
1131                 switch (event->button.button) {
1132                 case 1:
1133                         if (control_key_pressed) {
1134                                 if (item_selected (display, item)) {
1135                                         /* Un-selecting an already selected item */
1136                                         gl_display_unselect_item (display,
1137                                                                   item);
1138                                         return TRUE;
1139                                 } else {
1140                                         /* Add to current selection */
1141                                         gl_display_select_item (display, item);
1142                                 }
1143                         } else {
1144                                 if (!item_selected (display, item)) {
1145                                         /* No control, key so remove any selections before adding */
1146                                         gl_display_unselect_all (display);
1147                                         /* Add to current selection */
1148                                         gl_display_select_item (display, item);
1149                                 }
1150                         }
1151                         /* Go into dragging mode while button remains pressed. */
1152                         x = item_x;
1153                         y = item_y;
1154                         cursor = gdk_cursor_new (GDK_FLEUR);
1155                         gnome_canvas_item_grab (item,
1156                                                 GDK_POINTER_MOTION_MASK |
1157                                                 GDK_BUTTON_RELEASE_MASK |
1158                                                 GDK_BUTTON_PRESS_MASK,
1159                                                 cursor, event->button.time);
1160                         gdk_cursor_destroy (cursor);
1161                         dragging = TRUE;
1162                         return TRUE;
1163
1164                 case 3:
1165                         if (!item_selected (display, item)) {
1166                                 if (!control_key_pressed) {
1167                                         /* No control, key so remove any selections before adding */
1168                                         gl_display_unselect_all (display);
1169                                 }
1170                         }
1171                         /* Add to current selection */
1172                         gl_display_select_item (display, item);
1173                         /* bring up apropriate menu for selection. */
1174                         popup_selection_menu (display, item, event);
1175                         return TRUE;
1176
1177                 default:
1178                         return FALSE;
1179                 }
1180
1181         case GDK_BUTTON_RELEASE:
1182                 switch (event->button.button) {
1183                 case 1:
1184                         /* Exit dragging mode */
1185                         gnome_canvas_item_ungrab (item, event->button.time);
1186                         dragging = FALSE;
1187                         return TRUE;
1188
1189                 default:
1190                         return FALSE;
1191                 }
1192
1193         case GDK_MOTION_NOTIFY:
1194                 if (dragging && (event->motion.state & GDK_BUTTON1_MASK)) {
1195                         /* Dragging mode, move selection */
1196                         new_x = item_x;
1197                         new_y = item_y;
1198                         move_selected_items (display, (new_x - x), (new_y - y));
1199                         x = new_x;
1200                         y = new_y;
1201                         return TRUE;
1202                 } else {
1203                         return FALSE;
1204                 }
1205
1206         case GDK_2BUTTON_PRESS:
1207                 switch (event->button.button) {
1208                 case 1:
1209                         /* Also exit dragging mode on a a double-click, bring up menu */
1210                         gnome_canvas_item_ungrab (item, event->button.time);
1211                         dragging = FALSE;
1212                         gl_display_select_item (display, item);
1213                         gl_item_edit_dialog (item);
1214                         return TRUE;
1215
1216                 default:
1217                         return FALSE;
1218                 }
1219
1220         case GDK_ENTER_NOTIFY:
1221                 cursor = gdk_cursor_new (GDK_FLEUR);
1222                 gdk_window_set_cursor (display->canvas->window, cursor);
1223                 gdk_cursor_destroy (cursor);
1224                 return TRUE;
1225
1226         case GDK_LEAVE_NOTIFY:
1227                 cursor = gdk_cursor_new (GDK_LEFT_PTR);
1228                 gdk_window_set_cursor (display->canvas->window, cursor);
1229                 gdk_cursor_destroy (cursor);
1230                 return TRUE;
1231
1232         default:
1233                 return FALSE;
1234         }
1235
1236 }
1237
1238 /*****************************************************************************/
1239 /* create menu for multiple selections.                                      */
1240 /*****************************************************************************/
1241 GtkWidget *
1242 gl_display_new_selection_menu (glDisplay * display)
1243 {
1244         GtkWidget *menu, *menuitem;
1245
1246         g_return_val_if_fail (GL_IS_DISPLAY (display), NULL);
1247
1248         menu = gtk_menu_new ();
1249
1250         menuitem = gtk_menu_item_new_with_label (_("Delete"));
1251         gtk_menu_append (GTK_MENU (menu), menuitem);
1252         gtk_widget_show (menuitem);
1253         gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
1254                             GTK_SIGNAL_FUNC (delete_selection_cb), display);
1255
1256         menuitem = gtk_menu_item_new ();
1257         gtk_menu_append (GTK_MENU (menu), menuitem);
1258         gtk_widget_show (menuitem);
1259
1260         menuitem = gtk_menu_item_new_with_label (_("Bring to front"));
1261         gtk_menu_append (GTK_MENU (menu), menuitem);
1262         gtk_widget_show (menuitem);
1263         gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
1264                             GTK_SIGNAL_FUNC (raise_selection_cb), display);
1265
1266         menuitem = gtk_menu_item_new_with_label (_("Send to back"));
1267         gtk_menu_append (GTK_MENU (menu), menuitem);
1268         gtk_widget_show (menuitem);
1269         gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
1270                             GTK_SIGNAL_FUNC (lower_selection_cb), display);
1271
1272         return menu;
1273 }
1274
1275 /*****************************************************************************/
1276 /* create menu for given item.                                               */
1277 /*****************************************************************************/
1278 GtkWidget *
1279 gl_display_new_item_menu (GnomeCanvasItem * item)
1280 {
1281         GtkWidget *menu, *menuitem;
1282
1283         g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (item), NULL);
1284
1285         menu = gtk_menu_new ();
1286
1287         menuitem = gtk_menu_item_new_with_label (_("Edit properties..."));
1288         gtk_menu_append (GTK_MENU (menu), menuitem);
1289         gtk_widget_show (menuitem);
1290         gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate",
1291                                    GTK_SIGNAL_FUNC (gl_item_edit_dialog),
1292                                    GTK_OBJECT (item));
1293
1294         menuitem = gtk_menu_item_new ();
1295         gtk_menu_append (GTK_MENU (menu), menuitem);
1296         gtk_widget_show (menuitem);
1297
1298         menuitem = gtk_menu_item_new_with_label (_("Delete"));
1299         gtk_menu_append (GTK_MENU (menu), menuitem);
1300         gtk_widget_show (menuitem);
1301         gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
1302                             GTK_SIGNAL_FUNC (delete_item_cb), item);
1303
1304         menuitem = gtk_menu_item_new ();
1305         gtk_menu_append (GTK_MENU (menu), menuitem);
1306         gtk_widget_show (menuitem);
1307
1308         menuitem = gtk_menu_item_new_with_label (_("Bring to front"));
1309         gtk_menu_append (GTK_MENU (menu), menuitem);
1310         gtk_widget_show (menuitem);
1311         gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
1312                             GTK_SIGNAL_FUNC (raise_item_cb), item);
1313
1314         menuitem = gtk_menu_item_new_with_label (_("Send to back"));
1315         gtk_menu_append (GTK_MENU (menu), menuitem);
1316         gtk_widget_show (menuitem);
1317         gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
1318                             GTK_SIGNAL_FUNC (lower_item_cb), item);
1319
1320         gtk_object_set_data (GTK_OBJECT (item), "object_menu", menu);
1321
1322         return menu;
1323 }
1324
1325 /*---------------------------------------------------------------------------*/
1326 /* PRIVATE.  popup menu for given item.                                      */
1327 /*---------------------------------------------------------------------------*/
1328 static void
1329 popup_selection_menu (glDisplay * display,
1330                       GnomeCanvasItem * item,
1331                       GdkEvent * event)
1332 {
1333         GtkWidget *menu;
1334
1335         g_return_if_fail (GL_IS_DISPLAY (display));
1336         g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1337
1338         if (multiple_items_selected (display)) {
1339                 if (display->menu != NULL) {
1340                         gtk_menu_popup (GTK_MENU (display->menu),
1341                                         NULL, NULL, NULL, NULL,
1342                                         event->button.button,
1343                                         event->button.time);
1344                 }
1345         } else {
1346
1347                 menu = gl_item_get_menu (item);
1348                 if (menu != NULL) {
1349                         gtk_menu_popup (GTK_MENU (menu),
1350                                         NULL, NULL, NULL, NULL,
1351                                         event->button.button,
1352                                         event->button.time);
1353                 }
1354
1355         }
1356
1357 }
1358
1359 /*---------------------------------------------------------------------------*/
1360 /* PRIVATE.  delete selection callback.                                      */
1361 /*---------------------------------------------------------------------------*/
1362 static void
1363 delete_selection_cb (GtkWidget * widget,
1364                      glDisplay * display)
1365 {
1366         GList *p, *p_next;
1367
1368         g_return_if_fail (GL_IS_DISPLAY (display));
1369
1370         for (p = display->selected_item_list; p != NULL; p = p_next) {
1371                 p_next = p->next;
1372                 delete_item_cb (widget, GNOME_CANVAS_ITEM (p->data));
1373         }
1374 }
1375
1376 /*---------------------------------------------------------------------------*/
1377 /* PRIVATE.  raise item to front callback.                                   */
1378 /*---------------------------------------------------------------------------*/
1379 static void
1380 raise_selection_cb (GtkWidget * widget,
1381                     glDisplay * display)
1382 {
1383         GList *p;
1384
1385         g_return_if_fail (GL_IS_DISPLAY (display));
1386
1387         for (p = display->selected_item_list; p != NULL; p = p->next) {
1388                 raise_item_cb (widget, GNOME_CANVAS_ITEM (p->data));
1389         }
1390 }
1391
1392 /*---------------------------------------------------------------------------*/
1393 /* PRIVATE.  lower item to back callback.                                    */
1394 /*---------------------------------------------------------------------------*/
1395 static void
1396 lower_selection_cb (GtkWidget * widget,
1397                     glDisplay * display)
1398 {
1399         GList *p;
1400
1401         g_return_if_fail (GL_IS_DISPLAY (display));
1402
1403         for (p = display->selected_item_list; p != NULL; p = p->next) {
1404                 lower_item_cb (widget, GNOME_CANVAS_ITEM (p->data));
1405         }
1406 }
1407
1408 /*---------------------------------------------------------------------------*/
1409 /* PRIVATE.  delete item callback.                                           */
1410 /*---------------------------------------------------------------------------*/
1411 static void
1412 delete_item_cb (GtkWidget * widget,
1413                 GnomeCanvasItem * item)
1414 {
1415         glDisplay *display;
1416
1417         g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1418
1419         display = gl_item_get_display (item);
1420
1421         gl_display_unselect_item (display, item);
1422         display->item_list = g_list_remove (display->item_list, item);
1423
1424         gl_item_free (&item);
1425
1426         gl_display_set_modified (display);
1427 }
1428
1429 /*---------------------------------------------------------------------------*/
1430 /* PRIVATE.  raise item to front callback.                                   */
1431 /*---------------------------------------------------------------------------*/
1432 static void
1433 raise_item_cb (GtkWidget * widget,
1434                GnomeCanvasItem * item)
1435 {
1436         glLabelObject *object;
1437
1438         g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1439
1440         object = gl_item_get_object (item);
1441         gl_label_object_raise_to_front (object);
1442
1443         gnome_canvas_item_raise_to_top (item);
1444 }
1445
1446 /*---------------------------------------------------------------------------*/
1447 /* PRIVATE.  lower item to back callback.                                    */
1448 /*---------------------------------------------------------------------------*/
1449 static void
1450 lower_item_cb (GtkWidget * widget,
1451                GnomeCanvasItem * item)
1452 {
1453         glLabelObject *object;
1454         glDisplay *display;
1455
1456         g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1457
1458         object = gl_item_get_object (item);
1459         display = gl_item_get_display (item);
1460
1461         gl_label_object_lower_to_back (object);
1462
1463         /* Send to bottom */
1464         gnome_canvas_item_lower_to_bottom (item);
1465
1466         /* now raise it above all items that form the backgound */
1467         gnome_canvas_item_raise (item, display->n_bg_items);
1468 }
1469
1470 /*---------------------------------------------------------------------------*/
1471 /* PRIVATE.  move selected items                                             */
1472 /*---------------------------------------------------------------------------*/
1473 static void
1474 move_selected_items (glDisplay * display,
1475                      gdouble dx,
1476                      gdouble dy)
1477 {
1478         GList *p;
1479         GnomeCanvasItem *item;
1480
1481         g_return_if_fail (GL_IS_DISPLAY (display));
1482
1483         for (p = display->selected_item_list; p != NULL; p = p->next) {
1484
1485                 item = GNOME_CANVAS_ITEM (p->data);
1486
1487                 move_item (item, dx, dy);
1488                 gl_item_highlight (item);
1489
1490         }
1491
1492 }
1493
1494 /*---------------------------------------------------------------------------*/
1495 /* PRIVATE.  move item/object                                                */
1496 /*---------------------------------------------------------------------------*/
1497 static void
1498 move_item (GnomeCanvasItem * item,
1499            gdouble dx,
1500            gdouble dy)
1501 {
1502         glLabelObject *object;
1503         glDisplay *display;
1504
1505         g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
1506
1507         object = gl_item_get_object (item);
1508
1509         object->x += dx;
1510         object->y += dy;
1511
1512         gnome_canvas_item_move (item, dx, dy);
1513
1514         display = gl_item_get_display (item);
1515         gl_display_set_modified (display);
1516 }
1517
1518 /*---------------------------------------------------------------------------*/
1519 /* PRIVATE. Return item at (x,y) if it is in our list of managed items.      */
1520 /*---------------------------------------------------------------------------*/
1521 static GnomeCanvasItem *
1522 display_item_at (glDisplay * display,
1523                  gdouble x,
1524                  gdouble y)
1525 {
1526         GnomeCanvasItem *item;
1527
1528         g_return_val_if_fail (GL_IS_DISPLAY (display), NULL);
1529
1530         item = gnome_canvas_get_item_at (GNOME_CANVAS (display->canvas), x, y);
1531
1532         /* No item is at x, y */
1533         if (item == NULL)
1534                 return NULL;
1535
1536         /* Don't include our background items */
1537         if (g_list_find (display->bg_item_list, item) != NULL)
1538                 return NULL;
1539
1540         return item;
1541 }
1542
1543 /*---------------------------------------------------------------------------*/
1544 /* PRIVATE.  Is the item in our current selection?                           */
1545 /*---------------------------------------------------------------------------*/
1546 static gboolean
1547 item_selected (glDisplay * display,
1548                GnomeCanvasItem * item)
1549 {
1550         g_return_val_if_fail (GL_IS_DISPLAY (display), FALSE);
1551         g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (item), FALSE);
1552
1553         if (g_list_find (display->selected_item_list, item) == NULL) {
1554                 return FALSE;
1555         }
1556         return TRUE;
1557 }
1558
1559 /*---------------------------------------------------------------------------*/
1560 /* PRIVATE.  Are there multiple items in our current selection?              */
1561 /*---------------------------------------------------------------------------*/
1562 static gboolean
1563 multiple_items_selected (glDisplay * display)
1564 {
1565         g_return_val_if_fail (GL_IS_DISPLAY (display), FALSE);
1566
1567         if (display->selected_item_list == NULL)
1568                 return FALSE;
1569         if (display->selected_item_list->next == NULL)
1570                 return FALSE;
1571         return TRUE;
1572 }
1573
1574 /*---------------------------------------------------------------------------*/
1575 /* PRIVATE.  Handle "selection-clear" signal.                                */
1576 /*---------------------------------------------------------------------------*/
1577 static void
1578 selection_clear_cb (GtkWidget * widget,
1579                     GdkEventSelection * event,
1580                     gpointer data)
1581 {
1582         glDisplay *display = GL_DISPLAY (data);
1583
1584         g_return_if_fail (GL_IS_DISPLAY (display));
1585
1586         display->have_selection = FALSE;
1587         gl_label_free (&display->selection_data);
1588         display->selection_data = NULL;
1589 }
1590
1591 /*---------------------------------------------------------------------------*/
1592 /* PRIVATE.  Handle "selection-get" signal.                                  */
1593 /*---------------------------------------------------------------------------*/
1594 static void
1595 selection_get_cb (GtkWidget * widget,
1596                   GtkSelectionData * selection_data,
1597                   guint info,
1598                   guint time,
1599                   gpointer data)
1600 {
1601         glDisplay *display = GL_DISPLAY (data);
1602         gchar *buffer;
1603
1604         g_return_if_fail (GL_IS_DISPLAY (display));
1605
1606         if (display->have_selection) {
1607
1608                 gl_label_save_xml_buffer (display->selection_data, &buffer);
1609                 gtk_selection_data_set (selection_data,
1610                                         GDK_SELECTION_TYPE_STRING, 8, buffer,
1611                                         strlen (buffer));
1612                 g_free (buffer);
1613         }
1614 }
1615
1616 /*---------------------------------------------------------------------------*/
1617 /* PRIVATE.  Handle "selection-received" signal.  (Result of Paste)          */
1618 /*---------------------------------------------------------------------------*/
1619 static void
1620 selection_received_cb (GtkWidget * widget,
1621                        GtkSelectionData * selection_data,
1622                        guint time,
1623                        gpointer data)
1624 {
1625         glDisplay *display = GL_DISPLAY (data);
1626         glLabel *label = NULL;
1627         GList *p;
1628         glLabelObject *object, *newobject;
1629         GnomeCanvasItem *item;
1630
1631         g_return_if_fail (GL_IS_DISPLAY (display));
1632
1633         if (selection_data->length < 0) {
1634                 return;
1635         }
1636         if (selection_data->type != GDK_SELECTION_TYPE_STRING) {
1637                 return;
1638         }
1639
1640         gl_display_unselect_all (display);
1641
1642         gl_label_open_xml_buffer (&label, selection_data->data);
1643         for (p = label->objects; p != NULL; p = p->next) {
1644                 object = (glLabelObject *) p->data;
1645                 newobject =
1646                     gl_label_object_new_from_object (display->label, object);
1647                 item = gl_item_new (newobject, display);
1648                 gl_display_add_item (display, item);
1649                 gl_display_select_item (display, item);
1650         }
1651         gl_label_free (&label);
1652
1653         gl_display_set_modified (display);
1654 }
1655
1656 /*****************************************************************************/
1657 /* Zoom in one "notch"                                                       */
1658 /*****************************************************************************/
1659 void
1660 gl_display_zoom_in (glDisplay * display)
1661 {
1662         gint i, i_min;
1663         gdouble dist, dist_min;
1664
1665         g_return_if_fail (GL_IS_DISPLAY (display));
1666
1667         /* Find index of current scale (or best match) */
1668         i_min = 1;              /* start with 2nd largest scale */
1669         dist_min = fabs (scales[1] - display->scale);
1670         for (i = 2; scales[i] != 0.0; i++) {
1671                 dist = fabs (scales[i] - display->scale);
1672                 if (dist < dist_min) {
1673                         i_min = i;
1674                         dist_min = dist;
1675                 }
1676         }
1677
1678         /* zoom in one "notch" */
1679         i = MAX (0, i_min - 1);
1680         gl_display_set_zoom (display, scales[i] / HOME_SCALE);
1681 }
1682
1683 /*****************************************************************************/
1684 /* Zoom out one "notch"                                                      */
1685 /*****************************************************************************/
1686 void
1687 gl_display_zoom_out (glDisplay * display)
1688 {
1689         gint i, i_min;
1690         gdouble dist, dist_min;
1691
1692         g_return_if_fail (GL_IS_DISPLAY (display));
1693
1694         /* Find index of current scale (or best match) */
1695         i_min = 0;              /* start with largest scale */
1696         dist_min = fabs (scales[0] - display->scale);
1697         for (i = 1; scales[i] != 0.0; i++) {
1698                 dist = fabs (scales[i] - display->scale);
1699                 if (dist < dist_min) {
1700                         i_min = i;
1701                         dist_min = dist;
1702                 }
1703         }
1704
1705         /* zoom out one "notch" */
1706         if (scales[i_min] == 0.0)
1707                 return;
1708         i = i_min + 1;
1709         if (scales[i] == 0.0)
1710                 return;
1711         gl_display_set_zoom (display, scales[i] / HOME_SCALE);
1712
1713 }
1714
1715 /*****************************************************************************/
1716 /* Set current zoom factor to explicit value.                                */
1717 /*****************************************************************************/
1718 void
1719 gl_display_set_zoom (glDisplay * display,
1720                      gdouble scale)
1721 {
1722         g_return_if_fail (GL_IS_DISPLAY (display));
1723         g_return_if_fail (scale > 0.0);
1724
1725         display->scale = scale * HOME_SCALE;
1726         gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (display->canvas),
1727                                           scale * HOME_SCALE);
1728 }
1729
1730 /*****************************************************************************/
1731 /* Get current zoom factor.                                                  */
1732 /*****************************************************************************/
1733 gdouble
1734 gl_display_get_zoom (glDisplay * display)
1735 {
1736         g_return_val_if_fail (GL_IS_DISPLAY (display), 1.0);
1737
1738         return display->scale / HOME_SCALE;
1739 }