| 31 #include "container.h" |
31 #include "container.h" |
| 32 #include "menu.h" |
32 #include "menu.h" |
| 33 #include "../common/context.h" |
33 #include "../common/context.h" |
| 34 #include "../common/object.h" |
34 #include "../common/object.h" |
| 35 |
35 |
| |
36 static void imageviewer_destroy(UiImageViewer *iv) { |
| |
37 if(iv->pixbuf) { |
| |
38 g_object_unref(iv->pixbuf); |
| |
39 } |
| |
40 free(iv); |
| |
41 } |
| |
42 |
| |
43 #if GTK_MAJOR_VERSION >= 4 |
| |
44 |
| |
45 static void imageviewer_draw( |
| |
46 GtkDrawingArea *drawingarea, |
| |
47 cairo_t *cr, |
| |
48 int width, |
| |
49 int height, |
| |
50 gpointer userdata) |
| |
51 { |
| |
52 ui_cairo_draw_image(userdata, cr, width, height); |
| |
53 } |
| |
54 |
| |
55 #else |
| |
56 |
| |
57 static gboolean imageviewer_draw(GtkWidget *widget, cairo_t *cr, gpointer userdata) { |
| |
58 int width = gtk_widget_get_allocated_width(widget); |
| |
59 int height = gtk_widget_get_allocated_height(widget); |
| |
60 ui_cairo_draw_image(userdata, cr, width, height); |
| |
61 return FALSE; |
| |
62 } |
| |
63 |
| |
64 #endif |
| 36 |
65 |
| 37 UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args) { |
66 UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args) { |
| 38 UiObject *current = uic_current_obj(obj); |
67 UiObject *current = uic_current_obj(obj); |
| 39 |
68 |
| 40 GtkWidget *scrolledwindow = SCROLLEDWINDOW_NEW(); |
69 GtkWidget *drawingarea = gtk_drawing_area_new(); |
| 41 #if GTK_CHECK_VERSION(4, 0, 0) |
70 GtkWidget *toplevel; |
| 42 GtkWidget *image = gtk_picture_new(); |
71 GtkWidget *widget = drawingarea; |
| 43 #else |
72 |
| 44 GtkWidget *image = gtk_image_new(); |
73 gtk_widget_set_size_request(drawingarea, 100, 100); |
| 45 #endif |
|
| 46 |
|
| 47 ui_set_name_and_style(image, args.name, args.style_class); |
|
| 48 |
74 |
| 49 #if GTK_MAJOR_VERSION < 4 |
75 #if GTK_MAJOR_VERSION < 4 |
| 50 GtkWidget *eventbox = gtk_event_box_new(); |
76 GtkWidget *eventbox = gtk_event_box_new(); |
| 51 SCROLLEDWINDOW_SET_CHILD(scrolledwindow, eventbox); |
77 gtk_container_add(GTK_CONTAINER(eventbox), drawingarea); |
| 52 gtk_container_add(GTK_CONTAINER(eventbox), image); |
78 widget = eventbox; |
| 53 #else |
|
| 54 SCROLLEDWINDOW_SET_CHILD(scrolledwindow, image); |
|
| 55 GtkWidget *eventbox = image; |
|
| 56 #endif |
79 #endif |
| 57 |
80 |
| 58 UI_APPLY_LAYOUT1(current, args); |
81 if(args.scrollarea) { |
| 59 current->container->add(current->container, scrolledwindow, TRUE); |
82 toplevel = SCROLLEDWINDOW_NEW(); |
| |
83 SCROLLEDWINDOW_SET_CHILD(toplevel, widget); |
| |
84 args.adjustwidgetsize = TRUE; |
| |
85 } else { |
| |
86 toplevel = widget; |
| |
87 } |
| |
88 |
| |
89 UiImageViewer *imgviewer = malloc(sizeof(UiImageViewer)); |
| |
90 memset(imgviewer, 0, sizeof(UiImageViewer)); |
| |
91 imgviewer->obj = obj; |
| |
92 imgviewer->onbuttonpress = args.onbuttonpress; |
| |
93 imgviewer->onbuttonpressdata = args.onbuttonpressdata; |
| |
94 imgviewer->onbuttonrelease = args.onbuttonrelease; |
| |
95 imgviewer->onbuttonreleasedata = args.onbuttonreleasedata; |
| |
96 if(args.image_padding > 0) { |
| |
97 imgviewer->padding_left = args.image_padding; |
| |
98 imgviewer->padding_right = args.image_padding; |
| |
99 imgviewer->padding_top = args.image_padding; |
| |
100 imgviewer->padding_bottom = args.image_padding; |
| |
101 } else { |
| |
102 imgviewer->padding_left = args.image_padding_left; |
| |
103 imgviewer->padding_right = args.image_padding_right; |
| |
104 imgviewer->padding_top = args.image_padding_top; |
| |
105 imgviewer->padding_bottom = args.image_padding_bottom; |
| |
106 } |
| |
107 imgviewer->adjustwidgetsize = args.adjustwidgetsize; |
| |
108 imgviewer->autoscale = args.autoscale; |
| |
109 imgviewer->useradjustable = args.useradjustable; |
| |
110 imgviewer->zoom_scale = 20; |
| |
111 |
| |
112 g_object_set_data_full(G_OBJECT(drawingarea), "uiimageviewer", imgviewer, (GDestroyNotify)imageviewer_destroy); |
| 60 |
113 |
| 61 UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_GENERIC); |
114 UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_GENERIC); |
| |
115 imgviewer->var = var; |
| |
116 imgviewer->widget = drawingarea; |
| |
117 |
| 62 if(var) { |
118 if(var) { |
| 63 UiGeneric *value = var->value; |
119 UiGeneric *value = var->value; |
| 64 value->get = ui_imageviewer_get; |
120 value->get = ui_imageviewer_get; |
| 65 value->get_type = ui_imageviewer_get_type; |
121 value->get_type = ui_imageviewer_get_type; |
| 66 value->set = ui_imageviewer_set; |
122 value->set = ui_imageviewer_set; |
| 67 value->obj = image; |
123 value->obj = imgviewer; |
| 68 if(value->value && value->type && !strcmp(value->type, UI_IMAGE_OBJECT_TYPE)) { |
124 if(value->value && value->type && !strcmp(value->type, UI_IMAGE_OBJECT_TYPE)) { |
| 69 GdkPixbuf *pixbuf = value->value; |
125 GdkPixbuf *pixbuf = value->value; |
| 70 value->value = NULL; |
126 value->value = NULL; |
| 71 ui_imageviewer_set(value, pixbuf, UI_IMAGE_OBJECT_TYPE); |
127 ui_imageviewer_set(value, pixbuf, UI_IMAGE_OBJECT_TYPE); |
| |
128 g_object_unref(pixbuf); |
| 72 } |
129 } |
| 73 } |
130 } |
| 74 |
131 |
| |
132 #if GTK_MAJOR_VERSION >= 4 |
| |
133 gtk_drawing_area_set_draw_func( |
| |
134 GTK_DRAWING_AREA(drawingarea), |
| |
135 imageviewer_draw, |
| |
136 imgviewer, |
| |
137 NULL); |
| |
138 |
| |
139 if(args.useradjustable) { |
| |
140 gtk_widget_set_focusable(drawingarea, TRUE); |
| |
141 } |
| |
142 |
| |
143 GtkEventController *scrollcontroller = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_VERTICAL); |
| |
144 g_signal_connect(scrollcontroller, "scroll", G_CALLBACK(ui_imageviewer_scroll), imgviewer); |
| |
145 gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(scrollcontroller)); |
| |
146 |
| |
147 GtkGesture *drag = gtk_gesture_drag_new(); |
| |
148 g_signal_connect(drag, "drag-begin", G_CALLBACK(ui_imageviewer_drag_begin_cb), imgviewer); |
| |
149 g_signal_connect(drag, "drag-end", G_CALLBACK(ui_imageviewer_drag_end_cb), imgviewer); |
| |
150 g_signal_connect(drag, "drag-update", G_CALLBACK(ui_imageviewer_drag_update_cb), imgviewer); |
| |
151 gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(drag)); |
| |
152 |
| |
153 GtkGesture *click = gtk_gesture_click_new(); |
| |
154 g_signal_connect(click, "pressed", G_CALLBACK(ui_imageviewer_pressed_cb), imgviewer); |
| |
155 g_signal_connect(click, "released", G_CALLBACK(ui_imageviewer_released_cb), imgviewer); |
| |
156 gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(click)); |
| |
157 |
| |
158 #elif GTK_MAJOR_VERSION == 3 |
| |
159 g_signal_connect( |
| |
160 drawingarea, |
| |
161 "draw", |
| |
162 G_CALLBACK(imageviewer_draw), |
| |
163 imgviewer); |
| |
164 |
| |
165 gtk_widget_add_events(eventbox, GDK_SCROLL_MASK); |
| |
166 |
| |
167 g_signal_connect( |
| |
168 eventbox, |
| |
169 "scroll-event", |
| |
170 G_CALLBACK(ui_imageviewer_scroll_event), |
| |
171 imgviewer); |
| |
172 g_signal_connect( |
| |
173 eventbox, |
| |
174 "button-press-event", |
| |
175 G_CALLBACK(ui_imageviewer_button_press_event), |
| |
176 imgviewer); |
| |
177 g_signal_connect( |
| |
178 eventbox, |
| |
179 "button-release-event", |
| |
180 G_CALLBACK(ui_imageviewer_button_release_event), |
| |
181 imgviewer); |
| |
182 |
| |
183 #endif |
| |
184 |
| 75 if(args.contextmenu) { |
185 if(args.contextmenu) { |
| 76 UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, eventbox); |
186 UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, widget); |
| 77 ui_widget_set_contextmenu(eventbox, menu); |
187 ui_widget_set_contextmenu(widget, menu); |
| 78 } |
188 } |
| 79 |
189 |
| 80 return scrolledwindow; |
190 UI_APPLY_LAYOUT1(current, args); |
| |
191 current->container->add(current->container, toplevel, TRUE); |
| |
192 |
| |
193 return toplevel; |
| |
194 } |
| |
195 |
| |
196 static void imageviewer_reset(UiImageViewer *imgviewer) { |
| |
197 imgviewer->isautoscaled = FALSE; |
| |
198 imgviewer->transx = 0; |
| |
199 imgviewer->transy; |
| |
200 imgviewer->begin_transx = 0; |
| |
201 imgviewer->begin_transy = 0; |
| |
202 imgviewer->scale = 1; |
| |
203 imgviewer->user_scale = 1; |
| |
204 } |
| |
205 |
| |
206 UIWIDGET ui_imageviewer_reset(UIWIDGET w) { |
| |
207 UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer"); |
| |
208 if(imgviewer) { |
| |
209 imageviewer_reset(imgviewer); |
| |
210 gtk_widget_queue_draw(w); |
| |
211 } |
| |
212 } |
| |
213 |
| |
214 UIWIDGET ui_imageviewer_set_autoscale(UIWIDGET w, UiBool set) { |
| |
215 UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer"); |
| |
216 if(imgviewer) { |
| |
217 imgviewer->autoscale = set; |
| |
218 } |
| |
219 } |
| |
220 |
| |
221 UIWIDGET ui_imageviewer_set_adjustwidgetsize(UIWIDGET w, UiBool set) { |
| |
222 UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer"); |
| |
223 if(imgviewer) { |
| |
224 imgviewer->adjustwidgetsize = set; |
| |
225 } |
| |
226 } |
| |
227 |
| |
228 UIWIDGET ui_imageviewer_set_useradjustable(UIWIDGET w, UiBool set) { |
| |
229 UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer"); |
| |
230 if(imgviewer) { |
| |
231 imgviewer->useradjustable = set; |
| |
232 } |
| |
233 } |
| |
234 |
| |
235 void ui_cairo_draw_image(UiImageViewer *imgviewer, cairo_t *cr, int width, int height) { |
| |
236 if(!imgviewer->pixbuf) { |
| |
237 return; |
| |
238 } |
| |
239 |
| |
240 GdkPixbuf *pixbuf = imgviewer->pixbuf; |
| |
241 double dpixwidth = (double)gdk_pixbuf_get_width(pixbuf); |
| |
242 double dpixheight = (double)gdk_pixbuf_get_height(pixbuf); |
| |
243 |
| |
244 double dwidth = width; |
| |
245 double dheight = height; |
| |
246 double scale = 1; |
| |
247 // if autoscale is enabled, scale the image to fill available space |
| |
248 // if useradjustable is also enabled, the autoscaling is only done once |
| |
249 if(imgviewer->autoscale && imgviewer->scale != 0) { |
| |
250 if(!imgviewer->isautoscaled) { |
| |
251 scale = dwidth / dpixwidth; |
| |
252 if(dpixheight * scale > dheight) { |
| |
253 scale = dheight / dpixheight; |
| |
254 } |
| |
255 |
| |
256 if(imgviewer->useradjustable) { |
| |
257 imgviewer->isautoscaled = TRUE; |
| |
258 } |
| |
259 |
| |
260 imgviewer->scale = scale; |
| |
261 } else { |
| |
262 scale = imgviewer->scale; |
| |
263 } |
| |
264 |
| |
265 imgviewer->user_scale = scale; |
| |
266 } else { |
| |
267 // user-adjusted scaling |
| |
268 //scale = 1 + ((double)imgviewer->zoom / (double)imgviewer->zoom_scale); |
| |
269 scale = imgviewer->user_scale; |
| |
270 } |
| |
271 |
| |
272 dpixwidth *= scale; |
| |
273 dpixheight *= scale; |
| |
274 double x = (dwidth - dpixwidth) / 2; |
| |
275 double y = (dheight - dpixheight) / 2; |
| |
276 |
| |
277 x += imgviewer->transx; |
| |
278 y += imgviewer->transy; |
| |
279 |
| |
280 cairo_translate(cr, x, y); |
| |
281 cairo_scale(cr, scale, scale); |
| |
282 |
| |
283 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); |
| |
284 cairo_paint(cr); |
| 81 } |
285 } |
| 82 |
286 |
| 83 void* ui_imageviewer_get(UiGeneric *g) { |
287 void* ui_imageviewer_get(UiGeneric *g) { |
| |
288 UiImageViewer *imgviewer = g->obj; |
| |
289 g->value = imgviewer->pixbuf; |
| 84 return g->value; |
290 return g->value; |
| 85 } |
291 } |
| 86 |
292 |
| 87 const char* ui_imageviewer_get_type(UiGeneric *g) { |
293 const char* ui_imageviewer_get_type(UiGeneric *g) { |
| 88 |
294 return UI_IMAGE_OBJECT_TYPE; |
| 89 } |
295 } |
| 90 |
296 |
| 91 int ui_imageviewer_set(UiGeneric *g, void *value, const char *type) { |
297 int ui_imageviewer_set(UiGeneric *g, void *value, const char *type) { |
| 92 if(!type || strcmp(type, UI_IMAGE_OBJECT_TYPE)) { |
298 if(!type || strcmp(type, UI_IMAGE_OBJECT_TYPE)) { |
| 93 return 1; |
299 return 1; |
| 94 } |
300 } |
| 95 |
301 |
| 96 // TODO: do we need to free the previous value here? |
|
| 97 |
|
| 98 g->value = value; |
|
| 99 g->type = type; |
|
| 100 GdkPixbuf *pixbuf = value; |
302 GdkPixbuf *pixbuf = value; |
| 101 |
303 g_object_ref(pixbuf); |
| 102 if(pixbuf) { |
304 |
| |
305 UiImageViewer *imgviewer = g->obj; |
| |
306 g->value = pixbuf; |
| |
307 |
| |
308 imageviewer_reset(imgviewer); |
| |
309 |
| |
310 if(imgviewer->pixbuf) { |
| |
311 g_object_unref(imgviewer->pixbuf); |
| |
312 } |
| |
313 imgviewer->pixbuf = pixbuf; |
| |
314 |
| |
315 if(imgviewer->adjustwidgetsize && !imgviewer->autoscale) { |
| 103 int width = gdk_pixbuf_get_width(pixbuf); |
316 int width = gdk_pixbuf_get_width(pixbuf); |
| 104 int height = gdk_pixbuf_get_height(pixbuf); |
317 int height = gdk_pixbuf_get_height(pixbuf); |
| 105 |
318 gtk_widget_set_size_request(imgviewer->widget, width, height); |
| 106 #if GTK_CHECK_VERSION(4, 0, 0) |
319 } |
| 107 GdkTexture *texture = gdk_texture_new_for_pixbuf(pixbuf); |
320 gtk_widget_queue_draw(imgviewer->widget); |
| 108 gtk_picture_set_paintable(GTK_PICTURE(g->obj), GDK_PAINTABLE(texture)); |
|
| 109 #else |
|
| 110 gtk_image_set_from_pixbuf(GTK_IMAGE(g->obj), pixbuf); |
|
| 111 #endif |
|
| 112 gtk_widget_set_size_request(g->obj, width, height); |
|
| 113 } |
|
| 114 |
|
| 115 |
321 |
| 116 return 0; |
322 return 0; |
| 117 } |
323 } |
| 118 |
324 |
| 119 |
325 |
| 125 return 1; |
331 return 1; |
| 126 } |
332 } |
| 127 |
333 |
| 128 if(obj->set) { |
334 if(obj->set) { |
| 129 obj->set(obj, pixbuf, UI_IMAGE_OBJECT_TYPE); |
335 obj->set(obj, pixbuf, UI_IMAGE_OBJECT_TYPE); |
| |
336 g_object_unref(pixbuf); |
| 130 } else { |
337 } else { |
| 131 obj->value = pixbuf; |
338 obj->value = pixbuf; |
| 132 } |
339 } |
| 133 |
340 |
| 134 return 0; |
341 return 0; |
| 135 } |
342 } |
| |
343 |
| |
344 void ui_image_ref(UIIMAGE img) { |
| |
345 g_object_ref(img); |
| |
346 } |
| |
347 |
| |
348 void ui_image_unref(UIIMAGE img) { |
| |
349 g_object_unref(img); |
| |
350 } |
| |
351 |
| |
352 #if GTK_MAJOR_VERSION >= 4 |
| |
353 |
| |
354 gboolean ui_imageviewer_scroll( |
| |
355 GtkEventControllerScroll *widget, |
| |
356 gdouble dx, |
| |
357 gdouble dy, |
| |
358 gpointer userdata) |
| |
359 { |
| |
360 UiImageViewer *imgviewer = userdata; |
| |
361 if(imgviewer->useradjustable) { |
| |
362 double step = dy / imgviewer->zoom_scale; |
| |
363 if(imgviewer->user_scale - step > 0) { |
| |
364 imgviewer->user_scale -= step; |
| |
365 } |
| |
366 |
| |
367 imgviewer->scale = 0; // disable autoscale |
| |
368 gtk_widget_queue_draw(imgviewer->widget); |
| |
369 return TRUE; |
| |
370 } |
| |
371 return FALSE; |
| |
372 } |
| |
373 |
| |
374 void ui_imageviewer_drag_begin_cb( |
| |
375 GtkGestureDrag *self, |
| |
376 gdouble start_x, |
| |
377 gdouble start_y, |
| |
378 gpointer userdata) |
| |
379 { |
| |
380 UiImageViewer *imgviewer = userdata; |
| |
381 imgviewer->begin_transx = imgviewer->transx; |
| |
382 imgviewer->begin_transy = imgviewer->transy; |
| |
383 } |
| |
384 |
| |
385 void ui_imageviewer_drag_end_cb( |
| |
386 GtkGestureDrag* self, |
| |
387 gdouble x, |
| |
388 gdouble y, |
| |
389 gpointer userdata) |
| |
390 { |
| |
391 |
| |
392 } |
| |
393 |
| |
394 void ui_imageviewer_drag_update_cb( |
| |
395 GtkGestureDrag *self, |
| |
396 gdouble x, |
| |
397 gdouble y, |
| |
398 gpointer userdata) |
| |
399 { |
| |
400 UiImageViewer *imgviewer = userdata; |
| |
401 if(imgviewer->useradjustable) { |
| |
402 imgviewer->transx = imgviewer->begin_transx + x; |
| |
403 imgviewer->transy = imgviewer->begin_transy + y; |
| |
404 gtk_widget_queue_draw(imgviewer->widget); |
| |
405 } |
| |
406 } |
| |
407 |
| |
408 static void imgviewer_button_event( |
| |
409 GtkGestureClick *gesture, |
| |
410 UiImageViewer *imgviewer, |
| |
411 ui_callback callback, |
| |
412 void *userdata) |
| |
413 { |
| |
414 UiEvent event; |
| |
415 event.obj = imgviewer->obj; |
| |
416 event.window = event.obj->window; |
| |
417 event.document = event.obj->ctx->document; |
| |
418 event.eventdata = NULL; |
| |
419 event.intval = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(gesture)); |
| |
420 event.set = 0; |
| |
421 callback(&event, userdata); |
| |
422 } |
| |
423 |
| |
424 void ui_imageviewer_pressed_cb( |
| |
425 GtkGestureClick *self, |
| |
426 gint n_press, |
| |
427 gdouble x, |
| |
428 gdouble y, |
| |
429 gpointer userdata) |
| |
430 { |
| |
431 UiImageViewer *imgviewer = userdata; |
| |
432 if(imgviewer->onbuttonpress) { |
| |
433 imgviewer_button_event(self, imgviewer, imgviewer->onbuttonpress, imgviewer->onbuttonpressdata); |
| |
434 } |
| |
435 } |
| |
436 |
| |
437 void ui_imageviewer_released_cb( |
| |
438 GtkGestureClick *self, |
| |
439 gint n_press, |
| |
440 gdouble x, |
| |
441 gdouble y, |
| |
442 gpointer userdata) |
| |
443 { |
| |
444 UiImageViewer *imgviewer = userdata; |
| |
445 if(imgviewer->onbuttonrelease) { |
| |
446 imgviewer_button_event(self, imgviewer, imgviewer->onbuttonrelease, imgviewer->onbuttonreleasedata); |
| |
447 } |
| |
448 } |
| |
449 |
| |
450 #else |
| |
451 |
| |
452 gboolean ui_imageviewer_scroll_event( |
| |
453 GtkWidget *widget, |
| |
454 GdkEventScroll event, |
| |
455 gpointer userdata) |
| |
456 { |
| |
457 // TODO |
| |
458 return FALSE; |
| |
459 } |
| |
460 |
| |
461 gboolean ui_imageviewer_button_press_event( |
| |
462 GtkWidget *widget, |
| |
463 GdkEventButton event, |
| |
464 gpointer userdata) |
| |
465 { |
| |
466 // TODO |
| |
467 return FALSE; |
| |
468 } |
| |
469 |
| |
470 gboolean ui_imageviewer_button_release_event( |
| |
471 GtkWidget *widget, |
| |
472 GdkEventButton event, |
| |
473 gpointer userdata) |
| |
474 { |
| |
475 // TODO |
| |
476 return FALSE; |
| |
477 } |
| |
478 |
| |
479 #endif |