Migrated code from gtk2 to gtk3
[tbo.git] / src / tbo-drawing.c
1 /*
2  * This file is part of TBO, a gnome comic editor
3  * Copyright (C) 2010  Daniel Garcia Moreno <dani@danigm.net>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 #include <glib.h>
21 #include <cairo.h>
22 #include <stdio.h>
23 #include <math.h>
24 #include "tbo-types.h"
25 #include "tbo-drawing.h"
26 #include "dnd.h"
27 #include "comic.h"
28 #include "frame.h"
29 #include "page.h"
30 #include "tbo-tool-bubble.h"
31 #include "tbo-tool-doodle.h"
32 #include "tbo-tooltip.h"
33
34 G_DEFINE_TYPE (TboDrawing, tbo_drawing, GTK_TYPE_LAYOUT);
35
36 /* private methods */
37 static gboolean
38 expose_event (GtkWidget *widget, cairo_t *cr1, gpointer dara)
39 {
40     cairo_t *cr;
41     gint w, h;
42     TboDrawing *self = TBO_DRAWING (widget);
43
44     cr = gdk_cairo_create(gtk_layout_get_bin_window (GTK_LAYOUT (widget)));
45     w = gdk_window_get_width (gtk_layout_get_bin_window (GTK_LAYOUT (widget)));
46     h = gdk_window_get_height (gtk_layout_get_bin_window (GTK_LAYOUT (widget)));
47
48     cairo_set_source_rgb (cr, 0, 0, 0);
49     cairo_rectangle (cr, 0, 0, w, h);
50     cairo_fill (cr);
51
52     tbo_drawing_draw (TBO_DRAWING (widget), cr);
53
54     tbo_tooltip_draw (cr);
55
56     // Update drawing helpers
57     if (self->tool)
58         self->tool->drawing (self->tool, cr);
59
60     cairo_destroy(cr);
61
62     return FALSE;
63 }
64
65 static gboolean
66 motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
67 {
68     TboDrawing *self = TBO_DRAWING (widget);
69     event->x = event->x / self->zoom;
70     event->y = event->y / self->zoom;
71
72     if (self->tool)
73         self->tool->on_move (self->tool, widget, event);
74
75     return FALSE;
76 }
77
78 static gboolean
79 button_press_event (GtkWidget *widget, GdkEventButton *event)
80 {
81     TboDrawing *self = TBO_DRAWING (widget);
82     event->x = event->x / self->zoom;
83     event->y = event->y / self->zoom;
84
85     if (self->tool) {
86         if (TBO_IS_TOOL_BUBBLE (self->tool) || TBO_IS_TOOL_DOODLE (self->tool))
87         {
88             tbo_toolbar_set_selected_tool (self->tool->tbo->toolbar, TBO_TOOLBAR_SELECTOR);
89         }
90         self->tool->on_click (self->tool, widget, event);
91     }
92
93     return FALSE;
94 }
95
96 static gboolean
97 button_release_event (GtkWidget *widget, GdkEventButton *event)
98 {
99     TboDrawing *self = TBO_DRAWING (widget);
100     event->x = event->x / self->zoom;
101     event->y = event->y / self->zoom;
102
103     if (self->tool)
104         self->tool->on_release (self->tool, widget, event);
105
106     return FALSE;
107 }
108
109 /* init methods */
110
111 static void
112 tbo_drawing_init (TboDrawing *self)
113 {
114     self->current_frame = NULL;
115     self->zoom = 1;
116     self->comic = NULL;
117     self->tool = NULL;
118 }
119
120 static void
121 tbo_drawing_realize (GtkWidget *widget)
122 {
123     GdkWindow *bin_window;
124
125     if (GTK_WIDGET_CLASS (tbo_drawing_parent_class)->realize)
126             (* GTK_WIDGET_CLASS (tbo_drawing_parent_class)->realize) (widget);
127
128     bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
129     gdk_window_set_events (bin_window,
130                            (gdk_window_get_events (bin_window) |
131                             GDK_BUTTON_PRESS_MASK |
132                             GDK_BUTTON_RELEASE_MASK |
133                             GDK_POINTER_MOTION_MASK));
134 }
135
136 static void
137 tbo_drawing_finalize (GObject *self)
138 {
139     /* Chain up to the parent class */
140     G_OBJECT_CLASS (tbo_drawing_parent_class)->finalize (self);
141 }
142
143 static void
144 tbo_drawing_class_init (TboDrawingClass *klass)
145 {
146     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
147     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
148
149     widget_class->draw = expose_event;
150     widget_class->motion_notify_event = motion_notify_event;
151     widget_class->button_press_event = button_press_event;
152     widget_class->button_release_event = button_release_event;
153     widget_class->realize = tbo_drawing_realize;
154     gobject_class->finalize = tbo_drawing_finalize;
155 }
156
157 /* object functions */
158
159 GtkWidget *
160 tbo_drawing_new ()
161 {
162     GtkWidget *drawing;
163     drawing = g_object_new (TBO_TYPE_DRAWING, NULL);
164     return drawing;
165 }
166
167 GtkWidget *
168 tbo_drawing_new_with_params (Comic *comic)
169 {
170     GtkWidget *drawing = tbo_drawing_new ();
171     TBO_DRAWING (drawing)->comic = comic;
172     gtk_layout_set_size (GTK_LAYOUT (drawing), comic->width+2, comic->height+2);
173
174     return drawing;
175 }
176
177 void
178 tbo_drawing_update (TboDrawing *self)
179 {
180     GtkAllocation alloc;
181     gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
182     gtk_widget_queue_draw_area (GTK_WIDGET (self),
183             0, 0,
184             alloc.width,
185             alloc.height);
186 }
187
188 void
189 tbo_drawing_set_current_frame (TboDrawing *self, Frame *frame)
190 {
191     self->current_frame = frame;
192 }
193
194 Frame *
195 tbo_drawing_get_current_frame (TboDrawing *self)
196 {
197     return self->current_frame;
198 }
199
200 void
201 tbo_drawing_draw (TboDrawing *self, cairo_t *cr)
202 {
203     Frame *frame;
204     GList *frame_list;
205     Page *page;
206
207     int w, h;
208
209     w = self->comic->width;
210     h = self->comic->height;
211     // white background
212     if (tbo_drawing_get_current_frame (self))
213         cairo_set_source_rgb(cr, 0, 0, 0);
214     else
215         cairo_set_source_rgb(cr, 1, 1, 1);
216     cairo_rectangle(cr, 0, 0, w*self->zoom, h*self->zoom);
217     cairo_fill(cr);
218
219     cairo_scale (cr, self->zoom, self->zoom);
220
221     page = tbo_comic_get_current_page (self->comic);
222
223     if (!self->current_frame)
224     {
225         for (frame_list = tbo_page_get_frames (page); frame_list; frame_list = frame_list->next)
226         {
227             // draw each frame
228             frame = (Frame *)frame_list->data;
229             tbo_frame_draw (frame, cr);
230         }
231     }
232     else
233     {
234         tbo_frame_draw_scaled (self->current_frame, cr, w, h);
235     }
236 }
237
238 /* TODO this method should be in TboPage */
239 void
240 tbo_drawing_draw_page (TboDrawing *self, cairo_t *cr, Page *page, gint w, gint h)
241 {
242     Frame *frame;
243     GList *frame_list;
244
245     // white background
246     cairo_set_source_rgb(cr, 1, 1, 1);
247     cairo_rectangle(cr, 0, 0, w, h);
248     cairo_fill(cr);
249
250     for (frame_list = tbo_page_get_frames (page); frame_list; frame_list = frame_list->next)
251     {
252         // draw each frame
253         frame = (Frame *)frame_list->data;
254         tbo_frame_draw (frame, cr);
255     }
256 }
257
258 void
259 tbo_drawing_zoom_in (TboDrawing *self)
260 {
261     self->zoom += ZOOM_STEP;
262     tbo_drawing_adjust_scroll (self);
263 }
264
265 void
266 tbo_drawing_zoom_out (TboDrawing *self)
267 {
268     self->zoom -= ZOOM_STEP;
269     tbo_drawing_adjust_scroll (self);
270 }
271
272 void
273 tbo_drawing_zoom_100 (TboDrawing *self)
274 {
275     self->zoom = 1;
276     tbo_drawing_adjust_scroll (self);
277 }
278
279 void
280 tbo_drawing_zoom_fit (TboDrawing *self)
281 {
282     float z1, z2;
283     int w, h;
284     GtkAllocation alloc;
285     gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
286     w = alloc.width;
287     h = alloc.height;
288
289     z1 = fabs ((float)w / (float)self->comic->width);
290     z2 = fabs ((float)h / (float)self->comic->height);
291     self->zoom = z1 < z2 ? z1 : z2;
292     tbo_drawing_adjust_scroll (self);
293 }
294
295 gdouble
296 tbo_drawing_get_zoom (TboDrawing *self)
297 {
298     return self->zoom;
299 }
300
301 void
302 tbo_drawing_adjust_scroll (TboDrawing *self)
303 {
304     if (!self->comic)
305         return;
306     gtk_layout_set_size (GTK_LAYOUT (self), self->comic->width*self->zoom, self->comic->height*self->zoom);
307     tbo_drawing_update (self);
308 }
309
310 void
311 tbo_drawing_init_dnd (TboDrawing *self, TboWindow *tbo)
312 {
313     gtk_drag_dest_set (GTK_WIDGET (self), GTK_DEST_DEFAULT_ALL, TARGET_LIST, N_TARGETS, GDK_ACTION_COPY);
314     g_signal_connect (self, "drag-data-received", G_CALLBACK(drag_data_received_handl), tbo);
315 }