1 /*
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This is a plug-in for GIMP.
5  *
6  * Generates images containing vector type drawings.
7  *
8  * Copyright (C) 1997 Andy Thomas  alt@picnic.demon.co.uk
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 
26 #include <libgimp/gimp.h>
27 #include <libgimp/gimpui.h>
28 
29 #include "gfig.h"
30 #include "gfig-grid.h"
31 #include "gfig-dobject.h"
32 #include "gfig-preview.h"
33 
34 #include "libgimp/stdplugins-intl.h"
35 
36 #define PREVIEW_MASK  (GDK_EXPOSURE_MASK       | \
37                        GDK_POINTER_MOTION_MASK | \
38                        GDK_BUTTON_PRESS_MASK   | \
39                        GDK_BUTTON_RELEASE_MASK | \
40                        GDK_BUTTON_MOTION_MASK  | \
41                        GDK_KEY_PRESS_MASK      | \
42                        GDK_KEY_RELEASE_MASK)
43 
44 static gint       x_pos_val;
45 static gint       y_pos_val;
46 static gint       pos_tag = -1;
47 GtkWidget        *status_label_dname;
48 GtkWidget        *status_label_fname;
49 static GtkWidget *pos_label;       /* XY pos marker */
50 
51 
52 static void       gfig_preview_realize  (GtkWidget *widget);
53 static gboolean   gfig_preview_events   (GtkWidget *widget,
54                                          GdkEvent  *event);
55 static gboolean   gfig_preview_expose   (GtkWidget *widget,
56                                          GdkEvent  *event);
57 
58 static gint       gfig_invscale_x        (gint      x);
59 static gint       gfig_invscale_y        (gint      y);
60 static GtkWidget *gfig_pos_labels        (void);
61 static GtkWidget *make_pos_info          (void);
62 
63 static void       gfig_pos_update        (gint      x,
64                                           gint      y);
65 static void       gfig_pos_update_labels (gpointer  data);
66 
67 GtkWidget *
make_preview(void)68 make_preview (void)
69 {
70   GtkWidget *frame;
71   GtkWidget *vbox;
72   GtkWidget *hbox;
73   GtkWidget *table;
74   GtkWidget *ruler;
75 
76   gfig_context->preview = gtk_drawing_area_new ();
77   gtk_widget_set_events (GTK_WIDGET (gfig_context->preview), PREVIEW_MASK);
78 
79   g_signal_connect (gfig_context->preview , "realize",
80                     G_CALLBACK (gfig_preview_realize),
81                     NULL);
82 
83   g_signal_connect (gfig_context->preview , "event",
84                     G_CALLBACK (gfig_preview_events),
85                     NULL);
86 
87   g_signal_connect_after (gfig_context->preview , "expose-event",
88                           G_CALLBACK (gfig_preview_expose),
89                           NULL);
90 
91   gtk_widget_set_size_request (gfig_context->preview,
92                                preview_width, preview_height);
93 
94   frame = gtk_frame_new (NULL);
95 
96   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
97 
98   table = gtk_table_new (3, 3, FALSE);
99   gtk_table_attach (GTK_TABLE (table), gfig_context->preview, 1, 2, 1, 2,
100                     GTK_FILL , GTK_FILL , 0, 0);
101   gtk_container_add (GTK_CONTAINER (frame), table);
102 
103   ruler = gimp_ruler_new (GTK_ORIENTATION_HORIZONTAL);
104   gimp_ruler_set_range (GIMP_RULER (ruler), 0, preview_width, PREVIEW_SIZE);
105   g_signal_connect_swapped (gfig_context->preview, "motion-notify-event",
106                             G_CALLBACK (GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (ruler))->motion_notify_event),
107                             ruler);
108   gtk_table_attach (GTK_TABLE (table), ruler, 1, 2, 0, 1,
109                     GTK_FILL, GTK_FILL, 0, 0);
110   gtk_widget_show (ruler);
111 
112   ruler = gimp_ruler_new (GTK_ORIENTATION_VERTICAL);
113   gimp_ruler_set_range (GIMP_RULER (ruler), 0, preview_height, PREVIEW_SIZE);
114   g_signal_connect_swapped (gfig_context->preview, "motion-notify-event",
115                             G_CALLBACK (GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (ruler))->motion_notify_event),
116                             ruler);
117   gtk_table_attach (GTK_TABLE (table), ruler, 0, 1, 1, 2,
118                     GTK_FILL, GTK_FILL, 0, 0);
119   gtk_widget_show (ruler);
120 
121   gtk_widget_show (frame);
122   gtk_widget_show (table);
123 
124   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
125   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
126   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
127   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
128 
129   frame = make_pos_info ();
130   gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
131 
132   gtk_widget_show (vbox);
133   gtk_widget_show (hbox);
134 
135   return vbox;
136 }
137 
138 static void
gfig_preview_realize(GtkWidget * widget)139 gfig_preview_realize (GtkWidget *widget)
140 {
141   GdkDisplay *display = gtk_widget_get_display (widget);
142 
143   gdk_window_set_cursor (gtk_widget_get_window (gfig_context->preview),
144                          gdk_cursor_new_for_display (display, GDK_CROSSHAIR));
145   gfig_grid_colors (widget);
146 }
147 
148 static void
draw_background(cairo_t * cr)149 draw_background (cairo_t  *cr)
150 {
151   if (! back_pixbuf)
152     back_pixbuf = gimp_image_get_thumbnail (gfig_context->image_id,
153                                             preview_width, preview_height,
154                                             GIMP_PIXBUF_LARGE_CHECKS);
155 
156   if (back_pixbuf)
157     {
158       gdk_cairo_set_source_pixbuf (cr, back_pixbuf, 0, 0);
159       cairo_paint (cr);
160     }
161 }
162 
163 static gboolean
gfig_preview_expose(GtkWidget * widget,GdkEvent * event)164 gfig_preview_expose (GtkWidget *widget,
165                      GdkEvent  *event)
166 {
167   cairo_t *cr = gdk_cairo_create (event->expose.window);
168 
169   if (gfig_context->show_background)
170     draw_background (cr);
171 
172   draw_grid (cr);
173   draw_objects (gfig_context->current_obj->obj_list, TRUE, cr);
174 
175   if (obj_creating)
176     {
177       GList *single = g_list_prepend (NULL, obj_creating);
178       draw_objects (single, TRUE, cr);
179       g_list_free (single);
180     }
181 
182   cairo_destroy (cr);
183   return FALSE;
184 }
185 
186 static gboolean
gfig_preview_events(GtkWidget * widget,GdkEvent * event)187 gfig_preview_events (GtkWidget *widget,
188                      GdkEvent  *event)
189 {
190   GdkEventButton *bevent;
191   GdkEventMotion *mevent;
192   GdkPoint        point;
193   static gint     tmp_show_single = 0;
194 
195   switch (event->type)
196     {
197     case GDK_EXPOSE:
198       break;
199 
200     case GDK_BUTTON_PRESS:
201       bevent = (GdkEventButton *) event;
202       point.x = bevent->x;
203       point.y = bevent->y;
204 
205       g_assert (need_to_scale == 0); /* If not out of step some how */
206 
207       /* Start drawing of object */
208       if (selvals.otype >= MOVE_OBJ)
209         {
210           if (!selvals.scaletoimage)
211             {
212               point.x = gfig_invscale_x (point.x);
213               point.y = gfig_invscale_y (point.y);
214             }
215           object_operation_start (&point, bevent->state & GDK_SHIFT_MASK);
216 
217           /* If constraining save start pnt */
218           if (selvals.opts.snap2grid)
219             {
220               /* Save point to constrained point ... if button 3 down */
221               if (bevent->button == 3)
222                 {
223                   find_grid_pos (&point, &point, FALSE);
224                 }
225             }
226         }
227       else
228         {
229           if (selvals.opts.snap2grid)
230             find_grid_pos (&point, &point, FALSE);
231           object_start (&point, bevent->state & GDK_SHIFT_MASK);
232 
233           gtk_widget_queue_draw (widget);
234         }
235 
236       break;
237 
238     case GDK_BUTTON_RELEASE:
239       bevent = (GdkEventButton *) event;
240       point.x = bevent->x;
241       point.y = bevent->y;
242 
243       if (selvals.opts.snap2grid)
244         find_grid_pos (&point, &point, bevent->button == 3);
245 
246       /* Still got shift down ?*/
247       if (selvals.otype >= MOVE_OBJ)
248         {
249           if (!selvals.scaletoimage)
250             {
251               point.x = gfig_invscale_x (point.x);
252               point.y = gfig_invscale_y (point.y);
253             }
254           object_operation_end (&point, bevent->state & GDK_SHIFT_MASK);
255         }
256       else
257         {
258           if (obj_creating)
259             {
260               object_end (&point, bevent->state & GDK_SHIFT_MASK);
261             }
262           else
263             break;
264         }
265 
266       gfig_paint_callback ();
267       break;
268 
269     case GDK_MOTION_NOTIFY:
270       mevent = (GdkEventMotion *) event;
271       point.x = mevent->x;
272       point.y = mevent->y;
273 
274       if (selvals.opts.snap2grid)
275         find_grid_pos (&point, &point, mevent->state & GDK_BUTTON3_MASK);
276 
277       if (selvals.otype >= MOVE_OBJ)
278         {
279           /* Moving objects around */
280           if (!selvals.scaletoimage)
281             {
282               point.x = gfig_invscale_x (point.x);
283               point.y = gfig_invscale_y (point.y);
284             }
285           object_operation (&point, mevent->state & GDK_SHIFT_MASK);
286           gfig_pos_update (point.x, point.y);
287           return FALSE;
288         }
289 
290       if (obj_creating)
291         {
292           obj_creating->class->update (&point);
293           gtk_widget_queue_draw (widget);
294         }
295       gfig_pos_update (point.x, point.y);
296       break;
297 
298     case GDK_KEY_PRESS:
299       if ((tmp_show_single = obj_show_single) != -1)
300         {
301           obj_show_single = -1;
302           draw_grid_clear ();
303         }
304       break;
305 
306     case GDK_KEY_RELEASE:
307       if (tmp_show_single != -1)
308         {
309           obj_show_single = tmp_show_single;
310           draw_grid_clear ();
311         }
312       break;
313 
314     default:
315       break;
316     }
317 
318   return FALSE;
319 }
320 
321 static GtkWidget *
make_pos_info(void)322 make_pos_info (void)
323 {
324   GtkWidget *frame;
325   GtkWidget *hbox;
326   GtkWidget *label;
327 
328   frame = gimp_frame_new (_("Object Details"));
329 
330   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
331   gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
332   gtk_container_add (GTK_CONTAINER (frame), hbox);
333 
334   /* Add labels */
335   label = gfig_pos_labels ();
336   gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
337   gfig_pos_enable (NULL, NULL);
338 
339 #if 0
340   label = gfig_obj_size_label ();
341   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
342 #endif /* 0 */
343 
344   gtk_widget_show (hbox);
345   gtk_widget_show (frame);
346 
347   return frame;
348 }
349 
350 static gint
gfig_invscale_x(gint x)351 gfig_invscale_x (gint x)
352 {
353   if (!selvals.scaletoimage)
354     return (gint) (x * scale_x_factor);
355   else
356     return x;
357 }
358 
359 static gint
gfig_invscale_y(gint y)360 gfig_invscale_y (gint y)
361 {
362   if (!selvals.scaletoimage)
363     return (gint) (y * scale_y_factor);
364   else
365     return y;
366 }
367 
368 static GtkWidget *
gfig_pos_labels(void)369 gfig_pos_labels (void)
370 {
371   GtkWidget *label;
372   GtkWidget *hbox;
373   gchar      buf[256];
374 
375   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
376   gtk_widget_show (hbox);
377 
378   /* Position labels */
379   label = gtk_label_new (_("XY position:"));
380   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
381   gtk_widget_show (label);
382 
383   pos_label = gtk_label_new ("");
384   gtk_box_pack_start (GTK_BOX (hbox), pos_label, FALSE, FALSE, 0);
385   gtk_widget_show (pos_label);
386 
387   g_snprintf (buf, sizeof (buf), "%d, %d", 0, 0);
388   gtk_label_set_text (GTK_LABEL (pos_label), buf);
389 
390   return hbox;
391 }
392 
393 void
gfig_pos_enable(GtkWidget * widget,gpointer data)394 gfig_pos_enable (GtkWidget *widget,
395                  gpointer   data)
396 {
397   gboolean enable = selvals.showpos;
398 
399   gtk_widget_set_sensitive (GTK_WIDGET (pos_label), enable);
400 }
401 
402 static void
gfig_pos_update_labels(gpointer data)403 gfig_pos_update_labels (gpointer data)
404 {
405   static gchar buf[256];
406 
407   pos_tag = -1;
408 
409   g_snprintf (buf, sizeof (buf), "%d, %d", x_pos_val, y_pos_val);
410   gtk_label_set_text (GTK_LABEL (pos_label), buf);
411 }
412 
413 static void
gfig_pos_update(gint x,gint y)414 gfig_pos_update (gint x,
415                  gint y)
416 {
417   if ((x_pos_val !=x || y_pos_val != y) && pos_tag == -1 && selvals.showpos)
418     {
419       x_pos_val = x;
420       y_pos_val = y;
421       gfig_pos_update_labels (NULL);
422     }
423 }
424