OSDN Git Service

f97cd48758b17c3f4d3c7055c7899f1cac9075b6
[mypaint-anime/master.git] / gtkmydrawwidget.c
1 /* MyPaint - pressure sensitive painting application
2  * Copyright (C) 2005-2007 Martin Renold
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 // gtk stock code - left gtk prefix to use the pygtk wrapper-generator easier
20 #include "gtkmydrawwidget.h"
21 #include "stroke_recorder.h"
22
23 static void gtk_my_draw_widget_class_init    (GtkMyDrawWidgetClass *klass);
24 static void gtk_my_draw_widget_init          (GtkMyDrawWidget      *mdw);
25 static void gtk_my_draw_widget_finalize (GObject *object);
26 static void gtk_my_draw_widget_realize (GtkWidget *widget);
27 static gint gtk_my_draw_widget_button_updown (GtkWidget *widget, GdkEventButton *event);
28 static gint gtk_my_draw_widget_motion_notify (GtkWidget *widget, GdkEventMotion *event);
29 static gint gtk_my_draw_widget_proximity_inout (GtkWidget *widget, GdkEventProximity *event);
30 static gint gtk_my_draw_widget_expose (GtkWidget *widget, GdkEventExpose *event);
31 static void gtk_my_draw_widget_surface_modified (GtkMySurface *s, gint x, gint y, gint w, gint h, GtkMyDrawWidget *mdw);
32
33
34 static gpointer parent_class;
35
36 enum {
37   DRAGGING_FINISHED,
38   LAST_SIGNAL
39 };
40 guint gtk_my_draw_widget_signals[LAST_SIGNAL] = { 0 };
41
42 GType
43 gtk_my_draw_widget_get_type (void)
44 {
45   static GType type = 0;
46
47   if (!type)
48     {
49       static const GTypeInfo info =
50       {
51         sizeof (GtkMyDrawWidgetClass),
52         NULL,           /* base_init */
53         NULL,           /* base_finalize */
54         (GClassInitFunc) gtk_my_draw_widget_class_init,
55         NULL,           /* class_finalize */
56         NULL,           /* class_data */
57         sizeof (GtkMyDrawWidget),
58         0,              /* n_preallocs */
59         (GInstanceInitFunc) gtk_my_draw_widget_init,
60       };
61
62       type =
63         g_type_register_static (GTK_TYPE_DRAWING_AREA, "GtkMyDrawWidget",
64                                 &info, 0);
65     }
66
67   return type;
68 }
69
70 static void
71 gtk_my_draw_widget_class_init (GtkMyDrawWidgetClass *class)
72 {
73   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
74   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
75
76   parent_class = g_type_class_peek_parent (class);
77         
78   gobject_class->finalize = gtk_my_draw_widget_finalize;
79   widget_class->realize = gtk_my_draw_widget_realize;
80
81   widget_class->expose_event = gtk_my_draw_widget_expose;
82   widget_class->motion_notify_event = gtk_my_draw_widget_motion_notify;
83   widget_class->button_press_event = gtk_my_draw_widget_button_updown;
84   widget_class->button_release_event = gtk_my_draw_widget_button_updown;
85   widget_class->proximity_in_event = gtk_my_draw_widget_proximity_inout;
86   widget_class->proximity_out_event = gtk_my_draw_widget_proximity_inout;
87
88
89   gtk_my_draw_widget_signals[DRAGGING_FINISHED] = g_signal_new 
90     ("dragging_finished",
91      G_TYPE_FROM_CLASS (class),
92      G_SIGNAL_RUN_LAST,
93      G_STRUCT_OFFSET (GtkMyDrawWidgetClass, dragging_finished),
94      NULL, NULL,
95      g_cclosure_marshal_VOID__VOID,
96      G_TYPE_NONE, 0);
97
98 }
99
100 static void
101 gtk_my_draw_widget_realize (GtkWidget *widget)
102 {
103   GtkMyDrawWidget *mdw;
104   GdkWindowAttr attributes;
105   gint attributes_mask;
106
107   g_return_if_fail (GTK_IS_MY_DRAW_WIDGET (widget));
108
109   mdw = GTK_MY_DRAW_WIDGET (widget);
110   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
111
112   attributes.window_type = GDK_WINDOW_CHILD;
113   attributes.x = widget->allocation.x;
114   attributes.y = widget->allocation.y;
115   attributes.width = widget->allocation.width;
116   attributes.height = widget->allocation.height;
117   attributes.wclass = GDK_INPUT_OUTPUT;
118   attributes.visual = gtk_widget_get_visual (widget);
119   attributes.colormap = gtk_widget_get_colormap (widget);
120
121   attributes.event_mask = gtk_widget_get_events (widget);
122   attributes.event_mask |= (GDK_EXPOSURE_MASK |
123                             GDK_LEAVE_NOTIFY_MASK |
124                             GDK_BUTTON_PRESS_MASK |
125                             GDK_BUTTON_RELEASE_MASK |
126                             GDK_POINTER_MOTION_MASK |
127                             GDK_PROXIMITY_IN_MASK |
128                             GDK_PROXIMITY_OUT_MASK);
129
130   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
131
132   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
133   gdk_window_set_user_data (widget->window, mdw);
134
135   widget->style = gtk_style_attach (widget->style, widget->window);
136   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
137
138   // needed for some unknown reason
139   gtk_widget_add_events (widget, attributes.event_mask);
140   // needed for known reason
141   gtk_widget_set_extension_events (widget, GDK_EXTENSION_EVENTS_ALL);
142
143   //gtk_drawing_area_send_configure (GTK_DRAWING_AREA (widget));
144 }
145
146 static void
147 gtk_my_draw_widget_finalize (GObject *object)
148 {
149   GtkMyDrawWidget * mdw;
150   g_return_if_fail (object != NULL);
151   g_return_if_fail (GTK_IS_MY_DRAW_WIDGET (object));
152   mdw = GTK_MY_DRAW_WIDGET (object);
153   // can be called multiple times
154   if (mdw->surface) {
155     g_signal_handlers_disconnect_by_func (mdw->surface, gtk_my_draw_widget_surface_modified, mdw);
156     g_object_unref (mdw->surface);
157     mdw->surface = NULL;
158   }
159   if (mdw->replaying) {
160     g_array_free (mdw->replaying, TRUE);
161     mdw->replaying = NULL;
162   }
163   if (mdw->recording) {
164     g_array_free (mdw->recording, TRUE);
165     mdw->recording = NULL;
166   }
167   G_OBJECT_CLASS (parent_class)->finalize (object);
168 }
169
170 GtkWidget*
171 gtk_my_draw_widget_new (void)
172 {
173   return g_object_new (GTK_TYPE_MY_DRAW_WIDGET, NULL);
174 }
175
176 void gtk_my_draw_widget_init (GtkMyDrawWidget *mdw)
177 {
178   mdw->surface = NULL;
179   // Dummy width, must be set later using discard_and_resize. 
180   // I decided for an empty constructor because [complicated excuse
181   // removed] since http://live.gnome.org/PyGTK/WhatsNew28
182   gtk_my_draw_widget_discard_and_resize (mdw, 1, 1);
183
184   mdw->zoom = 1.0;
185   mdw->one_over_zoom = 1.0;
186   mdw->dragging = 0;
187
188   mdw->recording = NULL;
189   mdw->replaying = NULL;
190   mdw->last_time = 0;
191 }
192
193 void gtk_my_draw_widget_discard_and_resize (GtkMyDrawWidget *mdw, int width, int height)
194 {
195   if (mdw->surface) {
196     g_signal_handlers_disconnect_by_func (mdw->surface, gtk_my_draw_widget_surface_modified, mdw);
197     g_object_unref (mdw->surface);
198   }
199   mdw->surface = gtk_my_surface_old_new  (width, height);
200   g_signal_connect (mdw->surface, "surface_modified", G_CALLBACK (gtk_my_draw_widget_surface_modified), mdw);
201
202 }
203
204 static void
205 gtk_my_draw_widget_process_motion_or_button (GtkWidget *widget, guint32 time, gdouble x, gdouble y, gdouble pressure)
206 {
207   //RawInput * ri;
208   GtkMyDrawWidget * mdw;
209   mdw = GTK_MY_DRAW_WIDGET (widget);
210
211   g_assert (pressure >= 0 && pressure <= 1);
212
213   int dtime;
214   if (!mdw->last_time) {
215     dtime = 100; //ms
216   } else {
217     dtime = time - mdw->last_time;
218   }
219   mdw->last_time = time;
220
221   if (mdw->recording) {
222     StrokeEvent e;
223     e.dtime = dtime;
224     e.x = x;
225     e.y = y;
226     e.pressure = pressure;
227     g_array_append_val (mdw->recording, e);
228   }
229   
230   if (mdw->brush) {
231     gtk_my_brush_stroke_to (mdw->brush, mdw->surface,
232                             x*mdw->one_over_zoom + mdw->viewport_x, y*mdw->one_over_zoom + mdw->viewport_y,
233                             pressure, (double)dtime / 1000.0 /* in seconds */);
234   }
235 }
236
237 static void
238 gtk_my_draw_widget_surface_modified (GtkMySurface *s, gint x, gint y, gint w, gint h, GtkMyDrawWidget *mdw)
239 {
240   x -= (int)(mdw->viewport_x+0.5);
241   y -= (int)(mdw->viewport_y+0.5);
242   if (mdw->zoom != 1.0) {
243     x = (int)(x * mdw->zoom);
244     y = (int)(y * mdw->zoom);
245     w = (int)(w * mdw->zoom);
246     h = (int)(h * mdw->zoom);
247     // worst-case rounding problem
248     w += 2;
249     h += 2;
250   }
251   gtk_widget_queue_draw_area (GTK_WIDGET (mdw), x, y, w, h);
252   //printf ("queued %d %d %d %d\n", x, y, w, h);
253 }
254
255 static gint
256 gtk_my_draw_widget_button_updown (GtkWidget *widget, GdkEventButton *event)
257 {
258   GtkMyDrawWidget * mdw;
259   g_return_val_if_fail (widget != NULL, FALSE);
260   g_return_val_if_fail (GTK_IS_MY_DRAW_WIDGET (widget), FALSE);
261   mdw = GTK_MY_DRAW_WIDGET (widget);
262
263   double pressure;
264   if (!gdk_event_get_axis ((GdkEvent *)event, GDK_AXIS_PRESSURE, &pressure)) {
265     pressure = (event->state & GDK_BUTTON1_MASK) ? 0.5 : 0;
266   }
267   gtk_my_draw_widget_process_motion_or_button (widget, event->time, event->x, event->y, pressure);
268   return TRUE;
269 }
270
271 static gint
272 gtk_my_draw_widget_motion_notify (GtkWidget *widget, GdkEventMotion *event)
273 {
274   GtkMyDrawWidget * mdw;
275   g_return_val_if_fail (widget != NULL, FALSE);
276   g_return_val_if_fail (GTK_IS_MY_DRAW_WIDGET (widget), FALSE);
277   mdw = GTK_MY_DRAW_WIDGET (widget);
278
279   if ((event->state & GDK_BUTTON2_MASK) && mdw->allow_dragging) {
280     if (!mdw->dragging) {
281       mdw->dragging = 1;
282       mdw->dragging_last_x = (int) event->x;
283       mdw->dragging_last_y = (int) event->y;
284     } else {
285       float x, y, dx, dy;
286       x = event->x;
287       y = event->y;
288       dx = x - mdw->dragging_last_x;
289       dy = y - mdw->dragging_last_y;
290       if (dx == 0 && dy == 0) return TRUE;
291       dx *= mdw->one_over_zoom;
292       dy *= mdw->one_over_zoom;
293       mdw->dragging_last_x = x;
294       mdw->dragging_last_y = y;
295       gtk_my_draw_widget_set_viewport (mdw, mdw->viewport_x - dx, mdw->viewport_y - dy);
296       g_signal_emit (mdw, gtk_my_draw_widget_signals[DRAGGING_FINISHED], 0);
297       return TRUE;
298     }
299   } else {
300     mdw->dragging = 0;
301   }
302
303   double pressure;
304   if (!gdk_event_get_axis ((GdkEvent *)event, GDK_AXIS_PRESSURE, &pressure)) {
305     pressure = (event->state & GDK_BUTTON1_MASK) ? 0.5 : 0;
306   }
307   gtk_my_draw_widget_process_motion_or_button (widget, event->time, event->x, event->y, pressure);
308   return TRUE;
309 }
310
311 static gint
312 gtk_my_draw_widget_proximity_inout (GtkWidget *widget, GdkEventProximity *event)
313
314   // handled in python now
315   return FALSE;
316 }
317
318 static gint
319 gtk_my_draw_widget_expose (GtkWidget *widget, GdkEventExpose *event)
320 {
321   GtkMyDrawWidget * mdw;
322   guchar *rgb;
323   int rowstride;
324
325   g_return_val_if_fail (widget != NULL, FALSE);
326   g_return_val_if_fail (GTK_IS_MY_DRAW_WIDGET (widget), FALSE);
327   g_return_val_if_fail (event != NULL, FALSE);
328
329   mdw = GTK_MY_DRAW_WIDGET (widget);
330
331   rowstride = event->area.width * 3;
332   rowstride = (rowstride + 3) & -4; /* align to 4-byte boundary */
333   rgb = g_new (guchar, event->area.height * rowstride);
334
335   //printf("Zoom = %f\n", mdw->zoom);
336   if (mdw->zoom == 0.0) mdw->zoom = 1.0; // whyever.
337   if (mdw->zoom == 1.0) {
338     gtk_my_surface_old_render 
339       (mdw->surface,
340        rgb, rowstride,
341        event->area.x + (int)(mdw->viewport_x+0.5), event->area.y + (int)(mdw->viewport_y+0.5),
342        event->area.width, event->area.height,
343        /*bpp*/3*8);
344   } else {
345     gtk_my_surface_old_render_zoom 
346       (mdw->surface,
347        rgb, rowstride,
348        event->area.x + mdw->viewport_x*mdw->zoom, event->area.y + mdw->viewport_y*mdw->zoom,
349        event->area.width, event->area.height,
350        /*bpp*/3*8,
351        mdw->one_over_zoom);
352   }
353
354   gdk_draw_rgb_image (widget->window,
355                       widget->style->black_gc,
356                       event->area.x, event->area.y,
357                       event->area.width, event->area.height,
358                       GDK_RGB_DITHER_MAX,
359                       rgb,
360                       rowstride);
361
362   g_free (rgb);
363   return FALSE;
364 }
365
366 void           
367 gtk_my_draw_widget_clear (GtkMyDrawWidget *mdw)
368 {
369   gtk_my_surface_clear (GTK_MY_SURFACE (mdw->surface));
370   gtk_widget_queue_draw (GTK_WIDGET (mdw));
371 }
372
373
374 GtkMyBrush* 
375 gtk_my_draw_widget_set_brush (GtkMyDrawWidget *mdw, GtkMyBrush * brush)
376 {
377   GtkMyBrush* brush_old = mdw->brush;
378   if (brush) g_object_ref (brush);
379   mdw->brush = brush;
380
381   // The caller owns the reference now (caller-owns-return in
382   // fix_generated_defs.py) thus we don't g_object_unref here.
383   // Cannot return a borrowed reference instead because we just
384   // discarded the pointer.
385   return brush_old;
386 }
387
388 void gtk_my_draw_widget_allow_dragging (GtkMyDrawWidget *mdw, int allow)
389 {
390   mdw->allow_dragging = allow;
391 }
392 void gtk_my_draw_widget_set_viewport (GtkMyDrawWidget *mdw, float x, float y)
393 {
394   mdw->viewport_x = x;
395   mdw->viewport_y = y;
396   gtk_widget_queue_draw (GTK_WIDGET (mdw));
397 }
398 float gtk_my_draw_widget_get_viewport_x (GtkMyDrawWidget *mdw)
399 {
400   return mdw->viewport_x;
401 }
402 float gtk_my_draw_widget_get_viewport_y (GtkMyDrawWidget *mdw)
403 {
404   return mdw->viewport_y;
405 }
406
407 void gtk_my_draw_widget_set_zoom (GtkMyDrawWidget *mdw, float zoom)
408 {
409   if (mdw->zoom == zoom) return;
410   if (zoom > 0.99 && zoom < 1.01) zoom = 1.0;
411   mdw->zoom = zoom;
412   mdw->one_over_zoom = 1.0/zoom;
413   gtk_widget_queue_draw (GTK_WIDGET (mdw));
414 }
415
416 float gtk_my_draw_widget_get_zoom (GtkMyDrawWidget *mdw)
417 {
418   return mdw->zoom;
419 }
420
421 GdkPixbuf* gtk_my_draw_widget_get_as_pixbuf (GtkMyDrawWidget *mdw)
422 {
423   GdkPixbuf* pixbuf;
424   pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, /*has_alpha*/0, /*bits_per_sample*/8,
425                            mdw->surface->w, mdw->surface->h);
426
427   gtk_my_surface_old_render (mdw->surface, 
428                              gdk_pixbuf_get_pixels (pixbuf), 
429                              gdk_pixbuf_get_rowstride (pixbuf),
430                              0, 0, mdw->surface->w, mdw->surface->h,
431                              /*bpp*/3*8);
432
433   return pixbuf;
434 }
435
436 GdkPixbuf* gtk_my_draw_widget_get_nonwhite_as_pixbuf (GtkMyDrawWidget *mdw)
437 {
438   Rect r;
439   GdkPixbuf* pixbuf;
440   gtk_my_surface_old_get_nonwhite_region (mdw->surface, &r);
441
442   pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, /*has_alpha*/0, /*bits_per_sample*/8,
443                            r.w, r.h);
444
445   gtk_my_surface_old_render (mdw->surface, 
446                              gdk_pixbuf_get_pixels (pixbuf), 
447                              gdk_pixbuf_get_rowstride (pixbuf),
448                              r.x, r.y, r.w, r.h,
449                              /*bpp*/3*8);
450
451   return pixbuf;
452 }
453
454 void gtk_my_draw_widget_set_from_pixbuf (GtkMyDrawWidget *mdw, GdkPixbuf* pixbuf)
455 {
456   int w, h, n_channels;
457
458   n_channels = gdk_pixbuf_get_n_channels (pixbuf);
459
460   g_assert (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
461   g_assert (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
462   //ignore - g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
463   g_assert (n_channels == 4 || n_channels == 3);
464
465   w = gdk_pixbuf_get_width (pixbuf);
466   h = gdk_pixbuf_get_height (pixbuf);
467
468   gtk_my_surface_old_load (mdw->surface,
469                            gdk_pixbuf_get_pixels (pixbuf),
470                            gdk_pixbuf_get_rowstride (pixbuf),
471                            w, h,
472                            /*bpp*/n_channels*8);
473   gtk_widget_queue_draw (GTK_WIDGET (mdw));
474 }
475
476 void gtk_my_draw_widget_start_recording (GtkMyDrawWidget *mdw)
477 {
478   g_assert (!mdw->recording);
479   mdw->recording = g_array_new (FALSE, FALSE, sizeof(StrokeEvent));
480 }
481
482 GString* gtk_my_draw_widget_stop_recording (GtkMyDrawWidget *mdw)
483 {
484   // see also mydrawwidget.override
485   GString *s;
486   s = event_array_to_string (mdw->recording);
487   g_array_free (mdw->recording, TRUE); mdw->recording = NULL;
488   return s;
489 }
490
491 void gtk_my_draw_widget_stop_replaying (GtkMyDrawWidget *mdw)
492 {
493   g_print ("TODO\n");
494   g_assert (!mdw->replaying);
495   //mdw->replaying = 
496 }
497
498 void gtk_my_draw_widget_replay (GtkMyDrawWidget *mdw, GString* data, int immediately)
499 {
500   // see also mydrawwidget.override
501   if (mdw->replaying) {
502     g_print ("Attempting to start replay while replaying.\n");
503     return;
504   }
505   mdw->replaying = string_to_event_array (data);
506
507   if (immediately) {
508     int i;
509     for (i=0; i<mdw->replaying->len; i++) {
510       StrokeEvent *e;
511       e = &g_array_index (mdw->replaying, StrokeEvent, i);
512       //g_print ("Replay: dtime=%d, x=%f.\n", e->dtime, e->x);
513       gtk_my_brush_stroke_to (mdw->brush, mdw->surface,
514                               e->x*mdw->one_over_zoom + mdw->viewport_x, e->y*mdw->one_over_zoom + mdw->viewport_y,
515                               e->pressure, (double)(e->dtime) / 1000.0 /* in seconds */);
516     }
517     g_array_free (mdw->replaying, TRUE); mdw->replaying = NULL;
518   } else {
519     g_print ("TODO\n");
520   }
521 }
522
523 /*
524 GTimer *clock_timer;
525
526 ...
527     clock_timer = g_timer_new();
528     g_idle_add(continue_replay, NULL);
529     g_timer_start(clock_timer);
530 ...
531
532 static gboolean
533 update_clock(gpointer dummy)
534 {
535     char buf[10];
536     sprintf(buf, "%d", (int)g_timer_elapsed(clock_timer, NULL));
537     gtk_label_set_text(GTK_LABEL(clock_label), buf);
538     return(TRUE);
539 }
540 */