1 /*
2 * * Copyright (C) 2006-2011 Anders Brander <anders@brander.dk>,
3 * * Anders Kvist <akv@lnxbx.dk> and Klaus Post <klauspost@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include <rawstudio.h>
21 #include <glib.h>
22 #include <stdio.h>
23 #include <gtk/gtk.h>
24 #include <config.h>
25 #include <libxml/encoding.h>
26 #include <libxml/xmlwriter.h>
27 #include "application.h"
28 #include "rs-batch.h"
29 #include "conf_interface.h"
30 #include "gettext.h"
31 #include "gtk-helper.h"
32 #include "gtk-interface.h"
33 #include "filename.h"
34 #include "rs-cache.h"
35 #include "rs-photo.h"
36 #include "rs-actions.h"
37 #include "rs-store.h"
38
39 extern GtkWindow *rawstudio_window;
40
41 static GtkWidget *make_batchview(RS_QUEUE *queue);
42 static void size_update_infolabel(RS_QUEUE *queue);
43 static gchar *batch_queue_filename = NULL;
44 static void batch_queue_update_sensivity(RS_QUEUE *queue);
45
46 static void
batch_queue_save(RS_QUEUE * queue)47 batch_queue_save(RS_QUEUE *queue)
48 {
49 xmlTextWriterPtr writer;
50 GtkTreeIter iter;
51 gchar *filename;
52 gint setting_id;
53
54 g_assert(queue != NULL);
55 g_assert(batch_queue_filename != NULL);
56
57 writer = xmlNewTextWriterFilename(batch_queue_filename, 0);
58 if (!writer)
59 return;
60 xmlTextWriterSetIndent(writer, 1);
61 xmlTextWriterStartDocument(writer, NULL, "ISO-8859-1", NULL);
62 xmlTextWriterStartElement(writer, BAD_CAST "rawstudio-batch-queue");
63
64 if (gtk_tree_model_get_iter_first(queue->list, &iter))
65 do
66 {
67 gtk_tree_model_get(queue->list, &iter,
68 RS_QUEUE_ELEMENT_FILENAME, &filename,
69 RS_QUEUE_ELEMENT_SETTING_ID, &setting_id,
70 -1);
71 xmlTextWriterStartElement(writer, BAD_CAST "entry");
72 xmlTextWriterWriteFormatElement(writer, BAD_CAST "filename", "%s", filename);
73 xmlTextWriterWriteFormatElement(writer, BAD_CAST "snapshot", "%d", setting_id);
74 xmlTextWriterEndElement(writer);
75 g_free(filename);
76 } while(gtk_tree_model_iter_next(queue->list, &iter));
77
78 xmlTextWriterEndDocument(writer);
79 xmlFreeTextWriter(writer);
80
81 return;
82 }
83
84 static void
batch_queue_load(RS_QUEUE * queue)85 batch_queue_load(RS_QUEUE *queue)
86 {
87 xmlDocPtr doc;
88 xmlNodePtr cur;
89 xmlNodePtr entry = NULL;
90 xmlChar *val;
91
92 g_assert(queue != NULL);
93
94 if (!batch_queue_filename)
95 batch_queue_filename = g_build_filename(rs_confdir_get(), "batch-queue.xml", NULL);
96
97 if (!g_file_test(batch_queue_filename, G_FILE_TEST_IS_REGULAR))
98 return;
99
100 doc = xmlParseFile(batch_queue_filename);
101 if (!doc)
102 return;
103
104 cur = xmlDocGetRootElement(doc);
105 cur = cur->xmlChildrenNode;
106
107 while(cur)
108 {
109 if ((!xmlStrcmp(cur->name, BAD_CAST "entry")))
110 {
111 xmlChar *filename = NULL;
112 gint setting_id = -1;
113
114 entry = cur->xmlChildrenNode;
115
116 while (entry)
117 {
118 if ((!xmlStrcmp(entry->name, BAD_CAST "filename")))
119 {
120 filename = xmlNodeListGetString(doc, entry->xmlChildrenNode, 1);
121 }
122 if ((!xmlStrcmp(entry->name, BAD_CAST "snapshot")))
123 {
124 val = xmlNodeListGetString(doc, entry->xmlChildrenNode, 1);
125 setting_id = atoi((char *)val);
126 if (setting_id > 2) setting_id = 2;
127 if (setting_id < 0) setting_id = 0;
128 xmlFree(val);
129 }
130 entry = entry->next;
131 }
132 if (filename && (setting_id >= 0))
133 {
134 rs_batch_add_to_queue(queue, (gchar *) filename, setting_id);
135 xmlFree(filename);
136 }
137 }
138 cur = cur->next;
139 }
140
141 xmlFreeDoc(doc);
142 return;
143 }
144
rs_batch_new_queue(RS_BLOB * rs)145 RS_QUEUE* rs_batch_new_queue(RS_BLOB *rs)
146 {
147 gchar *tmp;
148 RS_QUEUE *queue = g_new(RS_QUEUE, 1);
149 queue->rs = rs;
150 queue->output = NULL;
151
152 queue->list = GTK_TREE_MODEL(gtk_list_store_new(5, G_TYPE_STRING,G_TYPE_STRING,
153 G_TYPE_INT,G_TYPE_STRING, GDK_TYPE_PIXBUF));
154
155 queue->directory = rs_conf_get_string(CONF_BATCH_DIRECTORY);
156 if (queue->directory == NULL)
157 {
158 rs_conf_set_string(CONF_BATCH_DIRECTORY, DEFAULT_CONF_BATCH_DIRECTORY);
159 queue->directory = rs_conf_get_string(CONF_BATCH_DIRECTORY);
160 }
161
162 queue->filename = rs_conf_get_string(CONF_BATCH_FILENAME);
163 if (queue->filename == NULL)
164 {
165 rs_conf_set_string(CONF_BATCH_FILENAME, DEFAULT_CONF_BATCH_FILENAME);
166 queue->filename = rs_conf_get_string(CONF_BATCH_FILENAME);
167 }
168
169 queue->size_lock = LOCK_SCALE;
170 queue->size = 100;
171 queue->size_window = NULL;
172 queue->scale = 100;
173 queue->width = 600;
174 queue->height = 600;
175
176 /* Load last values */
177 rs_conf_get_integer(CONF_BATCH_SIZE_SCALE, &queue->scale);
178 rs_conf_get_integer(CONF_BATCH_SIZE_WIDTH, &queue->width);
179 rs_conf_get_integer(CONF_BATCH_SIZE_HEIGHT, &queue->height);
180 tmp = rs_conf_get_string(CONF_BATCH_SIZE_LOCK);
181 if (tmp)
182 {
183 if (g_str_equal(tmp, "bounding-box"))
184 queue->size_lock = LOCK_BOUNDING_BOX;
185 else if (g_str_equal(tmp, "width"))
186 queue->size_lock = LOCK_WIDTH;
187 else if (g_str_equal(tmp, "height"))
188 queue->size_lock = LOCK_HEIGHT;
189 g_free(tmp);
190 }
191
192 return queue;
193 }
194
195 gboolean
rs_batch_add_to_queue(RS_QUEUE * queue,const gchar * filename,const gint setting_id)196 rs_batch_add_to_queue(RS_QUEUE *queue, const gchar *filename, const gint setting_id)
197 {
198 gboolean ret = FALSE;
199 if (!rs_batch_exists_in_queue(queue, filename, setting_id))
200 {
201 RSMetadata *metadata;
202 gchar *filename_short, *setting_id_abc;
203 GdkPixbuf *pixbuf = NULL, *missing_thumb, *pixbuf_temp;
204
205 filename_short = g_path_get_basename(filename);
206
207 switch(setting_id)
208 {
209 case 0:
210 setting_id_abc = _("A");
211 break;
212 case 1:
213 setting_id_abc = _("B");
214 break;
215 case 2:
216 setting_id_abc = _("C");
217 break;
218 default:
219 return ret;
220 }
221
222 missing_thumb = gtk_widget_render_icon(GTK_WIDGET(rawstudio_window),
223 GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_DIALOG, NULL);
224
225 metadata = rs_metadata_new_from_file(filename);
226 pixbuf = rs_metadata_get_thumbnail(metadata);
227 g_object_unref(metadata);
228
229 if (pixbuf)
230 {
231 gint w,h,temp,size = 48;
232
233 w = gdk_pixbuf_get_width(pixbuf);
234 h = gdk_pixbuf_get_height(pixbuf);
235
236 if (w > h)
237 {
238 temp = 1000*h/w;
239 pixbuf_temp = gdk_pixbuf_scale_simple(pixbuf, size, size*temp/1000, GDK_INTERP_BILINEAR);
240 g_object_unref(pixbuf);
241 pixbuf = pixbuf_temp;
242 }
243 else
244 {
245 temp = 1000*w/h;
246 pixbuf_temp = gdk_pixbuf_scale_simple(pixbuf, size*temp/1000, size, GDK_INTERP_BILINEAR);
247 g_object_unref(pixbuf);
248 pixbuf = pixbuf_temp;
249 }
250 }
251 else
252 {
253 pixbuf = missing_thumb;
254 g_object_ref (pixbuf);
255 }
256 g_object_unref(missing_thumb);
257
258 if (!rs_batch_exists_in_queue(queue, filename, setting_id))
259 {
260 GtkTreeIter iter;
261
262 gtk_list_store_append (GTK_LIST_STORE(queue->list), &iter);
263 gtk_list_store_set (GTK_LIST_STORE(queue->list), &iter,
264 RS_QUEUE_ELEMENT_FILENAME, filename,
265 RS_QUEUE_ELEMENT_FILENAME_SHORT, filename_short,
266 RS_QUEUE_ELEMENT_SETTING_ID, setting_id,
267 RS_QUEUE_ELEMENT_SETTING_ID_ABC, setting_id_abc,
268 RS_QUEUE_ELEMENT_THUMBNAIL, pixbuf,
269 -1);
270 ret = TRUE;
271 }
272 g_object_unref(pixbuf);
273 g_free(filename_short);
274 }
275
276 batch_queue_save(queue);
277
278 batch_queue_update_sensivity(queue);
279
280 return ret;
281 }
282
283 gboolean
rs_batch_remove_from_queue(RS_QUEUE * queue,const gchar * filename,gint setting_id)284 rs_batch_remove_from_queue(RS_QUEUE *queue, const gchar *filename, gint setting_id)
285 {
286 gboolean ret = FALSE;
287 GtkTreeIter iter;
288
289 gchar *filename_temp = NULL;
290 gint setting_id_temp;
291
292 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(queue->list), &iter);
293
294 if (gtk_list_store_iter_is_valid(GTK_LIST_STORE(queue->list), &iter))
295 {
296 do
297 {
298 gtk_tree_model_get(queue->list, &iter,
299 RS_QUEUE_ELEMENT_FILENAME, &filename_temp,
300 RS_QUEUE_ELEMENT_SETTING_ID, &setting_id_temp,
301 -1);
302
303 if (g_str_equal(filename, filename_temp))
304 {
305 if (setting_id == setting_id_temp)
306 {
307 gtk_list_store_remove(GTK_LIST_STORE(queue->list), &iter);
308 ret = TRUE;
309 }
310 }
311 g_free(filename_temp);
312
313 /* Break out of the loop if we got a hit */
314 if (ret)
315 break;
316 } while (gtk_tree_model_iter_next(queue->list, &iter));
317 }
318
319 batch_queue_save(queue);
320
321 rs_core_actions_update_menu_items(queue->rs); /* FIXME: should be done with a signal */
322 batch_queue_update_sensivity(queue);
323
324 return ret;
325 }
326
327 gboolean
rs_batch_exists_in_queue(RS_QUEUE * queue,const gchar * filename,gint setting_id)328 rs_batch_exists_in_queue(RS_QUEUE *queue, const gchar *filename, gint setting_id)
329 {
330 gboolean ret = FALSE;
331 GtkTreeIter iter;
332
333 gchar *filename_temp;
334 gint setting_id_temp;
335
336 gtk_tree_model_get_iter_first(queue->list, &iter);
337
338 if (gtk_list_store_iter_is_valid(GTK_LIST_STORE(queue->list), &iter))
339 {
340 do
341 {
342 gtk_tree_model_get(queue->list, &iter,
343 RS_QUEUE_ELEMENT_FILENAME, &filename_temp,
344 RS_QUEUE_ELEMENT_SETTING_ID, &setting_id_temp,
345 -1);
346
347 if (g_str_equal(filename, filename_temp))
348 {
349 if (setting_id == setting_id_temp)
350 ret = TRUE;
351 }
352 g_free(filename_temp);
353 } while (gtk_tree_model_iter_next(queue->list, &iter) && !ret);
354 }
355 return ret;
356 }
357
358 static gboolean
window_destroy(GtkWidget * widget,GdkEvent * event,gpointer user_data)359 window_destroy(GtkWidget *widget, GdkEvent *event, gpointer user_data)
360 {
361 gboolean *abort_render = (gboolean *) user_data;
362 *abort_render = TRUE;
363 return(TRUE);
364 }
365
366 static void
cancel_clicked(GtkButton * button,gpointer user_data)367 cancel_clicked(GtkButton *button, gpointer user_data)
368 {
369 gboolean *abort_render = (gboolean *) user_data;
370 *abort_render = TRUE;
371 return;
372 }
373
374 void
rs_batch_process(RS_QUEUE * queue)375 rs_batch_process(RS_QUEUE *queue)
376 {
377 RS_PHOTO *photo = NULL;
378 GtkTreeIter iter;
379 gchar *filename_in;
380 gint setting_id;
381 GtkWidget *preview = gtk_image_new();
382 GdkPixbuf *pixbuf = NULL;
383 gint width = -1, height = -1;
384 gdouble scale = -1.0;
385 gchar *parsed_filename, *basename, *parsed_dir;
386 GString *filename;
387 GString *status = g_string_new(NULL);
388 GtkWidget *window;
389 GtkWidget *label = gtk_label_new(NULL);
390 GtkWidget *vbox = gtk_vbox_new(FALSE, 4);
391 GtkWidget *cancel;
392 gboolean abort_render = FALSE;
393 GTimeVal start_time;
394 GTimeVal now_time = {0,0};
395 gint time, eta;
396 GtkWidget *eta_label = gtk_label_new(NULL);
397 gchar *eta_text, *title_text;
398 gint h = 0, m = 0, s = 0;
399 gint done = 0, left = 0;
400 RSFilter *finput = rs_filter_new("RSInputImage16", NULL);
401 RSFilter *fdemosaic = rs_filter_new("RSDemosaic", finput);
402 RSFilter *ffujirotate = rs_filter_new("RSFujiRotate", fdemosaic);
403 RSFilter *flensfun = rs_filter_new("RSLensfun", ffujirotate);
404 RSFilter *frotate = rs_filter_new("RSRotate", flensfun);
405 RSFilter *fcrop = rs_filter_new("RSCrop", frotate);
406 RSFilter *fcache = rs_filter_new("RSCache", fcrop);
407 RSFilter *fresample= rs_filter_new("RSResample", fcache);
408 RSFilter *ftransform_input = rs_filter_new("RSColorspaceTransform", fresample);
409 RSFilter *fdcp= rs_filter_new("RSDcp", ftransform_input);
410 RSFilter *fdenoise= rs_filter_new("RSDenoise", fdcp);
411 RSFilter *ftransform_display = rs_filter_new("RSColorspaceTransform", fdenoise);
412 RSFilter *fend = ftransform_display;
413 RSFilterResponse *filter_response;
414 RSColorSpace *display_color_space;
415 gchar* cs_name;
416
417 if ((cs_name = rs_conf_get_string("display-colorspace")))
418 display_color_space = rs_color_space_new_singleton(cs_name);
419 else
420 display_color_space = rs_color_space_new_singleton("RSSrgb");
421
422 /* FIXME: This is just a temporary hack to make batch work */
423 #if 0
424 {
425 RSIccProfile *profile;
426
427 profile = NULL;
428 gchar *profile_filename = rs_conf_get_cms_profile(CMS_PROFILE_INPUT);
429 if (profile_filename)
430 {
431 profile = rs_icc_profile_new_from_file(profile_filename);
432 g_free(profile_filename);
433 }
434 /*if (!profile)
435 profile = rs_icc_profile_new_from_file(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "profiles" G_DIR_SEPARATOR_S "generic_camera_profile.icc");*/
436 g_object_set(finput, "icc-profile", profile, NULL);
437 g_object_unref(profile);
438
439 profile = NULL;
440 profile_filename = rs_conf_get_cms_profile(CMS_PROFILE_EXPORT);
441 if (profile_filename)
442 {
443 profile = rs_icc_profile_new_from_file(profile_filename);
444 g_free(profile_filename);
445 }
446 if (!profile)
447 profile = rs_icc_profile_new_from_file(PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "profiles" G_DIR_SEPARATOR_S "sRGB.icc");
448 g_object_set(fend, "icc-profile", profile, NULL);
449 g_object_unref(profile);
450 }
451 #endif
452 gdk_threads_enter();
453 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
454 gtk_window_set_transient_for(GTK_WINDOW(window), rawstudio_window);
455 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT);
456 gtk_window_set_destroy_with_parent(GTK_WINDOW(window), TRUE);
457 gtk_window_resize(GTK_WINDOW(window), 250, 250);
458 g_signal_connect((gpointer) window, "delete_event", G_CALLBACK(window_destroy), &abort_render);
459
460 cancel = gtk_button_new_with_label(_("Cancel"));
461 g_signal_connect (G_OBJECT(cancel), "clicked",
462 G_CALLBACK(cancel_clicked), &abort_render);
463
464 gtk_container_add (GTK_CONTAINER (window), vbox);
465 gtk_box_pack_start (GTK_BOX (vbox), gui_framed(preview, _("Last image:"), GTK_SHADOW_IN), TRUE, TRUE, 0);
466 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
467 gtk_box_pack_start (GTK_BOX (vbox), eta_label, FALSE, FALSE, 0);
468 gtk_box_pack_start (GTK_BOX (vbox), cancel, FALSE, FALSE, 0);
469 gtk_container_set_border_width(GTK_CONTAINER(vbox), 15);
470
471 gtk_widget_show_all(window);
472 while (gtk_events_pending()) gtk_main_iteration();
473
474 g_mkdir_with_parents(queue->directory, 00755);
475
476 g_get_current_time(&start_time);
477
478 while(gtk_tree_model_get_iter_first(queue->list, &iter) && (!abort_render))
479 {
480 left = rs_batch_num_entries(queue);
481 if (done > 0 && now_time.tv_sec > 0)
482 {
483 time = (gint) (now_time.tv_sec-start_time.tv_sec);
484 eta = (time/done)*left;
485 h = (eta/3600);
486 eta %= 3600;
487 m = (eta/60);
488 eta %= 60;
489 s = eta;
490
491 eta_text = g_strdup_printf(_("Time left: %dh %dm %ds"), h, m, s);
492 title_text = g_strdup_printf(_("Processing Image %d/%d"), done+1, done+left);
493 }
494 else
495 {
496 eta_text = g_strdup(_("Time left: ..."));
497 title_text = g_strdup_printf(_("Processing Image 1/%d."), left);
498 }
499
500 gtk_window_set_title(GTK_WINDOW(window), title_text);
501 gtk_label_set_text(GTK_LABEL(eta_label), eta_text);
502 g_free(eta_text);
503 g_free(title_text);
504 done++;
505
506 gtk_tree_model_get(queue->list, &iter,
507 RS_QUEUE_ELEMENT_FILENAME, &filename_in,
508 RS_QUEUE_ELEMENT_SETTING_ID, &setting_id,
509 -1);
510 basename = g_path_get_basename(filename_in);
511 g_string_printf(status, _("Loading %s ..."), basename);
512 gtk_label_set_text(GTK_LABEL(label), status->str);
513 while (gtk_events_pending()) gtk_main_iteration();
514 g_free(basename);
515
516 gdk_threads_leave();
517 photo = rs_photo_load_from_file(filename_in);
518 if (photo)
519 {
520 rs_metadata_load_from_file(photo->metadata, filename_in);
521 rs_cache_load(photo);
522
523 /* Build new filename */
524 if (NULL == g_strrstr(queue->filename, "%p"))
525 {
526 filename = g_string_new(queue->directory);
527 g_string_append(filename, G_DIR_SEPARATOR_S);
528 g_string_append(filename, queue->filename);
529 }
530 else
531 filename = g_string_new(queue->filename);
532
533 g_string_append(filename, ".");
534 g_string_append(filename, rs_output_get_extension(queue->output));
535 parsed_filename = filename_parse(filename->str, filename_in, setting_id);
536
537 /* Create directory, if it doesn't exist */
538 parsed_dir = g_path_get_dirname(parsed_filename);
539 if (FALSE == g_file_test(parsed_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
540 if (g_mkdir_with_parents(parsed_dir, 0x1ff))
541 {
542 gdk_threads_enter();
543 gui_status_notify(_("Could not create output directory."));
544 break;
545 }
546
547 GList *filters = g_list_append(NULL, fend);
548 rs_photo_apply_to_filters(photo, filters, setting_id);
549 g_list_free(filters);
550
551 rs_filter_set_recursive(fend,
552 "image", photo->input_response,
553 "filename", photo->filename,
554 "bounding-box", TRUE,
555 "width", 250,
556 "height", 250,
557 NULL);
558
559 /* Render preview image */
560 RSFilterRequest *request = rs_filter_request_new();
561 rs_filter_request_set_quick(RS_FILTER_REQUEST(request), FALSE);
562 /* FIXME: Should be set to output colorspace, not forced to sRGB */
563
564 rs_filter_param_set_object(RS_FILTER_PARAM(request), "colorspace", display_color_space);
565 filter_response = rs_filter_get_image8(fend, request);
566 pixbuf = rs_filter_response_get_image8(filter_response);
567 gdk_threads_enter();
568 if (pixbuf)
569 {
570 gtk_image_set_from_pixbuf(GTK_IMAGE(preview), pixbuf);
571 g_object_unref(pixbuf);
572 }
573 g_object_unref(request);
574 g_object_unref(filter_response);
575
576 if (left > 1)
577 {
578 GtkTreeIter iter2 = iter;
579 if (gtk_tree_model_iter_next (queue->list, &iter2))
580 {
581 gtk_tree_model_get(queue->list, &iter2, RS_QUEUE_ELEMENT_FILENAME, &filename_in, -1);
582 rs_io_idle_prefetch_file(filename_in, 0xC01A);
583 }
584 }
585 /* Build text for small preview-window */
586 basename = g_path_get_basename(parsed_filename);
587 g_string_printf(status, _("Saving %s ..."), basename);
588 gtk_label_set_text(GTK_LABEL(label), status->str);
589 while (gtk_events_pending())
590 gtk_main_iteration();
591 g_free(basename);
592 gdk_threads_leave();
593
594 width = 65535;
595 height = 65535;
596 /* Calculate new size */
597 switch (queue->size_lock)
598 {
599 case LOCK_SCALE:
600 scale = queue->scale/100.0;
601 rs_filter_get_size_simple(fcrop, RS_FILTER_REQUEST_QUICK, &width, &height);
602 width = (gint) (((gdouble) width) * scale);
603 height = (gint) (((gdouble) height) * scale);
604 break;
605 case LOCK_WIDTH:
606 width = queue->width;
607 break;
608 case LOCK_HEIGHT:
609 height = queue->height;
610 break;
611 case LOCK_BOUNDING_BOX:
612 width = queue->width;
613 height = queue->height;
614 break;
615 }
616 rs_filter_set_recursive(fend,
617 "width", width,
618 "height", height,
619 NULL);
620
621 /* Save the image */
622 if (g_object_class_find_property(G_OBJECT_GET_CLASS(queue->output), "filename"))
623 g_object_set(queue->output, "filename", parsed_filename, NULL);
624
625 rs_output_set_from_conf(queue->output, "batch");
626
627 g_assert(RS_IS_OUTPUT(queue->output));
628 g_assert(RS_IS_FILTER(fend));
629
630 gboolean exported = rs_output_execute(queue->output, fend);
631 gdk_threads_enter();
632 if (exported)
633 rs_store_set_flags(NULL, photo->filename, NULL, NULL, &exported);
634 else
635 {
636 gui_status_notify(_("Could not export photo."));
637 break;
638 }
639
640 g_free(parsed_filename);
641 g_string_free(filename, TRUE);
642 g_object_unref(photo);
643 photo = NULL;
644 }
645 gtk_list_store_remove(GTK_LIST_STORE(queue->list), &iter);
646 batch_queue_save(queue);
647
648 g_get_current_time(&now_time);
649 }
650 gtk_widget_destroy(window);
651
652 batch_queue_update_sensivity(queue);
653 gdk_threads_leave();
654
655 g_object_unref(finput);
656 g_object_unref(fdemosaic);
657 g_object_unref(ffujirotate);
658 g_object_unref(flensfun);
659 g_object_unref(frotate);
660 g_object_unref(fcrop);
661 g_object_unref(fcache);
662 g_object_unref(fresample);
663 g_object_unref(fdcp);
664 g_object_unref(fdenoise);
665 g_object_unref(ftransform_input);
666 g_object_unref(ftransform_display);
667 }
668
669 static void
cursor_changed(GtkTreeView * tree_view,gpointer user_data)670 cursor_changed(GtkTreeView *tree_view, gpointer user_data)
671 {
672 batch_queue_update_sensivity((RS_QUEUE *) user_data);
673 }
674
675 static GtkWidget *
make_batchview(RS_QUEUE * queue)676 make_batchview(RS_QUEUE *queue)
677 {
678 GtkWidget *scroller;
679 GtkWidget *view;
680 GtkCellRenderer *renderer_text, *renderer_pixbuf;
681 GtkTreeViewColumn *column_filename, *column_setting_id, *column_pixbuf;
682
683 scroller = gtk_scrolled_window_new (NULL, NULL);
684 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
685 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
686
687 view = gtk_tree_view_new_with_model(queue->list);
688 queue->view = GTK_TREE_VIEW(view);
689
690 gtk_tree_view_set_reorderable(queue->view, TRUE);
691 gtk_container_add (GTK_CONTAINER (scroller), view);
692
693 renderer_text = gtk_cell_renderer_text_new();
694 renderer_pixbuf = gtk_cell_renderer_pixbuf_new();
695
696 column_pixbuf = gtk_tree_view_column_new_with_attributes (_("Icon"),
697 renderer_pixbuf,
698 "pixbuf", RS_QUEUE_ELEMENT_THUMBNAIL,
699 NULL);
700 gtk_tree_view_column_set_resizable(column_pixbuf, TRUE);
701 gtk_tree_view_column_set_sizing(column_pixbuf, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
702
703 column_filename = gtk_tree_view_column_new_with_attributes (_("Filename"),
704 renderer_text,
705 "text", RS_QUEUE_ELEMENT_FILENAME_SHORT,
706 NULL);
707 gtk_tree_view_column_set_resizable(column_filename, TRUE);
708 gtk_tree_view_column_set_sizing(column_filename, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
709
710 column_setting_id = gtk_tree_view_column_new_with_attributes (_("Setting"),
711 renderer_text,
712 "text", RS_QUEUE_ELEMENT_SETTING_ID_ABC,
713 NULL);
714 gtk_tree_view_column_set_resizable(column_setting_id, TRUE);
715 gtk_tree_view_column_set_sizing(column_setting_id, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
716
717 gtk_tree_view_append_column (GTK_TREE_VIEW (view), column_pixbuf);
718 gtk_tree_view_append_column (GTK_TREE_VIEW (view), column_filename);
719 gtk_tree_view_append_column (GTK_TREE_VIEW (view), column_setting_id);
720
721 g_signal_connect(G_OBJECT(view), "cursor-changed", G_CALLBACK(cursor_changed), queue);
722 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (view), FALSE);
723
724 return scroller;
725 }
726
727 static void
batch_button_remove_clicked(GtkWidget * button,RS_QUEUE * queue)728 batch_button_remove_clicked(GtkWidget *button, RS_QUEUE *queue)
729 {
730 GtkTreePath *path;
731 GtkTreeViewColumn *column;
732
733 gtk_tree_view_get_cursor(queue->view,&path,&column);
734
735 if(path && column)
736 {
737 GtkTreeIter iter;
738
739 if(gtk_tree_model_get_iter(queue->list,&iter,path))
740 {
741 gtk_list_store_remove(GTK_LIST_STORE(queue->list), &iter);
742 batch_queue_save(queue);
743 }
744 }
745 rs_core_actions_update_menu_items(queue->rs); /* FIXME: should be done with a signal */
746 batch_queue_update_sensivity(queue);
747 return;
748 }
749
750 static void
batch_button_remove_all_clicked(GtkWidget * button,RS_QUEUE * queue)751 batch_button_remove_all_clicked(GtkWidget *button, RS_QUEUE *queue)
752 {
753 gtk_list_store_clear(GTK_LIST_STORE(queue->list));
754 batch_queue_save(queue);
755 batch_queue_update_sensivity(queue);
756 return;
757 }
758
759 static void
batch_button_start_clicked(GtkWidget * button,RS_QUEUE * queue)760 batch_button_start_clicked(GtkWidget *button, RS_QUEUE *queue)
761 {
762 rs_core_action_group_activate("ProcessBatch");
763 }
764
765 static void
batch_queue_update_sensivity(RS_QUEUE * queue)766 batch_queue_update_sensivity(RS_QUEUE *queue)
767 {
768 GtkTreePath *selected_path;
769 GtkTreeIter iter;
770
771 /* If we have any entries, enable "Start" and "Remove all" */
772 if (gtk_tree_model_get_iter_first(queue->list, &iter))
773 {
774 rs_core_action_group_set_sensivity("ProcessBatch", TRUE);
775 gtk_widget_set_sensitive(queue->start_button, TRUE);
776 gtk_widget_set_sensitive(queue->remove_all_button, TRUE);
777 }
778 else
779 {
780 rs_core_action_group_set_sensivity("ProcessBatch", FALSE);
781 gtk_widget_set_sensitive(queue->start_button, FALSE);
782 gtk_widget_set_sensitive(queue->remove_all_button, FALSE);
783 }
784
785 /* If anything is selected, enable "Remove" */
786 gtk_tree_view_get_cursor(queue->view, &selected_path, NULL);
787 if(selected_path)
788 {
789 gtk_widget_set_sensitive(queue->remove_button, TRUE);
790 gtk_tree_path_free(selected_path);
791 }
792 else
793 gtk_widget_set_sensitive(queue->remove_button, FALSE);
794 }
795
796 GtkWidget *
make_batchbuttons(RS_QUEUE * queue)797 make_batchbuttons(RS_QUEUE *queue)
798 {
799 GtkWidget *box;
800
801 box = gtk_hbox_new(FALSE,4);
802
803 queue->start_button = gui_button_new_from_stock_with_label(GTK_STOCK_EXECUTE, _("Start"));
804 g_signal_connect ((gpointer) queue->start_button, "clicked", G_CALLBACK (batch_button_start_clicked), queue);
805
806 queue->remove_button = gui_button_new_from_stock_with_label(GTK_STOCK_REMOVE, _("Remove"));
807 g_signal_connect ((gpointer) queue->remove_button, "clicked", G_CALLBACK (batch_button_remove_clicked), queue);
808
809 queue->remove_all_button = gui_button_new_from_stock_with_label(GTK_STOCK_REMOVE, _("Remove all"));
810 g_signal_connect ((gpointer) queue->remove_all_button, "clicked", G_CALLBACK (batch_button_remove_all_clicked), queue);
811
812 gtk_box_pack_start(GTK_BOX (box), queue->start_button, FALSE, FALSE, 0);
813 gtk_box_pack_start(GTK_BOX (box), queue->remove_button, FALSE, FALSE, 0);
814 gtk_box_pack_start(GTK_BOX (box), queue->remove_all_button, FALSE, FALSE, 0);
815
816 return box;
817 }
818
819 static void
chooser_changed(GtkFileChooser * chooser,gpointer user_data)820 chooser_changed(GtkFileChooser *chooser, gpointer user_data)
821 {
822 RS_QUEUE *queue = (RS_QUEUE *) user_data;
823 g_free(queue->directory);
824 queue->directory = gtk_file_chooser_get_filename(chooser);
825 rs_conf_set_string(CONF_BATCH_DIRECTORY, queue->directory);
826 return;
827 }
828
829 static void
filetype_changed(gpointer active,gpointer user_data)830 filetype_changed(gpointer active, gpointer user_data)
831 {
832 RS_QUEUE *queue = (RS_QUEUE *) user_data;
833 GType filetype = (GType)active;
834
835 if (!filetype)
836 return;
837 if (queue->output)
838 g_object_unref(queue->output);
839 queue->output = rs_output_new(g_type_name(filetype));
840 }
841
842 static void
size_lockbox_changed(gpointer selected,gpointer user_data)843 size_lockbox_changed(gpointer selected, gpointer user_data)
844 {
845 RS_QUEUE *queue = (RS_QUEUE *) user_data;
846 gint i;
847 queue->size_lock = GPOINTER_TO_INT(selected);
848
849 for(i=0;i<3;i++)
850 {
851 gtk_widget_hide(queue->size_width[i]);
852 gtk_widget_hide(queue->size_height[i]);
853 gtk_widget_hide(queue->size_scale[i]);
854 }
855
856 /* Show needed spinners */
857 switch (queue->size_lock)
858 {
859 case LOCK_WIDTH:
860 for(i=0;i<3;i++)
861 gtk_widget_show(queue->size_width[i]);
862 break;
863 case LOCK_HEIGHT:
864 for(i=0;i<3;i++)
865 gtk_widget_show(queue->size_height[i]);
866 break;
867 case LOCK_SCALE:
868 for(i=0;i<3;i++)
869 gtk_widget_show(queue->size_scale[i]);
870 break;
871 case LOCK_BOUNDING_BOX:
872 for(i=0;i<3;i++)
873 {
874 gtk_widget_show(queue->size_width[i]);
875 gtk_widget_show(queue->size_height[i]);
876 }
877 break;
878 }
879
880 size_update_infolabel(queue);
881
882 return;
883 }
884
885 static void
size_width_changed(GtkSpinButton * spinbutton,RS_QUEUE * queue)886 size_width_changed(GtkSpinButton *spinbutton, RS_QUEUE *queue)
887 {
888 queue->width = gtk_spin_button_get_value_as_int(spinbutton);
889 size_update_infolabel(queue);
890 rs_conf_set_integer(CONF_BATCH_SIZE_WIDTH, queue->width);
891 }
892
893 static void
size_height_changed(GtkSpinButton * spinbutton,RS_QUEUE * queue)894 size_height_changed(GtkSpinButton *spinbutton, RS_QUEUE *queue)
895 {
896 queue->height = gtk_spin_button_get_value_as_int(spinbutton);
897 size_update_infolabel(queue);
898 rs_conf_set_integer(CONF_BATCH_SIZE_HEIGHT, queue->height);
899 }
900
901 static void
size_scale_changed(GtkSpinButton * spinbutton,RS_QUEUE * queue)902 size_scale_changed(GtkSpinButton *spinbutton, RS_QUEUE *queue)
903 {
904 queue->scale = gtk_spin_button_get_value_as_int(spinbutton);
905 size_update_infolabel(queue);
906 rs_conf_set_integer(CONF_BATCH_SIZE_SCALE, queue->scale);
907 }
908
909 static void
size_close_clicked(GtkButton * button,RS_QUEUE * queue)910 size_close_clicked(GtkButton *button, RS_QUEUE *queue)
911 {
912 gtk_widget_hide(queue->size_window);
913 }
914
915 static void
edit_settings_clicked(GtkButton * button,RS_QUEUE * queue)916 edit_settings_clicked(GtkButton *button, RS_QUEUE *queue)
917 {
918 RSOutput *output = queue->output;
919 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Edit output settings"),
920 NULL,
921 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
922 GTK_STOCK_OK,
923 GTK_RESPONSE_ACCEPT,
924 NULL);
925 g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
926
927 GtkWidget *content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
928 GtkWidget *settings = rs_output_get_parameter_widget(output, "batch");
929 gtk_container_add (GTK_CONTAINER (content), settings);
930 gtk_widget_show_all(dialog);
931 gtk_dialog_run(GTK_DIALOG(dialog));
932 }
933
934 static void
batch_size_selection(GtkWidget * button,RS_QUEUE * queue)935 batch_size_selection(GtkWidget *button, RS_QUEUE *queue)
936 {
937 RS_CONFBOX *lockbox;
938 GtkWidget *vbox = gtk_vbox_new(FALSE, 4);
939 GtkWidget *table;
940 GtkWidget *close;
941
942 /* Only open one at a time */
943 if (queue->size_window)
944 {
945 /* Leave the window at its last position */
946 gtk_window_set_position(GTK_WINDOW(queue->size_window), GTK_WIN_POS_NONE);
947 gtk_widget_show(queue->size_window);
948 gtk_window_present(GTK_WINDOW(queue->size_window));
949 return;
950 }
951
952 /* Make window */
953 queue->size_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
954 g_signal_connect(G_OBJECT(queue->size_window), "delete_event", G_CALLBACK(gtk_widget_hide_on_delete), queue);
955 gtk_window_set_title (GTK_WINDOW(queue->size_window), _("Photo size"));
956 gtk_window_set_position(GTK_WINDOW(queue->size_window), GTK_WIN_POS_MOUSE);
957 gtk_widget_realize (queue->size_window);
958 gdk_window_set_type_hint(queue->size_window->window, GDK_WINDOW_TYPE_HINT_UTILITY);
959 gtk_window_set_transient_for(GTK_WINDOW(queue->size_window), rawstudio_window);
960
961 /* Chooser */
962 lockbox = gui_confbox_new(CONF_BATCH_SIZE_LOCK);
963 gui_confbox_add_entry(lockbox, "scale", _("Constant scale"), GINT_TO_POINTER(LOCK_SCALE));
964 gui_confbox_add_entry(lockbox, "width", _("Constant width"), GINT_TO_POINTER(LOCK_WIDTH));
965 gui_confbox_add_entry(lockbox, "height", _("Constant height"), GINT_TO_POINTER(LOCK_HEIGHT));
966 gui_confbox_add_entry(lockbox, "bounding-box", _("Maximum size"), GINT_TO_POINTER(LOCK_BOUNDING_BOX));
967 gui_confbox_load_conf(lockbox, "scale");
968 gtk_widget_show(gui_confbox_get_widget(lockbox));
969
970 gtk_box_pack_start (GTK_BOX (vbox), gui_confbox_get_widget(lockbox), FALSE, TRUE, 0);
971 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
972
973 /* Spinners */
974 table = gtk_table_new(3, 3, FALSE);
975 gtk_table_set_col_spacings(GTK_TABLE(table), 0);
976 gtk_table_set_row_spacings(GTK_TABLE(table), 0);
977 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
978
979 queue->size_width[0] = gtk_label_new(_("Photo width:"));
980 queue->size_width[1] = gtk_spin_button_new_with_range(10.0, 10000.0, 1.0);
981 queue->size_width[2] = gtk_label_new(_("pixels"));
982 gtk_spin_button_set_value(GTK_SPIN_BUTTON(queue->size_width[1]), (gdouble) queue->width);
983 g_signal_connect(G_OBJECT(queue->size_width[1]), "value_changed", G_CALLBACK(size_width_changed), queue);
984 gtk_table_attach_defaults(GTK_TABLE(table), queue->size_width[0], 0, 1, 0, 1);
985 gtk_table_attach_defaults(GTK_TABLE(table), queue->size_width[1], 1, 2, 0, 1);
986 gtk_table_attach_defaults(GTK_TABLE(table), queue->size_width[2], 2, 3, 0, 1);
987
988 queue->size_height[0] = gtk_label_new(_("Photo height:"));
989 queue->size_height[1] = gtk_spin_button_new_with_range(10.0, 10000.0, 1.0);
990 queue->size_height[2] = gtk_label_new(_("pixels"));
991 gtk_spin_button_set_value(GTK_SPIN_BUTTON(queue->size_height[1]), (gdouble) queue->height);
992 g_signal_connect(G_OBJECT(queue->size_height[1]), "value_changed", G_CALLBACK(size_height_changed), queue);
993 gtk_table_attach_defaults(GTK_TABLE(table), queue->size_height[0], 0, 1, 1, 2);
994 gtk_table_attach_defaults(GTK_TABLE(table), queue->size_height[1], 1, 2, 1, 2);
995 gtk_table_attach_defaults(GTK_TABLE(table), queue->size_height[2], 2, 3, 1, 2);
996
997 queue->size_scale[0] = gtk_label_new(_("Photo scale:"));
998 queue->size_scale[1] = gtk_spin_button_new_with_range(10.0, 10000.0, 1.0);
999 queue->size_scale[2] = gtk_label_new(_("%"));
1000 gtk_spin_button_set_value(GTK_SPIN_BUTTON(queue->size_scale[1]), (gdouble) queue->scale);
1001 g_signal_connect(G_OBJECT(queue->size_scale[1]), "value_changed", G_CALLBACK(size_scale_changed), queue);
1002 gtk_table_attach_defaults(GTK_TABLE(table), queue->size_scale[0], 0, 1, 2, 3);
1003 gtk_table_attach_defaults(GTK_TABLE(table), queue->size_scale[1], 1, 2, 2, 3);
1004 gtk_table_attach_defaults(GTK_TABLE(table), queue->size_scale[2], 2, 3, 2, 3);
1005
1006 /* Align everything nicely */
1007 gtk_misc_set_alignment(GTK_MISC(queue->size_height[0]), 1.0, 0.5);
1008 gtk_misc_set_alignment(GTK_MISC(queue->size_height[2]), 0.0, 0.5);
1009 gtk_misc_set_alignment(GTK_MISC(queue->size_width[0]), 1.0, 0.5);
1010 gtk_misc_set_alignment(GTK_MISC(queue->size_width[2]), 0.0, 0.5);
1011 gtk_misc_set_alignment(GTK_MISC(queue->size_scale[0]), 1.0, 0.5);
1012 gtk_misc_set_alignment(GTK_MISC(queue->size_scale[2]), 0.0, 0.5);
1013
1014 /* Close button */
1015 close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
1016 g_signal_connect (G_OBJECT(close), "clicked", G_CALLBACK (size_close_clicked), queue);
1017 gtk_box_pack_end (GTK_BOX (vbox), gui_aligned(close, 1.0, 0.5, 0.0, 0.0), FALSE, TRUE, 0);
1018
1019 gtk_container_add (GTK_CONTAINER (queue->size_window), vbox);
1020 gtk_widget_show_all(queue->size_window);
1021 gtk_window_present(GTK_WINDOW(queue->size_window));
1022
1023 size_lockbox_changed(gui_confbox_get_active(lockbox), queue);
1024 gui_confbox_set_callback(lockbox, queue, size_lockbox_changed);
1025 }
1026
1027 static void
size_update_infolabel(RS_QUEUE * queue)1028 size_update_infolabel(RS_QUEUE *queue)
1029 {
1030 GString *gs = g_string_new("");
1031
1032 switch (queue->size_lock)
1033 {
1034 case LOCK_WIDTH:
1035 g_string_printf(gs, _("Constant width:\n%d"), queue->width);
1036 break;
1037 case LOCK_HEIGHT:
1038 g_string_printf(gs, _("Constant height:\n%d"), queue->height);
1039 break;
1040 case LOCK_SCALE:
1041 g_string_printf(gs, _("Constant Scale:\n%d"), queue->scale);
1042 g_string_append(gs, "%"); /* FIXME: merge with the above line after release */
1043 break;
1044 case LOCK_BOUNDING_BOX:
1045 g_string_printf(gs, _("Maximum size:\n%d x %d"), queue->width, queue->height);
1046 break;
1047 }
1048
1049 gtk_label_set_justify(GTK_LABEL(queue->size_label), GTK_JUSTIFY_CENTER);
1050 gtk_label_set_label(GTK_LABEL(queue->size_label), gs->str);
1051
1052 g_string_free(gs, TRUE);
1053
1054 return;
1055 }
1056
1057 static GtkWidget *
make_batch_options(RS_QUEUE * queue)1058 make_batch_options(RS_QUEUE *queue)
1059 {
1060 GtkWidget *chooser;
1061 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
1062 GtkWidget *vbox = gtk_vbox_new(FALSE, 4);
1063 GtkWidget *filename;
1064 RS_CONFBOX *filetype_confbox;
1065 GtkWidget *size_button;
1066 gpointer active;
1067
1068 chooser = gtk_file_chooser_button_new(_("Choose output directory"),
1069 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
1070 if (g_path_is_absolute(queue->directory))
1071 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), queue->directory);
1072 g_signal_connect (chooser, "current_folder_changed",
1073 G_CALLBACK (chooser_changed), queue);
1074 gtk_box_pack_start (GTK_BOX (vbox), gui_framed(chooser,
1075 _("Output directory:"), GTK_SHADOW_NONE), FALSE, FALSE, 0);
1076
1077 filename = rs_filename_chooser_button_new(&queue->filename, CONF_BATCH_FILENAME);
1078 gtk_box_pack_start (GTK_BOX (vbox), gui_framed(filename,
1079 _("Filename template:"), GTK_SHADOW_NONE), FALSE, FALSE, 0);
1080
1081 filetype_confbox = gui_confbox_filetype_new(CONF_BATCH_FILETYPE);
1082 gui_confbox_set_callback(filetype_confbox, queue, filetype_changed);
1083 gtk_box_pack_start (GTK_BOX (vbox), gui_confbox_get_widget(filetype_confbox), FALSE, TRUE, 0);
1084
1085 active = gui_confbox_get_active(filetype_confbox);
1086 if (!active)
1087 active = GUINT_TO_POINTER(g_type_from_name("RSJpegfile"));
1088 filetype_changed(active, queue);
1089
1090 GtkWidget *edit_settings = gtk_button_new_with_label(_("Edit output settings"));
1091 g_signal_connect ((gpointer) edit_settings, "clicked", G_CALLBACK (edit_settings_clicked), queue);
1092 gtk_box_pack_start (GTK_BOX (vbox), edit_settings, FALSE, TRUE, 0);
1093
1094 /* Export size */
1095 hbox = gtk_hbox_new(FALSE, 1);
1096 queue->size_label = gtk_label_new(NULL);
1097 size_update_infolabel(queue);
1098 size_button = gtk_button_new();
1099 gtk_button_set_label(GTK_BUTTON(size_button), _("Change"));
1100 g_signal_connect ((gpointer) size_button, "clicked", G_CALLBACK (batch_size_selection), queue);
1101 gtk_box_pack_start (GTK_BOX (hbox), queue->size_label, FALSE, FALSE, 1);
1102 gtk_box_pack_end (GTK_BOX (hbox), size_button, FALSE, FALSE, 1);
1103
1104 gtk_box_pack_start (GTK_BOX (vbox), gui_framed(hbox, _("Export dimensions"), GTK_SHADOW_IN), FALSE, TRUE, 0);
1105
1106 return(vbox);
1107 }
1108
1109 GtkWidget *
make_batchbox(RS_QUEUE * queue)1110 make_batchbox(RS_QUEUE *queue)
1111 {
1112 GtkWidget *batchbox;
1113
1114 batchbox = gtk_vbox_new(FALSE,4);
1115 gtk_box_pack_start (GTK_BOX (batchbox), make_batch_options(queue), FALSE, FALSE, 0);
1116 gtk_box_pack_start (GTK_BOX (batchbox), make_batchview(queue), TRUE, TRUE, 0);
1117 gtk_box_pack_start (GTK_BOX (batchbox), make_batchbuttons(queue), FALSE, FALSE, 0);
1118 batch_queue_load(queue);
1119
1120 batch_queue_update_sensivity(queue);
1121
1122 return batchbox;
1123 }
1124
1125 /**
1126 * Returns the number of entries in the batch queue
1127 * @param queue A RS_QUEUE
1128 * @return The number of entries in the queue
1129 */
1130 gint
rs_batch_num_entries(RS_QUEUE * queue)1131 rs_batch_num_entries(RS_QUEUE *queue)
1132 {
1133 gint num = 0;
1134 GtkTreeIter iter;
1135
1136 gtk_tree_model_get_iter_first(queue->list, &iter);
1137
1138 if (gtk_list_store_iter_is_valid(GTK_LIST_STORE(queue->list), &iter))
1139 {
1140 do
1141 {
1142 num++;
1143 } while (gtk_tree_model_iter_next(queue->list, &iter));
1144 }
1145 return num;
1146 }
1147