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