1 /* Bluefish HTML Editor
2 * image.c - the thumbnail/multi-thumbnail dialogs
3 *
4 * Copyright (C) 2003-2012 Olivier Sessink
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 /* indented with indent -psl -ts4 -kr -l110 */
20
21 /*#define DEBUG */
22
23 #include <string.h>
24
25 #include "image.h"
26 #include "cap.h"
27 #include "htmlbar.h"
28 #include "html_diag.h"
29 #include "../bf_lib.h"
30 #include "../dialog_utils.h"
31 #include "../document.h"
32 #include "../file.h"
33 #include "../gtk_easy.h"
34 #include "../stringlist.h"
35
36 static GdkPixbufLoader *
pbloader_from_filename(const gchar * filename)37 pbloader_from_filename(const gchar * filename)
38 {
39 GdkPixbufLoader *pbloader;
40 GError *error = NULL;
41 if (filename) {
42 gchar *ext = strrchr(filename, '.');
43 if (ext) {
44 gchar *tmp2 = g_utf8_strdown(ext + 1, -1);
45 if (strcmp(tmp2, "jpg") == 0) {
46 pbloader = gdk_pixbuf_loader_new_with_type("jpeg", &error);
47 } else {
48 pbloader = gdk_pixbuf_loader_new_with_type(tmp2, &error);
49 }
50 if (error) {
51 pbloader = gdk_pixbuf_loader_new(); /* try to guess from the data */
52 g_error_free(error);
53 }
54 g_free(tmp2);
55 return pbloader;
56 }
57 }
58 return gdk_pixbuf_loader_new();
59 }
60
61 static gchar *
create_thumbnail_filename(gchar * filename)62 create_thumbnail_filename(gchar * filename)
63 {
64 gchar *retval, *tmp;
65 gint len = 0, size;
66 tmp = strrchr(filename, '.');
67 if (tmp) {
68 len = strlen(tmp);
69 }
70 size =
71 strlen(filename) - len + strlen(main_v->props.image_thumbnailstring) +
72 strlen(main_v->props.image_thumbnailtype) + 2;
73 retval = g_malloc0(size * sizeof(gchar));
74 DEBUG_MSG("create_thumbnail_filename, size=%d bytes at %p\n", size, retval);
75 retval = strncpy(retval, filename, strlen(filename) - len);
76 retval = strcat(retval, main_v->props.image_thumbnailstring);
77 retval = strcat(retval, ".");
78 retval = strcat(retval, main_v->props.image_thumbnailtype);
79
80 return retval;
81 }
82
83 typedef struct {
84 Thtml_diag *dg;
85 GtkWidget *message;
86 GtkWidget *frame;
87 GdkPixbuf *pb;
88 GtkWidget *im;
89 GFile *full_uri;
90
91 GdkPixbufLoader *pbloader;
92 Topenfile *of;
93
94 /* and some options for the thumbnails */
95 GtkAdjustment *adjustment;
96 guint adj_changed_id;
97 } Timage_diag;
98
99 void
image_diag_destroy_cb(GtkWidget * widget,Timage_diag * imdg)100 image_diag_destroy_cb(GtkWidget * widget, Timage_diag * imdg)
101 {
102 html_diag_destroy_cb(widget, imdg->dg);
103 if (imdg->pb) {
104 g_object_unref(imdg->pb);
105 }
106 if (imdg->full_uri) {
107 g_object_unref(imdg->full_uri);
108 }
109 g_free(imdg);
110 }
111
112 static TcheckNsave_return
async_thumbsave_lcb(TcheckNsave_status status,GError * gerror,gpointer callback_data)113 async_thumbsave_lcb(TcheckNsave_status status, GError * gerror, gpointer callback_data)
114 {
115 DEBUG_MSG("async_thumbsave_lcb, status=%d\n", status);
116 /* TODO: handle error */
117 if (gerror) {
118 g_print("failed to save thumbnail: %s\n",gerror->message);
119 }
120 return CHECKNSAVE_CONT;
121 }
122
123 static void
image_insert_dialogok_lcb(GtkWidget * widget,Timage_diag * imdg)124 image_insert_dialogok_lcb(GtkWidget * widget, Timage_diag * imdg)
125 {
126 gchar *thestring, *finalstring;
127 gchar *thumbnailfilename, *filename;
128 gchar *tmp1, *tmp2;
129 gchar *buffer;
130 gsize buflen;
131 gint w, h;
132 GError *error = NULL;
133 GdkPixbuf *tmp_im;
134 GFile *fullthumbfilename;
135
136 filename = gtk_editable_get_chars(GTK_EDITABLE(imdg->dg->entry[0]), 0, -1);
137 if (strlen(filename)) {
138 thumbnailfilename = create_thumbnail_filename(filename);
139 /* we should use the full path to create the thumbnail filename */
140
141 tmp1 = g_file_get_uri(imdg->full_uri);
142 tmp2 = create_thumbnail_filename(tmp1);
143 fullthumbfilename = g_file_new_for_uri(tmp2);
144
145 g_free(tmp1);
146 g_free(tmp2);
147
148 #ifdef DEBUG
149 gchar *path = g_file_get_path(fullthumbfilename);
150 DEBUG_MSG("image_insert_dialogok_lcb, thumbnail will be stored at %s\n", path);
151 g_free(path);
152 #endif
153 w = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(imdg->dg->spin[0]));
154 h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(imdg->dg->spin[1]));
155 tmp_im = gdk_pixbuf_scale_simple(imdg->pb, w, h, GDK_INTERP_BILINEAR);
156
157 if (strcmp(main_v->props.image_thumbnailtype, "jpeg") == 0) {
158 gdk_pixbuf_save_to_buffer(tmp_im, &buffer, &buflen, main_v->props.image_thumbnailtype, &error,
159 "quality", "50", NULL);
160 /* gdk_pixbuf_save(tmp_im,fullthumbfilename,main_v->props.image_thumbnailtype,&error, "quality", "50",NULL); */
161 } else {
162 gdk_pixbuf_save_to_buffer(tmp_im, &buffer, &buflen, main_v->props.image_thumbnailtype, &error,
163 NULL);
164 /*gdk_pixbuf_save(tmp_im,fullthumbfilename,main_v->props.image_thumbnailtype,&error, NULL); */
165 }
166 g_object_unref(tmp_im);
167 if (error) {
168 g_print("ERROR while saving thumbnail to buffer: %s\n", error->message);
169 g_error_free(error);
170 } else {
171 GError *error = NULL;
172 GFileInfo *finfo;
173 Trefcpointer *refbuf = refcpointer_new(buffer);
174
175 finfo = g_file_query_info(fullthumbfilename, BF_FILEINFO, G_FILE_QUERY_INFO_NONE, NULL, &error);
176 if (error != NULL) {
177 g_print("image_insert_dialogok_lcb: %s\n ", error->message);
178 g_error_free(error);
179 }
180 #ifdef DEBUG
181 gchar *path = g_file_get_path(fullthumbfilename);
182 DEBUG_MSG("image_insert_dialogok_lcb, starting async save to %s\n", path);
183 g_free(path);
184 #endif
185
186 file_checkNsave_uri_async(fullthumbfilename, finfo, refbuf, buflen, FALSE, FALSE,
187 (CheckNsaveAsyncCallback) async_thumbsave_lcb, NULL, imdg->dg->bfwin);
188 refcpointer_unref(refbuf);
189 }
190
191 g_object_unref(fullthumbfilename);
192
193 thestring =
194 g_strconcat(cap("<A HREF=\""), filename, cap("\"><IMG SRC=\""), thumbnailfilename, "\"", NULL);
195 g_free(filename);
196 g_free(thumbnailfilename);
197
198 thestring =
199 insert_integer_if_spin(imdg->dg->spin[0], cap("WIDTH"), thestring,
200 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(imdg->dg->check[0])), 0);
201 thestring =
202 insert_integer_if_spin(imdg->dg->spin[1], cap("HEIGHT"), thestring,
203 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(imdg->dg->check[1])), 0);
204 if (!get_curlang_option_value(imdg->dg->bfwin, lang_is_XHTML)) {
205 thestring = insert_if_spin(imdg->dg->spin[2], cap("BORDER"), thestring, FALSE);
206 }
207 thestring = insert_if_spin(imdg->dg->spin[3], cap("HSPACE"), thestring, FALSE);
208 thestring = insert_if_spin(imdg->dg->spin[4], cap("VSPACE"), thestring, FALSE);
209 thestring = insert_string_if_entry(GTK_ENTRY(imdg->dg->entry[1]), cap("NAME"), thestring, NULL);
210 thestring = insert_string_if_entry(GTK_ENTRY(imdg->dg->entry[2]), cap("ALT"), thestring, "");
211 thestring = insert_string_if_entry(GTK_ENTRY(imdg->dg->entry[3]), cap("USEMAP"), thestring, NULL);
212 thestring =
213 insert_string_if_combobox(GTK_COMBO_BOX(imdg->dg->combo[0]), cap("ALIGN"), thestring, NULL);
214 thestring = insert_string_if_entry(GTK_ENTRY(imdg->dg->entry[4]), NULL, thestring, NULL);
215
216 finalstring = g_strconcat(thestring, get_curlang_option_value(imdg->dg->bfwin, self_close_singleton_tags) ? " />" : ">", NULL);
217 g_free(thestring);
218
219 if (imdg->dg->range.end == -1) {
220 doc_insert_two_strings(imdg->dg->doc, finalstring, cap("</a>"));
221 } else {
222 doc_replace_text(imdg->dg->doc, finalstring, imdg->dg->range.pos, imdg->dg->range.end);
223 }
224
225 g_free(finalstring);
226 }
227 image_diag_destroy_cb(NULL, imdg);
228 }
229
230 void
image_diag_cancel_clicked_cb(GtkWidget * widget,gpointer data)231 image_diag_cancel_clicked_cb(GtkWidget * widget, gpointer data)
232 {
233 image_diag_destroy_cb(NULL, data);
234 }
235
236 static void
image_diag_finish(Timage_diag * imdg,GCallback ok_func)237 image_diag_finish(Timage_diag * imdg, GCallback ok_func)
238 {
239 GtkWidget *align, *hbox;
240
241 #if GTK_CHECK_VERSION(3,0,0)
242 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
243 #else
244 hbox = gtk_hbutton_box_new();
245 #endif
246 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
247 gtk_box_set_spacing(GTK_BOX(hbox), 6);
248
249 imdg->dg->obut = bf_stock_ok_button(ok_func, imdg);
250 imdg->dg->cbut = bf_stock_cancel_button(G_CALLBACK(image_diag_cancel_clicked_cb), imdg);
251
252 gtk_box_pack_start(GTK_BOX(hbox), imdg->dg->cbut, FALSE, FALSE, 0);
253 gtk_box_pack_start(GTK_BOX(hbox), imdg->dg->obut, FALSE, FALSE, 0);
254 gtk_window_set_default(GTK_WINDOW(imdg->dg->dialog), imdg->dg->obut);
255
256 align = gtk_alignment_new(0, 0, 1, 0);
257 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 12, 0, 0, 0);
258 gtk_container_add(GTK_CONTAINER(align), hbox);
259 gtk_box_pack_start(GTK_BOX(imdg->dg->vbox), align, FALSE, FALSE, 0);
260 gtk_widget_show_all(GTK_WIDGET(imdg->dg->dialog));
261 }
262
263 static void
image_dialog_set_pixbuf(Timage_diag * imdg)264 image_dialog_set_pixbuf(Timage_diag * imdg)
265 {
266 gfloat toobig, pb_width, pd_height;
267 GdkPixbuf *tmp_pb;
268 if (!imdg->pb) {
269 return;
270 }
271
272 pb_width = (gfloat)gdk_pixbuf_get_width(imdg->pb);
273 pd_height = (gfloat)gdk_pixbuf_get_height(imdg->pb);
274 if (imdg->dg->bfwin) {
275 Thtmlbarsession *hbs;
276 hbs = g_hash_table_lookup(htmlbar_v.lookup, imdg->dg->bfwin->session);
277 toobig = pb_width / ((gfloat)hbs->thumbnailwidth);
278 DEBUG_MSG("initialize toobig as %f, using session thumbnailwidth %d and pixbuf width %f\n",toobig,hbs->thumbnailwidth,pb_width);
279 } else {
280 toobig = 1.0;
281 if ((pb_width / 250.0) > toobig) {
282 toobig = pb_width / 250.0;
283 }
284
285 if ((pd_height / 300.0) > toobig) {
286 toobig = pd_height / 300.0;
287 }
288 }
289 /* because the spin buttons in the html dialogs are designed that they can have an empty string value ""
290 they will not accept a number as long as there is an empty string in them */
291 gtk_entry_set_text(GTK_ENTRY(imdg->dg->spin[0]), "1");
292 gtk_entry_set_text(GTK_ENTRY(imdg->dg->spin[1]), "1");
293
294 gtk_spin_button_set_value(GTK_SPIN_BUTTON(imdg->dg->spin[0]), (pb_width / toobig));
295 gtk_spin_button_set_value(GTK_SPIN_BUTTON(imdg->dg->spin[1]), (pd_height / toobig));
296 g_signal_handler_block(G_OBJECT(imdg->adjustment), imdg->adj_changed_id);
297 gtk_adjustment_set_value(GTK_ADJUSTMENT(imdg->adjustment), 1.0 / toobig);
298 g_signal_handler_unblock(G_OBJECT(imdg->adjustment), imdg->adj_changed_id);
299
300 tmp_pb =
301 gdk_pixbuf_scale_simple(imdg->pb, (gint)(pb_width / toobig), (gint)(pd_height / toobig),
302 main_v->globses.image_thumbnail_refresh_quality ? GDK_INTERP_BILINEAR :
303 GDK_INTERP_NEAREST);
304
305 if (GTK_IS_WIDGET(imdg->im)) {
306 DEBUG_MSG("imdg->im == %p\n", imdg->im);
307 DEBUG_MSG("gtk_widget_destroy() %p\n", imdg->im);
308 gtk_widget_destroy(imdg->im);
309 }
310
311 imdg->im = gtk_image_new_from_pixbuf(tmp_pb);
312
313 g_object_unref(tmp_pb);
314 /*gtk_container_remove(GTK_CONTAINER(imdg->frame),imdg->message); */
315 gtk_widget_destroy(imdg->message);
316 imdg->message = NULL;
317
318 gtk_container_add(GTK_CONTAINER(imdg->frame), imdg->im);
319 gtk_widget_show(imdg->im);
320 DEBUG_MSG("imdg->im == %p\n", imdg->im);
321 DEBUG_MSG("image_filename_changed() finished. GTK_IS_WIDGET(imdg->im) == %d\n", GTK_IS_WIDGET(imdg->im));
322 }
323
324 static void
image_loaded_lcb(Topenfile_status status,GError * gerror,Trefcpointer * refp,goffset buflen,gpointer callback_data)325 image_loaded_lcb(Topenfile_status status, GError * gerror, Trefcpointer * refp, goffset buflen,
326 gpointer callback_data)
327 {
328 Timage_diag *imdg = callback_data;
329 gboolean cleanup = TRUE;
330 switch (status) {
331 case OPENFILE_ERROR:
332 case OPENFILE_ERROR_NOCHANNEL:
333 case OPENFILE_ERROR_NOREAD:
334 /* TODO: use error info in gerror */
335 gtk_label_set_text(GTK_LABEL(imdg->message), _("Loading image failed..."));
336 break;
337 case OPENFILE_ERROR_CANCELLED:
338 /* should we warn the user ?? */
339 gdk_pixbuf_loader_close(imdg->pbloader, NULL);
340 break;
341 case OPENFILE_CHANNEL_OPENED:
342 /* do nothing */
343 cleanup = FALSE;
344 break;
345 case OPENFILE_FINISHED:{
346 GError *error = NULL;
347 if (gdk_pixbuf_loader_write(imdg->pbloader, (const guchar *) refp->data, buflen, &error)
348 && gdk_pixbuf_loader_close(imdg->pbloader, &error)) {
349 imdg->pb = gdk_pixbuf_loader_get_pixbuf(imdg->pbloader);
350 if (imdg->pb) {
351 g_object_ref(imdg->pb);
352 image_dialog_set_pixbuf(imdg);
353 }
354 }
355 }
356 break;
357 }
358 if (cleanup) {
359 g_object_unref(imdg->pbloader);
360 imdg->pbloader = NULL;
361 imdg->of = NULL;
362 }
363 }
364
365 static void
image_filename_changed(GtkWidget * widget,Timage_diag * imdg)366 image_filename_changed(GtkWidget * widget, Timage_diag * imdg)
367 {
368 const gchar *filename;
369 gchar *tmp;
370 GFile *fullfilename = NULL;
371
372 DEBUG_MSG("image_filename_changed() started. GTK_IS_WIDGET(imdg->im) == %d\n", GTK_IS_WIDGET(imdg->im));
373 if (imdg->pb) {
374 g_object_unref(imdg->pb);
375 }
376 if (imdg->of) {
377 openfile_cancel(imdg->of);
378 }
379 DEBUG_MSG("image_filename_changed: filename=%s\n", gtk_entry_get_text(GTK_ENTRY(imdg->dg->entry[0])));
380
381 /* the entry usually has a relative filename, so we should make it absolute
382 using the basedir of the document */
383 filename = gtk_entry_get_text(GTK_ENTRY(imdg->dg->entry[0]));
384 /* we should use the full path to create the thumbnail filename */
385 tmp = strstr(filename, "://");
386 if ((tmp == NULL && filename[0] != '/') && imdg->dg->doc->uri) {
387 /* a relative path. create the absolute path. */
388 GFile *parent = g_file_get_parent(imdg->dg->doc->uri);
389 gchar *tmp;
390 /* filename is an URI, not a file path. the function g_file_resolve_relative_path
391 does not handle URI parts like %20 (a space) */
392 tmp = g_uri_unescape_string(filename, NULL);
393 DEBUG_MSG("unescaped filename=%s\n", tmp);
394 fullfilename = g_file_resolve_relative_path(parent, tmp);
395 g_free(tmp);
396 g_object_unref(parent);
397 } else if (tmp != NULL || filename[0] == '/') {
398 fullfilename = g_file_new_for_uri(filename);
399 } else {
400 return;
401 }
402
403 if (fullfilename && g_file_query_exists(fullfilename, NULL)) {
404 gchar *name, *msg;
405 gchar *path = g_file_get_basename(fullfilename);
406 DEBUG_MSG("path for fullfilename=%s\n", path);
407 imdg->pbloader = pbloader_from_filename(path);
408 g_free(path);
409
410 #ifdef DEBUG
411 gchar *path2 = g_file_get_path(fullfilename);
412 DEBUG_MSG("image_filename_changed: fullfilename=%s, loading!\n", path2);
413 g_free(path2);
414 #endif
415 imdg->of = file_openfile_uri_async(fullfilename, NULL, image_loaded_lcb, imdg);
416 imdg->full_uri = fullfilename;
417 name = g_file_get_uri(fullfilename);
418 msg = g_strdup_printf(_("Loading file %s..."), name);
419 if (imdg->message) {
420 gtk_widget_destroy(imdg->message);
421 }
422 imdg->message = gtk_label_new(msg);
423 gtk_container_add(GTK_CONTAINER(imdg->frame), imdg->message);
424 gtk_widget_show(imdg->message);
425 g_free(msg);
426 g_free(name);
427 }
428 }
429
430 static void
image_adjust_changed(GtkAdjustment * adj,Timage_diag * imdg)431 image_adjust_changed(GtkAdjustment * adj, Timage_diag * imdg)
432 {
433 GdkPixbuf *tmp_pb;
434 gint tn_width, tn_height;
435 DEBUG_MSG("image_adjust_changed started. GTK_IS_WIDGET(imdg->im) == %d\n", GTK_IS_WIDGET(imdg->im));
436 if (!imdg->pb) {
437 image_filename_changed(NULL, imdg);
438 return;
439 }
440
441 tn_width = gtk_adjustment_get_value(imdg->adjustment) * gdk_pixbuf_get_width(imdg->pb);
442 tn_height = gtk_adjustment_get_value(imdg->adjustment) * gdk_pixbuf_get_height(imdg->pb);
443 DEBUG_MSG("image_adjust_changed, width=%d, height=%d\n",tn_width,tn_height);
444 gtk_spin_button_set_value(GTK_SPIN_BUTTON(imdg->dg->spin[0]), tn_width);
445 gtk_spin_button_set_value(GTK_SPIN_BUTTON(imdg->dg->spin[1]), tn_height);
446
447 /* TODO: move this to an idle callback so we will not block the UI */
448 tmp_pb =
449 gdk_pixbuf_scale_simple(imdg->pb, tn_width, tn_height,
450 main_v->globses.image_thumbnail_refresh_quality ? GDK_INTERP_BILINEAR :
451 GDK_INTERP_NEAREST);
452
453 if (GTK_IS_WIDGET(imdg->im)) {
454 DEBUG_MSG("imdg->im == %p\n", imdg->im);
455 DEBUG_MSG("gtk_widget_destroy() %p\n", imdg->im);
456 gtk_widget_destroy(imdg->im);
457 }
458
459 imdg->im = gtk_image_new_from_pixbuf(tmp_pb);
460 g_object_unref(tmp_pb);
461 gtk_container_add(GTK_CONTAINER(imdg->frame), imdg->im);
462 gtk_widget_show(imdg->im);
463 DEBUG_MSG("image_adjust_changed finished. GTK_IS_WIDGET(imdg->im) == %d\n", GTK_IS_WIDGET(imdg->im));
464 }
465
466 void
image_insert_dialog_backend(gchar * filename,Tbfwin * bfwin,Ttagpopup * data)467 image_insert_dialog_backend(gchar * filename, Tbfwin * bfwin, Ttagpopup * data)
468 {
469 static gchar *tagitems[] =
470 { "width", "height", "alt", "border", "src", "hspace", "vspace", "align", "name", "usemap", NULL };
471 gchar *tagvalues[11];
472 gchar *custom = NULL, *tmp;
473 Timage_diag *imdg;
474 GList *popuplist = NULL;
475 GtkWidget *dgtable, *scale;
476
477 imdg = g_new0(Timage_diag, 1);
478
479 tmp = main_v->props.image_thumbnailtype; /* it is often uppercase, we need it lowercase */
480 main_v->props.image_thumbnailtype = g_ascii_strdown(tmp, -1);
481 g_free(tmp);
482
483 imdg->dg = html_diag_new(bfwin, _("Insert thumbnail"));
484
485 fill_dialogvalues(tagitems, tagvalues, &custom, (Ttagpopup *) data, imdg->dg);
486
487 imdg->frame = gtk_frame_new(_("Preview"));
488 imdg->message = NULL;
489
490 gtk_box_pack_start(GTK_BOX(imdg->dg->vbox), imdg->frame, TRUE, TRUE, 0);
491
492 imdg->adjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0.5, 0.0001, 1.1, 0.001, 0.1, 0.1));
493 #if GTK_CHECK_VERSION(3,0,0)
494 scale = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, imdg->adjustment);
495 #else
496 scale = gtk_hscale_new(imdg->adjustment);
497 #endif
498 imdg->adj_changed_id =
499 g_signal_connect(G_OBJECT(imdg->adjustment), "value_changed", G_CALLBACK(image_adjust_changed), imdg);
500 gtk_scale_set_digits(GTK_SCALE(scale), 3);
501 gtk_box_pack_start(GTK_BOX(imdg->dg->vbox), scale, FALSE, FALSE, 0);
502
503 dgtable = html_diag_table_in_vbox(imdg->dg, 5, 9);
504
505 if (filename) {
506 imdg->dg->entry[0] = dialog_entry_in_table(filename, dgtable, 1, 7, 0, 1);
507 } else {
508 imdg->dg->entry[0] = dialog_entry_in_table(tagvalues[4], dgtable, 1, 7, 0, 1);
509 }
510 dialog_mnemonic_label_in_table(_("_Image location:"), imdg->dg->entry[0], dgtable, 0, 1, 0, 1);
511
512 gtk_table_attach_defaults(GTK_TABLE(dgtable), file_but_new(imdg->dg->entry[0], 0, bfwin), 7, 9, 0, 1);
513 g_signal_connect(G_OBJECT(imdg->dg->entry[0]), "changed", G_CALLBACK(image_filename_changed), imdg);
514
515 imdg->dg->spin[0] = spinbut_with_value(NULL, 0, 5000, 1.0, 10.0);
516 imdg->dg->check[0] = gtk_check_button_new_with_label("%");
517 parse_integer_for_dialog(tagvalues[0], imdg->dg->spin[0], NULL, imdg->dg->check[0]);
518 dialog_mnemonic_label_in_table(_("_Width:"), imdg->dg->spin[0], dgtable, 6, 7, 1, 2);
519 gtk_table_attach_defaults(GTK_TABLE(dgtable), imdg->dg->spin[0], 7, 8, 1, 2);
520 gtk_table_attach_defaults(GTK_TABLE(dgtable), imdg->dg->check[0], 8, 9, 1, 2);
521
522 imdg->dg->spin[1] = spinbut_with_value(NULL, 0, 5000, 1.0, 10.0);
523 imdg->dg->check[1] = gtk_check_button_new_with_label("%");
524 parse_integer_for_dialog(tagvalues[1], imdg->dg->spin[1], NULL, imdg->dg->check[1]);
525 dialog_mnemonic_label_in_table(_("Hei_ght:"), imdg->dg->spin[1], dgtable, 6, 7, 2, 3);
526 gtk_table_attach_defaults(GTK_TABLE(dgtable), imdg->dg->spin[1], 7, 8, 2, 3);
527 gtk_table_attach_defaults(GTK_TABLE(dgtable), imdg->dg->check[1], 8, 9, 2, 3);
528
529 imdg->dg->entry[3] = dialog_entry_in_table(tagvalues[9], dgtable, 1, 3, 1, 2);
530 dialog_mnemonic_label_in_table(_("_Usemap:"), imdg->dg->entry[3], dgtable, 0, 1, 1, 2);
531
532 imdg->dg->entry[1] = dialog_entry_in_table(tagvalues[8], dgtable, 1, 3, 2, 3);
533 dialog_mnemonic_label_in_table(_("_Name:"), imdg->dg->entry[1], dgtable, 0, 1, 2, 3);
534
535 imdg->dg->entry[2] = dialog_entry_in_table(tagvalues[2], dgtable, 1, 6, 3, 4);
536 dialog_mnemonic_label_in_table(_("Alternate _text:"), imdg->dg->entry[2], dgtable, 0, 1, 3, 4);
537
538 imdg->dg->entry[4] = dialog_entry_in_table(custom, dgtable, 1, 6, 4, 5);
539 dialog_mnemonic_label_in_table(_("Custo_m:"), imdg->dg->entry[4], dgtable, 0, 1, 4, 5);
540
541 imdg->dg->spin[3] = spinbut_with_value(tagvalues[5], 0, 500, 1.0, 5.0);
542 dialog_mnemonic_label_in_table(_("_Hspace:"), imdg->dg->spin[3], dgtable, 6, 7, 3, 4);
543 gtk_table_attach_defaults(GTK_TABLE(dgtable), imdg->dg->spin[3], 7, 9, 3, 4);
544
545 imdg->dg->spin[4] = spinbut_with_value(tagvalues[6], 0, 500, 1.0, 5.0);
546 dialog_mnemonic_label_in_table(_("_Vspace:"), imdg->dg->spin[4], dgtable, 6, 7, 4, 5);
547 gtk_table_attach_defaults(GTK_TABLE(dgtable), imdg->dg->spin[4], 7, 9, 4, 5);
548
549 popuplist = g_list_append(NULL, "bottom");
550 popuplist = g_list_append(popuplist, "middle");
551 popuplist = g_list_append(popuplist, "top");
552 popuplist = g_list_append(popuplist, "left");
553 popuplist = g_list_append(popuplist, "right");
554 imdg->dg->combo[0] = html_diag_combobox_with_popdown_sized(tagvalues[7], popuplist, 1, 90);
555 g_list_free(popuplist);
556 dialog_mnemonic_label_in_table(_("_Align:"), imdg->dg->combo[0], dgtable, 3, 4, 1, 2);
557 gtk_table_attach_defaults(GTK_TABLE(dgtable), imdg->dg->combo[0], 4, 6, 1, 2);
558 if (!get_curlang_option_value(imdg->dg->bfwin, lang_is_XHTML)) {
559 imdg->dg->spin[2] = spinbut_with_value(tagvalues[3], 0, 500, 1.0, 5.0);
560 dialog_mnemonic_label_in_table(_("Borde_r:"), imdg->dg->spin[2], dgtable, 3, 4, 2, 3);
561 gtk_table_attach_defaults(GTK_TABLE(dgtable), imdg->dg->spin[2], 4, 6, 2, 3);
562 }
563 if (filename || tagvalues[4]) {
564 g_signal_emit_by_name(G_OBJECT(imdg->dg->entry[0]), "changed");
565 }
566
567 image_diag_finish(imdg, G_CALLBACK(image_insert_dialogok_lcb));
568
569 if (custom)
570 g_free(custom);
571 }
572
573 void
thumbnail_insert_dialog(Tbfwin * bfwin)574 thumbnail_insert_dialog(Tbfwin * bfwin)
575 {
576 image_insert_dialog_backend(NULL, bfwin, NULL);
577 }
578
579 /*
580 to get the multi thumbnail dialog to work with asynchronous backend is a little complex:
581
582 1) start loading images asynchronous, to avoid heavy memory usage, we'll limit the number
583 of simultaneous image loads to 3 or so
584 2) each image that is loaded, we check is there are images not loading yet, and we'll fire
585 the next load, we also immediately create the thumbnail and dispose the original pixbuf to
586 free that memory. we will write the HTML string only if all previous files have written
587 their html string in the document, if previous files have not finished we do nothing. If
588 we can write the html string, we also check if the next image is already finished, and
589 should write its html string. If there is no next image, we can call for cleanup
590 */
591
592 typedef struct {
593 GtkWidget *win;
594 GtkWidget *radio[4];
595 GtkWidget *spinlabels[2];
596 GtkWidget *spins[2];
597 GtkTextBuffer *tbuf;
598 gint mode;
599 GList *images;
600 Tbfwin *bfwin;
601 Tdocument *document;
602 } Tmuthudia;
603
604 typedef struct {
605 GFile *imagename;
606 GFile *thumbname;
607 Topenfile *of; /* if != NULL, the image is loading */
608 gpointer sf; /* if != NULL, the thumbnail is saving */
609 gboolean created; /* both loading and saving is finished */
610 gchar *string; /* the string to insert, if NULL && ->create = TRUE means
611 that the string is written to the document */
612 Tmuthudia *mtd;
613 } Timage2thumb;
614
615 static void
mt_dialog_destroy(GtkWidget * wid,Tmuthudia * mtd)616 mt_dialog_destroy(GtkWidget * wid, Tmuthudia * mtd)
617 {
618 /* check if we have some images still loading, all images that have 'created == TRUE'
619 are ready */
620 GList *tmplist;
621 for (tmplist = g_list_first(mtd->images); tmplist; tmplist = g_list_next(tmplist)) {
622 Timage2thumb *tmp = tmplist->data;
623 if (tmp->created == FALSE) {
624 return;
625 }
626 }
627 for (tmplist = g_list_first(mtd->images); tmplist; tmplist = g_list_next(tmplist)) {
628 Timage2thumb *tmp = tmplist->data;
629 g_object_unref(tmp->imagename);
630 g_object_unref(tmp->thumbname);
631 g_free(tmp);
632 }
633 DEBUG_MSG("multi_thumbnail_dialog_destroy, called for mtd=%p\n", mtd);
634 window_destroy(mtd->win);
635 g_free(mtd);
636 }
637
638 /* needs both pixbufs to get the width !! */
639 static void
mt_fill_string(Timage2thumb * i2t,GdkPixbuf * image,GdkPixbuf * thumb)640 mt_fill_string(Timage2thumb * i2t, GdkPixbuf * image, GdkPixbuf * thumb)
641 {
642 gint tw, th, ow, oh;
643 gchar *relthumb, *tmp, *relimage;
644 gchar *doc_curi = NULL;
645
646 relimage = tmp = g_file_get_uri(i2t->imagename);
647
648 if (i2t->mtd->document->uri) {
649 doc_curi = g_file_get_uri(i2t->mtd->document->uri);
650 }
651
652 if (i2t->mtd->document->uri) {
653 relimage = create_relative_link_to(doc_curi, tmp);
654 g_free(tmp);
655 }
656
657 relthumb = tmp = g_file_get_uri(i2t->thumbname);
658
659 if (i2t->mtd->bfwin->current_document->uri) {
660 relthumb = create_relative_link_to(doc_curi, tmp);
661 g_free(tmp);
662 }
663 if (doc_curi)
664 g_free(doc_curi);
665
666 ow = gdk_pixbuf_get_width(image);
667 oh = gdk_pixbuf_get_height(image);
668 tw = gdk_pixbuf_get_width(thumb);
669 th = gdk_pixbuf_get_height(thumb);
670 {
671 Tconvert_table *table, *tmpt;
672
673 table = tmpt = g_new(Tconvert_table, 8);
674 tmpt->my_int = 'r';
675 tmpt->my_char = g_strdup(relimage);
676 tmpt++;
677 tmpt->my_int = 't';
678 tmpt->my_char = g_strdup(relthumb);
679 tmpt++;
680 tmpt->my_int = 'w';
681 tmpt->my_char = g_strdup_printf("%d", ow);
682 tmpt++;
683 tmpt->my_int = 'h';
684 tmpt->my_char = g_strdup_printf("%d", oh);
685 tmpt++;
686 tmpt->my_int = 'x';
687 tmpt->my_char = g_strdup_printf("%d", tw);
688 tmpt++;
689 tmpt->my_int = 'y';
690 tmpt->my_char = g_strdup_printf("%d", th);
691 tmpt++;
692 tmpt->my_int = 'b';
693 tmpt->my_char = g_strdup("xxx");
694 tmpt++;
695 tmpt->my_char = NULL;
696 i2t->string = replace_string_printflike(main_v->globses.image_thumnailformatstring, table);
697 DEBUG_MSG("string to insert: %s\n", i2t->string);
698 tmpt = table;
699 while (tmpt->my_char) {
700 g_free(tmpt->my_char);
701 tmpt++;
702 }
703 g_free(table);
704 }
705 g_free(relimage);
706 g_free(relthumb);
707 }
708
709 static Timage2thumb *
mt_next(Timage2thumb * i2t)710 mt_next(Timage2thumb * i2t)
711 {
712 GList *tmplist;
713 tmplist = g_list_find(i2t->mtd->images, i2t);
714 tmplist = g_list_next(tmplist);
715 return (tmplist) ? tmplist->data : NULL;
716 }
717
718 static Timage2thumb *
mt_prev(Timage2thumb * i2t)719 mt_prev(Timage2thumb * i2t)
720 {
721 GList *tmplist;
722 tmplist = g_list_find(i2t->mtd->images, i2t);
723 tmplist = g_list_previous(tmplist);
724 return (tmplist) ? tmplist->data : NULL;
725 }
726
727 /* TRUE if already inserted or successfully inserted, FALSE if not yet ready */
728 static gboolean
mt_print_string(Timage2thumb * i2t)729 mt_print_string(Timage2thumb * i2t)
730 {
731 if (i2t->string == NULL && i2t->created == TRUE) {
732 /* already added the HTML string */
733 return TRUE;
734 } else if (i2t->string) {
735 Timage2thumb *tmp;
736 /* check the previous entry */
737 tmp = mt_prev(i2t);
738 if (tmp && !mt_print_string(tmp)) {
739 return FALSE;
740 }
741 /* we have a string, but it is not yet added, insert the string */
742 doc_insert_two_strings(i2t->mtd->document, i2t->string, NULL);
743 g_free(i2t->string);
744 i2t->string = NULL;
745 i2t->created = TRUE;
746 /* now check if the next string is also already ready for printing */
747 tmp = mt_next(i2t);
748 if (tmp)
749 mt_print_string(tmp);
750 return TRUE;
751 } else {
752 return FALSE;
753 }
754 }
755
756 static void mt_start_load(Timage2thumb * i2t);
757
758 static gboolean
mt_start_next_load(Timage2thumb * i2t)759 mt_start_next_load(Timage2thumb * i2t)
760 {
761 GList *tmplist;
762 for (tmplist = g_list_first(i2t->mtd->images); tmplist; tmplist = g_list_next(tmplist)) {
763 Timage2thumb *tmp = tmplist->data;
764 if (tmp->of == NULL && tmp->string == NULL && tmp->created == FALSE) {
765 mt_start_load(tmp);
766 return TRUE;
767 }
768 }
769 return FALSE;
770 }
771
772 static void
mt_openfile_lcb(Topenfile_status status,GError * gerror,Trefcpointer * refp,goffset buflen,gpointer callback_data)773 mt_openfile_lcb(Topenfile_status status, GError * gerror, Trefcpointer *refp, goffset buflen,
774 gpointer callback_data)
775 {
776 Timage2thumb *i2t = callback_data;
777 switch (status) {
778 case OPENFILE_ERROR:
779 case OPENFILE_ERROR_NOCHANNEL:
780 case OPENFILE_ERROR_NOREAD:
781 case OPENFILE_ERROR_CANCELLED:{
782 /* TODO: should we warn the user ?? */
783 #ifdef DEBUG
784 {
785 gchar *path = g_file_get_path(i2t->imagename);
786 DEBUG_MSG("mt_openfile_lcb, some error! status=%d for image %s\n", status, path);
787 g_free(path);
788 }
789 #endif
790 }
791 break;
792 case OPENFILE_CHANNEL_OPENED:
793 /* do nothing */
794 break;
795 case OPENFILE_FINISHED:{
796 GError *error = NULL;
797 gboolean nextload;
798 GdkPixbufLoader *pbloader;
799 #ifdef DEBUG
800 {
801 gchar *path = g_file_get_path(i2t->imagename);
802 DEBUG_MSG("mt_openfile_lcb, finished loading image %s\n", path);
803 g_free(path);
804 }
805 #endif
806 nextload = mt_start_next_load(i2t); /* fire up the next image load */
807
808 gchar *path = g_file_get_path(i2t->imagename);
809 pbloader = pbloader_from_filename(path);
810 g_free(path);
811
812 if (gdk_pixbuf_loader_write(pbloader, (const guchar *) refp->data, buflen, &error)
813 && gdk_pixbuf_loader_close(pbloader, &error)) {
814 gint tw, th, ow, oh;
815 GdkPixbuf *image;
816 GdkPixbuf *thumb;
817 gsize buflen;
818 image = gdk_pixbuf_loader_get_pixbuf(pbloader);
819 if (image) {
820 ow = gdk_pixbuf_get_width(image);
821 oh = gdk_pixbuf_get_height(image);
822 switch (main_v->globses.image_thumbnailsizing_type) {
823 case 0:
824 tw = (1.0 * ow / 100 * main_v->globses.image_thumbnailsizing_val1);
825 th = (1.0 * oh / 100 * main_v->globses.image_thumbnailsizing_val1);
826 break;
827 case 1:
828 tw = main_v->globses.image_thumbnailsizing_val1;
829 th = (1.0 * tw / ow * oh);
830 break;
831 case 2:
832 th = main_v->globses.image_thumbnailsizing_val1;
833 tw = (1.0 * th / oh * ow);
834 break;
835 default: /* all fall back to type 3 */
836 tw = main_v->globses.image_thumbnailsizing_val1;
837 th = main_v->globses.image_thumbnailsizing_val2;
838 break;
839 }
840 #ifdef DEBUG
841 {
842 gchar *path = g_file_get_path(i2t->imagename);
843 DEBUG_MSG("mt_openfile_lcb, start scaling %s to %dx%d\n", path, tw, th);
844 g_free(path);
845 }
846 #endif
847 thumb = gdk_pixbuf_scale_simple(image, tw, th, GDK_INTERP_BILINEAR);
848 #ifdef DEBUG
849 gchar *path = g_file_get_path(i2t->imagename);
850 DEBUG_MSG("mt_openfile_lcb, done scaling %s\n", path);
851 g_free(path);
852 #endif
853 mt_fill_string(i2t, image, thumb); /* create the string */
854 mt_print_string(i2t); /* print the string and all previous string (if possible) */
855 g_object_unref(pbloader);
856 /*gdk_pixbuf_unref(image); will be unreffed with the loader! */
857 /* save the thumbnail */
858 if (strcmp(main_v->props.image_thumbnailtype, "jpeg") == 0) {
859 gdk_pixbuf_save_to_buffer(thumb, (gchar **)&refp->data, &buflen, main_v->props.image_thumbnailtype,
860 &error, "quality", "50", NULL);
861 } else {
862 gdk_pixbuf_save_to_buffer(thumb, (gchar **)&refp->data, &buflen, main_v->props.image_thumbnailtype,
863 &error, NULL);
864 }
865 g_object_unref(thumb);
866 if (error) {
867 g_print("ERROR while saving thumbnail to buffer: %s\n", error->message);
868 g_error_free(error);
869 } else {
870 GError *error = NULL;
871 GFileInfo *finfo;
872 refcpointer_ref(refp);
873 finfo = g_file_query_info(i2t->thumbname,
874 BF_FILEINFO, G_FILE_QUERY_INFO_NONE, NULL, &error);
875 if (error != NULL) {
876 g_print("mt_openfile_lcb %s\n ", error->message);
877 g_error_free(error);
878 }
879 #ifdef DEBUG
880 gchar *path = g_file_get_path(i2t->thumbname);
881 DEBUG_MSG("mt_openfile_lcb, starting thumbnail save to %s\n", path);
882 g_free(path);
883 #endif
884 i2t->sf =
885 file_checkNsave_uri_async(i2t->thumbname, finfo, refp, buflen, FALSE, FALSE,
886 async_thumbsave_lcb, NULL, i2t->mtd->bfwin);
887 refcpointer_unref(refp);
888 }
889 } else {
890 /* ok, this image is not valid, how do we continue ?? */
891 #ifdef DEBUG
892 gchar *path = g_file_get_path(i2t->imagename);
893 DEBUG_MSG("mt_openfile_lcb, failed to convert %s to image\n", path);
894 g_free(path);
895 #endif
896 i2t->string = g_strdup("");
897 mt_print_string(i2t);
898 }
899 if (!nextload) {
900 /* there were no more images to load, perhaps we could already call cleanup */
901 mt_dialog_destroy(NULL, i2t->mtd);
902 }
903 }
904 }
905 break;
906 }
907 /* BUG: the last image that reaches this function should free 'mtd' after it is finished */
908 }
909
910 static void
mt_start_load(Timage2thumb * i2t)911 mt_start_load(Timage2thumb * i2t)
912 {
913 #ifdef DEBUG
914 gchar *path = g_file_get_path(i2t->imagename);
915 DEBUG_MSG("mt_start_load, starting load for %s\n", path);
916 g_free(path);
917 #endif
918 i2t->of = file_openfile_uri_async(i2t->imagename, NULL, mt_openfile_lcb, i2t);
919 }
920
921 static Timage2thumb *
mt_image2thumbnail(Tmuthudia * mtd,gchar * curi)922 mt_image2thumbnail(Tmuthudia * mtd, gchar * curi)
923 {
924 Timage2thumb *i2t;
925 gchar *tmp;
926 GFile *uri;
927
928 DEBUG_MSG("mt_image2thumbnail, called for %s\n", curi);
929 if (!curi)
930 return NULL;
931 uri = g_file_new_for_uri(curi);
932
933 if (!uri)
934 return NULL;
935 i2t = g_new0(Timage2thumb, 1);
936 i2t->mtd = mtd;
937 i2t->imagename = uri;
938 tmp = create_thumbnail_filename(curi);
939 i2t->thumbname = g_file_new_for_uri(tmp);
940 g_free(tmp);
941 return i2t;
942 }
943
944 static void
multi_thumbnail_ok_clicked(GtkWidget * widget,Tmuthudia * mtd)945 multi_thumbnail_ok_clicked(GtkWidget * widget, Tmuthudia * mtd)
946 {
947 GSList *files = NULL, *tmplist;
948 GtkWidget *dialog;
949 gint i;
950
951 gtk_widget_hide(mtd->win);
952
953 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mtd->radio[0]))) {
954 main_v->globses.image_thumbnailsizing_type = 0;
955 } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mtd->radio[1]))) {
956 main_v->globses.image_thumbnailsizing_type = 1;
957 } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mtd->radio[2]))) {
958 main_v->globses.image_thumbnailsizing_type = 2;
959 } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mtd->radio[3]))) {
960 main_v->globses.image_thumbnailsizing_type = 3;
961 }
962 {
963 gchar *tmp;
964 GtkTextIter start, end;
965 gtk_text_buffer_get_bounds(mtd->tbuf, &start, &end);
966 tmp = gtk_text_buffer_get_text(mtd->tbuf, &start, &end, FALSE);
967 if (tmp) {
968 if (main_v->globses.image_thumnailformatstring)
969 g_free(main_v->globses.image_thumnailformatstring);
970 main_v->globses.image_thumnailformatstring = tmp;
971 }
972 }
973
974 dialog =
975 file_chooser_dialog(mtd->bfwin, _("Select files for thumbnail creation"),
976 GTK_FILE_CHOOSER_ACTION_OPEN, NULL, FALSE, TRUE, "webimage", FALSE);
977 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
978 files = gtk_file_chooser_get_uris(GTK_FILE_CHOOSER(dialog));
979 }
980 gtk_widget_destroy(dialog);
981
982 main_v->globses.image_thumbnailsizing_val1 =
983 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mtd->spins[0]));
984 main_v->globses.image_thumbnailsizing_val2 =
985 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mtd->spins[1]));
986
987 i = 3;
988 tmplist = files;
989 while (tmplist) {
990 Timage2thumb *i2t;
991 i2t = mt_image2thumbnail(mtd, (gchar *) tmplist->data);
992 mtd->images = g_list_prepend(mtd->images, i2t);
993 if (i > 0) {
994 mt_start_load(i2t);
995 i--;
996 }
997 tmplist = g_slist_next(tmplist);
998 }
999 mtd->images = g_list_reverse(mtd->images);
1000 /* BUG: should we free the list of files now ?? */
1001 }
1002
1003 static void
multi_thumbnail_cancel_clicked(GtkWidget * widget,Tmuthudia * mtd)1004 multi_thumbnail_cancel_clicked(GtkWidget * widget, Tmuthudia * mtd)
1005 {
1006 mt_dialog_destroy(NULL, mtd);
1007 }
1008
1009 static void
multi_thumbnail_radio_toggled_lcb(GtkToggleButton * togglebutton,Tmuthudia * mtd)1010 multi_thumbnail_radio_toggled_lcb(GtkToggleButton * togglebutton, Tmuthudia * mtd)
1011 {
1012 /* only call this for activate, not for de-activate */
1013 if (gtk_toggle_button_get_active(togglebutton)) {
1014 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mtd->radio[0]))) {
1015 gtk_widget_hide(mtd->spins[1]);
1016 gtk_widget_hide(mtd->spinlabels[1]);
1017 gtk_label_set_text(GTK_LABEL(mtd->spinlabels[0]), _("Scaling (%)"));
1018 } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mtd->radio[1]))) {
1019 gtk_widget_hide(mtd->spins[1]);
1020 gtk_widget_hide(mtd->spinlabels[1]);
1021 gtk_label_set_text(GTK_LABEL(mtd->spinlabels[0]), _("Width"));
1022 } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mtd->radio[2]))) {
1023 gtk_widget_hide(mtd->spins[1]);
1024 gtk_widget_hide(mtd->spinlabels[1]);
1025 gtk_label_set_text(GTK_LABEL(mtd->spinlabels[0]), _("Height"));
1026 } else {
1027 gtk_widget_show(mtd->spins[1]);
1028 gtk_widget_show(mtd->spinlabels[1]);
1029 gtk_label_set_text(GTK_LABEL(mtd->spinlabels[0]), _("Width"));
1030 }
1031 }
1032 }
1033
1034 void
multi_thumbnail_dialog(Tbfwin * bfwin)1035 multi_thumbnail_dialog(Tbfwin * bfwin)
1036 {
1037 Tmuthudia *mtd;
1038 GtkWidget *vbox, *hbox, *but, *table, *label, *scrolwin, *textview;
1039 gint tb;
1040 gchar *tmp;
1041
1042 if (!bfwin->current_document) {
1043 return;
1044 }
1045
1046 tmp = main_v->props.image_thumbnailtype; /* it is often uppercase, we need it lowercase */
1047 main_v->props.image_thumbnailtype = g_ascii_strdown(tmp, -1);
1048 g_free(tmp);
1049
1050 mtd = g_new0(Tmuthudia, 1);
1051 mtd->bfwin = bfwin;
1052 mtd->document = bfwin->current_document;
1053 mtd->win =
1054 window_full2(_("Multi thumbnail"), GTK_WIN_POS_CENTER, 5, G_CALLBACK(mt_dialog_destroy), mtd, TRUE,
1055 bfwin->main_window);
1056 vbox = gtk_vbox_new(FALSE, 5);
1057 gtk_container_add(GTK_CONTAINER(mtd->win), vbox);
1058
1059 table = gtk_table_new(4, 3, FALSE);
1060 mtd->radio[0] = gtk_radio_button_new_with_label(NULL, _("By scaling"));
1061 mtd->radio[1] =
1062 gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(mtd->radio[0]),
1063 _("By width, keep aspect ratio"));
1064 mtd->radio[2] =
1065 gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(mtd->radio[0]),
1066 _("By height, keep aspect ratio"));
1067 mtd->radio[3] =
1068 gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(mtd->radio[0]),
1069 _("By width and height, ignore aspect ratio"));
1070 mtd->spinlabels[0] = gtk_label_new("");
1071 mtd->spinlabels[1] = gtk_label_new(_("Height"));
1072 mtd->spins[0] = gtk_spin_button_new_with_range(0, 1000, 1);
1073 mtd->spins[1] = gtk_spin_button_new_with_range(0, 1000, 1);
1074
1075 g_signal_connect(G_OBJECT(mtd->radio[0]), "toggled", G_CALLBACK(multi_thumbnail_radio_toggled_lcb), mtd);
1076 g_signal_connect(G_OBJECT(mtd->radio[1]), "toggled", G_CALLBACK(multi_thumbnail_radio_toggled_lcb), mtd);
1077 g_signal_connect(G_OBJECT(mtd->radio[2]), "toggled", G_CALLBACK(multi_thumbnail_radio_toggled_lcb), mtd);
1078 g_signal_connect(G_OBJECT(mtd->radio[3]), "toggled", G_CALLBACK(multi_thumbnail_radio_toggled_lcb), mtd);
1079 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mtd->spins[0]), main_v->globses.image_thumbnailsizing_val1);
1080 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mtd->spins[1]), main_v->globses.image_thumbnailsizing_val2);
1081
1082 gtk_table_attach_defaults(GTK_TABLE(table), mtd->radio[0], 0, 1, 0, 1);
1083 gtk_table_attach_defaults(GTK_TABLE(table), mtd->radio[1], 0, 1, 1, 2);
1084 gtk_table_attach_defaults(GTK_TABLE(table), mtd->radio[2], 0, 1, 2, 3);
1085 gtk_table_attach_defaults(GTK_TABLE(table), mtd->radio[3], 0, 1, 3, 4);
1086 gtk_table_attach_defaults(GTK_TABLE(table), mtd->spinlabels[0], 1, 2, 0, 1);
1087 gtk_table_attach_defaults(GTK_TABLE(table), mtd->spinlabels[1], 1, 2, 1, 2);
1088 gtk_table_attach_defaults(GTK_TABLE(table), mtd->spins[0], 2, 3, 0, 1);
1089 gtk_table_attach_defaults(GTK_TABLE(table), mtd->spins[1], 2, 3, 1, 2);
1090
1091 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
1092
1093 label =
1094 gtk_label_new(_
1095 ("%r: original filename %t: thumbnail filename\n%w: original width %h: original height\n%x: thumbnail width %y: thumbnail height\n%b: original size (bytes)"));
1096 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1097
1098 scrolwin =
1099 textview_buffer_in_scrolwin(&textview, -1, -1, main_v->globses.image_thumnailformatstring,
1100 GTK_WRAP_CHAR);
1101 mtd->tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
1102 gtk_box_pack_start(GTK_BOX(vbox), scrolwin, TRUE, TRUE, 0);
1103 #if GTK_CHECK_VERSION(3,0,0)
1104 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
1105 #else
1106 hbox = gtk_hbutton_box_new();
1107 #endif
1108 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
1109 gtk_box_set_spacing(GTK_BOX(hbox), 1);
1110 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1111 but = bf_stock_cancel_button(G_CALLBACK(multi_thumbnail_cancel_clicked), mtd);
1112 gtk_box_pack_start(GTK_BOX(hbox), but, FALSE, FALSE, 5);
1113 but = bf_stock_ok_button(G_CALLBACK(multi_thumbnail_ok_clicked), mtd);
1114 gtk_box_pack_start(GTK_BOX(hbox), but, FALSE, FALSE, 5);
1115 gtk_window_set_default(GTK_WINDOW(mtd->win), but);
1116 gtk_widget_show_all(mtd->win);
1117
1118 tb = main_v->globses.image_thumbnailsizing_type < 4 ? main_v->globses.image_thumbnailsizing_type : 0;
1119 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mtd->radio[tb]), TRUE);
1120 multi_thumbnail_radio_toggled_lcb(GTK_TOGGLE_BUTTON(mtd->radio[tb]), mtd);
1121 }
1122