1 /*
2 * Copyright (c) 2009 Giuseppe Torelli <colossus73@gmail.com>
3 * Copyright (c) 2009 Tadej Borovšak <tadeboro@gmail.com>
4 * Copyright (c) 2011 Robert Chéramy <robert@cheramy.net>
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 2 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 Library 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,write to the Free Software
18 * Foundation,Inc.,59 Temple Place - Suite 330,Boston,MA 02111-1307,USA.
19 *
20 */
21
22 #include "callbacks.h"
23 #include "export.h"
24 #include <math.h>
25 #include <sys/stat.h>
26
27 /* Internal structure, used for creating empty slide */
28 typedef struct _ImgEmptySlide ImgEmptySlide;
29 struct _ImgEmptySlide
30 {
31 /* Values */
32 gdouble c_start[3]; /* Start color */
33 gdouble c_stop[3]; /* Stop color */
34 gdouble pl_start[2]; /* Linear start point */
35 gdouble pl_stop[2]; /* Linear stop point */
36 gdouble pr_start[2]; /* Radial start point */
37 gdouble pr_stop[2]; /* Radial stop point */
38 gint drag; /* Are we draging point:
39 0 - no
40 1 - start point
41 2 - stop point */
42 gint gradient; /* Gradient type:
43 0 - solid color
44 1 - linear
45 2 - radial */
46
47 /* Widgets */
48 GtkWidget *color2;
49 GtkWidget *preview;
50 GtkWidget *radio[3];
51 };
52
53 static void img_file_chooser_add_preview(img_window_struct *);
54 static void img_update_preview_file_chooser(GtkFileChooser *,img_window_struct *);
55 static gboolean img_transition_timeout(img_window_struct *);
56 static gboolean img_still_timeout(img_window_struct *);
57 static void img_swap_toolbar_images( img_window_struct *, gboolean);
58 static void img_clean_after_preview(img_window_struct *);
59 static void img_about_dialog_activate_link(GtkAboutDialog * , const gchar *, gpointer );
60 static GdkPixbuf *img_rotate_pixbuf( GdkPixbuf *, GtkProgressBar *, ImgAngle );
61 static void img_rotate_selected_slides( img_window_struct *, gboolean );
62
63 static void
64 img_image_area_change_zoom( gdouble step,
65 gboolean reset,
66 img_window_struct *img );
67
68 static void
69 img_overview_change_zoom( gdouble step,
70 gboolean reset,
71 img_window_struct *img );
72
73 static void
74 img_gradient_toggled( GtkToggleButton *button,
75 ImgEmptySlide *slide );
76
77 static void
78 img_gradient_color_set( GtkColorButton *button,
79 ImgEmptySlide *slide );
80
81 static gboolean
82 img_gradient_expose( GtkWidget *widget,
83 GdkEventExpose *expose,
84 ImgEmptySlide *slide );
85
86 static gboolean
87 img_gradient_press( GtkWidget *widget,
88 GdkEventButton *button,
89 ImgEmptySlide *slide );
90
91 static gboolean
92 img_gradient_release( GtkWidget *widget,
93 GdkEventButton *button,
94 ImgEmptySlide *slide );
95
96 static gboolean
97 img_gradient_move( GtkWidget *widget,
98 GdkEventMotion *motion,
99 ImgEmptySlide *slide );
100
101
img_set_window_title(img_window_struct * img,gchar * text)102 void img_set_window_title(img_window_struct *img, gchar *text)
103 {
104 gchar *title = NULL;
105 static gchar version[] = VERSION "-" REVISION;
106
107 if (text == NULL)
108 {
109 title = g_strconcat("Imagination ", strcmp(REVISION, "-1") == 0 ? VERSION : version, NULL);
110 gtk_window_set_title (GTK_WINDOW (img->imagination_window), title);
111 g_free(title);
112 }
113 else
114 {
115 title = g_strconcat(text, " - Imagination ", strcmp(REVISION, "-1") == 0 ? VERSION : version, NULL);
116 gtk_window_set_title (GTK_WINDOW (img->imagination_window), title);
117 g_free(title);
118 }
119 }
120
img_new_slideshow(GtkMenuItem * item,img_window_struct * img_struct)121 void img_new_slideshow(GtkMenuItem *item,img_window_struct *img_struct)
122 {
123 if (img_struct->project_is_modified)
124 if (GTK_RESPONSE_OK != img_ask_user_confirmation(img_struct, _("You didn't save your slideshow yet. Are you sure you want to close it?")))
125 return;
126 img_close_slideshow(GTK_WIDGET(item), img_struct);
127 img_new_slideshow_settings_dialog(img_struct, FALSE);
128 }
129
img_project_properties(GtkMenuItem * item,img_window_struct * img_struct)130 void img_project_properties(GtkMenuItem *item, img_window_struct *img_struct)
131 {
132 img_new_slideshow_settings_dialog(img_struct, TRUE);
133 }
134
img_add_slides_thumbnails(GtkMenuItem * item,img_window_struct * img)135 void img_add_slides_thumbnails(GtkMenuItem *item, img_window_struct *img)
136 {
137 GSList *slides = NULL, *bak;
138 GdkPixbuf *thumb;
139 GtkTreeIter iter;
140 slide_struct *slide_info;
141 gint slides_cnt = 0, actual_slides = 0;
142
143 slides = img_import_slides_file_chooser(img);
144
145 if (slides == NULL)
146 return;
147
148 actual_slides = img->slides_nr;
149 img->slides_nr += g_slist_length(slides);
150 gtk_widget_show(img->progress_bar);
151
152 /* Remove model from thumbnail iconview for efficiency */
153 g_object_ref( G_OBJECT( img->thumbnail_model ) );
154 gtk_icon_view_set_model( GTK_ICON_VIEW( img->thumbnail_iconview ), NULL );
155 gtk_icon_view_set_model( GTK_ICON_VIEW( img->over_icon ), NULL );
156
157 bak = slides;
158 while (slides)
159 {
160 if( img_scale_image( slides->data, img->video_ratio, 88, 0,
161 img->distort_images, img->background_color,
162 &thumb, NULL ) )
163 {
164 slide_info = img_create_new_slide();
165 if (slide_info)
166 {
167 img_set_slide_file_info( slide_info, slides->data );
168 gtk_list_store_append (img->thumbnail_model,&iter);
169 gtk_list_store_set (img->thumbnail_model, &iter, 0, thumb,
170 1, slide_info,
171 2, NULL,
172 3, FALSE,
173 -1);
174 g_object_unref (thumb);
175 slides_cnt++;
176 }
177 g_free(slides->data);
178 }
179 img_increase_progressbar(img, slides_cnt);
180 slides = slides->next;
181 }
182 gtk_widget_hide(img->progress_bar);
183 g_slist_free(bak);
184 img_set_total_slideshow_duration(img);
185 img_set_statusbar_message(img,0);
186 img->project_is_modified = TRUE;
187
188 gtk_icon_view_set_model( GTK_ICON_VIEW( img->thumbnail_iconview ),
189 GTK_TREE_MODEL( img->thumbnail_model ) );
190 gtk_icon_view_set_model( GTK_ICON_VIEW( img->over_icon ),
191 GTK_TREE_MODEL( img->thumbnail_model ) );
192 g_object_unref( G_OBJECT( img->thumbnail_model ) );
193
194 /* Select the first slide */
195 if (actual_slides == 0)
196 img_goto_first_slide(NULL, img);
197
198 /* Select the first loaded slide if a previous set of slides was loaded */
199 else
200 img_select_nth_slide(img, actual_slides);
201 }
202
img_increase_progressbar(img_window_struct * img,gint nr)203 void img_increase_progressbar(img_window_struct *img, gint nr)
204 {
205 gchar *message;
206 gdouble percent;
207
208 percent = (gdouble)nr / img->slides_nr;
209 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (img->progress_bar), percent);
210 message = g_strdup_printf( _("Please wait, importing image %d out of %d"),
211 nr, img->slides_nr );
212 gtk_statusbar_push(GTK_STATUSBAR(img->statusbar), img->context_id, message);
213 g_free(message);
214
215 while (gtk_events_pending())
216 gtk_main_iteration();
217 }
218
img_remove_audio_files(GtkWidget * widget,img_window_struct * img)219 void img_remove_audio_files (GtkWidget *widget, img_window_struct *img)
220 {
221 GtkTreeSelection *sel;
222 GtkTreePath *path;
223 GtkTreeIter iter;
224 GList *rr_list = NULL;
225 GList *node;
226 gchar *time;
227 gint secs;
228
229 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(img->music_file_treeview));
230 gtk_tree_selection_selected_foreach(sel, (GtkTreeSelectionForeachFunc) img_remove_foreach_func, &rr_list);
231
232 for (node = rr_list; node != NULL; node = node->next)
233 {
234 path = gtk_tree_row_reference_get_path((GtkTreeRowReference *) node->data);
235 if (path)
236 {
237 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(img->music_file_liststore), &iter, path))
238 {
239 gtk_tree_model_get(GTK_TREE_MODEL(img->music_file_liststore), &iter, 3, &secs, -1);
240 gtk_list_store_remove(img->music_file_liststore, &iter);
241 }
242 gtk_tree_path_free(path);
243 }
244 img->total_music_secs -= secs;
245 }
246 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(img->music_file_liststore), &iter) == FALSE)
247 {
248 img_play_stop_selected_file(NULL, img);
249 gtk_widget_set_sensitive (img->remove_audio_button, FALSE);
250 gtk_widget_set_sensitive (img->play_audio_button, FALSE);
251 gtk_label_set_text(GTK_LABEL(img->music_time_data), "");
252 }
253 else
254 {
255 time = img_convert_seconds_to_time(img->total_music_secs);
256 gtk_label_set_text(GTK_LABEL(img->music_time_data), time);
257 g_free(time);
258 }
259 g_list_foreach(rr_list, (GFunc) gtk_tree_row_reference_free, NULL);
260 g_list_free(rr_list);
261 }
262
img_remove_foreach_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,GList ** rowref_list)263 void img_remove_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GList **rowref_list)
264 {
265 GtkTreeRowReference *rowref;
266
267 rowref = gtk_tree_row_reference_new(model, path);
268 *rowref_list = g_list_append(*rowref_list, rowref);
269 }
270
img_select_audio_files_to_add(GtkMenuItem * button,img_window_struct * img)271 void img_select_audio_files_to_add ( GtkMenuItem* button, img_window_struct *img)
272 {
273 GtkFileFilter *audio_filter, *all_files_filter;
274 GtkWidget *fs;
275 GSList *files = NULL;
276 gint response;
277 gchar *time = NULL;
278
279 fs = gtk_file_chooser_dialog_new( _("Import audio files, use CTRL key "
280 "for multiple select"),
281 GTK_WINDOW (img->imagination_window),
282 GTK_FILE_CHOOSER_ACTION_OPEN,
283 GTK_STOCK_CANCEL,
284 GTK_RESPONSE_CANCEL,
285 GTK_STOCK_OPEN,
286 GTK_RESPONSE_ACCEPT,
287 NULL );
288
289 /* only audio files filter */
290 audio_filter = gtk_file_filter_new ();
291 gtk_file_filter_set_name (audio_filter, _("All audio files") );
292 gtk_file_filter_add_pattern (audio_filter, "*.wav");
293 gtk_file_filter_add_pattern (audio_filter, "*.mp3");
294 gtk_file_filter_add_pattern (audio_filter, "*.ogg");
295 gtk_file_filter_add_pattern (audio_filter, "*.flac");
296 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fs), audio_filter);
297
298 /* All files filter */
299 all_files_filter = gtk_file_filter_new ();
300 gtk_file_filter_set_name(all_files_filter, _("All files"));
301 gtk_file_filter_add_pattern(all_files_filter, "*");
302 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fs), all_files_filter);
303
304 gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (fs), TRUE);
305
306 response = gtk_dialog_run (GTK_DIALOG (fs));
307 if (response == GTK_RESPONSE_ACCEPT)
308 {
309 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (fs));
310 g_slist_foreach( files, (GFunc) img_add_audio_files, img);
311 }
312 if (files != NULL)
313 {
314 g_slist_foreach(files, (GFunc) g_free, NULL);
315 g_slist_free (files);
316 }
317
318 /* Update incompatibilities display */
319 img_update_inc_audio_display( img );
320
321 time = img_convert_seconds_to_time(img->total_music_secs);
322 gtk_label_set_text(GTK_LABEL(img->music_time_data), time);
323 g_free(time);
324
325 gtk_widget_destroy (fs);
326 }
327
img_add_audio_files(gchar * filename,img_window_struct * img)328 void img_add_audio_files (gchar *filename, img_window_struct *img)
329 {
330 GtkTreeIter iter;
331 gchar *path, *file, *time;
332 gint secs;
333
334 path = g_path_get_dirname(filename);
335 file = g_path_get_basename(filename);
336 time = img_get_audio_length(img, filename, &secs);
337
338 if (time != NULL)
339 {
340 gtk_list_store_append(img->music_file_liststore, &iter);
341 gtk_list_store_set (img->music_file_liststore, &iter, 0, path, 1, file, 2, time, 3, secs, -1);
342
343 g_free(time);
344 }
345 g_free(path);
346 g_free(file);
347 }
348
img_import_slides_file_chooser(img_window_struct * img)349 GSList *img_import_slides_file_chooser(img_window_struct *img)
350 {
351 GtkFileFilter *all_images_filter, *all_files_filter;
352 GSList *slides = NULL;
353 int response;
354
355 img->import_slide_chooser =
356 gtk_file_chooser_dialog_new( _("Import images, use SHIFT key for "
357 "multiple select"),
358 GTK_WINDOW (img->imagination_window),
359 GTK_FILE_CHOOSER_ACTION_OPEN,
360 GTK_STOCK_CANCEL,
361 GTK_RESPONSE_CANCEL,
362 GTK_STOCK_OPEN,
363 GTK_RESPONSE_ACCEPT,
364 NULL);
365 img_file_chooser_add_preview(img);
366
367 /* Image files filter */
368 all_images_filter = gtk_file_filter_new ();
369 gtk_file_filter_set_name(all_images_filter,_("All image files"));
370 gtk_file_filter_add_pixbuf_formats( all_images_filter );
371 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(img->import_slide_chooser),all_images_filter);
372
373 /* All files filter */
374 all_files_filter = gtk_file_filter_new ();
375 gtk_file_filter_set_name(all_files_filter,_("All files"));
376 gtk_file_filter_add_pattern(all_files_filter,"*");
377 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(img->import_slide_chooser),all_files_filter);
378
379 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(img->import_slide_chooser),TRUE);
380 if (img->current_dir)
381 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(img->import_slide_chooser),img->current_dir);
382 response = gtk_dialog_run (GTK_DIALOG(img->import_slide_chooser));
383 if (response == GTK_RESPONSE_ACCEPT)
384 {
385 slides = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(img->import_slide_chooser));
386 if (img->current_dir)
387 g_free(img->current_dir);
388 img->current_dir = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(img->import_slide_chooser));
389 }
390 gtk_widget_destroy (img->import_slide_chooser);
391 return slides;
392 }
393
img_free_allocated_memory(img_window_struct * img_struct)394 void img_free_allocated_memory(img_window_struct *img_struct)
395 {
396 GtkTreeModel *model;
397 GtkTreeIter iter;
398 slide_struct *entry;
399
400 /* Free the memory allocated the single slides one by one */
401 if (img_struct->slides_nr)
402 {
403 model = GTK_TREE_MODEL( img_struct->thumbnail_model );
404
405 gtk_tree_model_get_iter_first(model,&iter);
406 do
407 {
408 gtk_tree_model_get(model, &iter,1,&entry,-1);
409 img_free_slide_struct( entry );
410 img_struct->slides_nr--;
411 }
412 while (gtk_tree_model_iter_next (model,&iter));
413 g_signal_handlers_block_by_func((gpointer)img_struct->thumbnail_iconview, (gpointer)img_iconview_selection_changed, img_struct);
414 g_signal_handlers_block_by_func((gpointer)img_struct->over_icon, (gpointer)img_iconview_selection_changed, img_struct);
415 gtk_list_store_clear(GTK_LIST_STORE(img_struct->thumbnail_model));
416 g_signal_handlers_unblock_by_func((gpointer)img_struct->thumbnail_iconview, (gpointer)img_iconview_selection_changed, img_struct);
417 g_signal_handlers_unblock_by_func((gpointer)img_struct->over_icon, (gpointer)img_iconview_selection_changed, img_struct);
418 }
419
420 /* Unlink the possible created rotated pictures and free the GSlist */
421 /* NOTE: This is now done by img_free_slide_struct function */
422
423 /* Delete the audio files in the liststore */
424 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(img_struct->music_file_liststore), &iter))
425 {
426 gtk_list_store_clear(img_struct->music_file_liststore);
427 img_struct->total_music_secs = 0;
428 gtk_label_set_text(GTK_LABEL(img_struct->music_time_data), "");
429 }
430
431 /* Free gchar pointers */
432 if (img_struct->current_dir)
433 {
434 g_free(img_struct->current_dir);
435 img_struct->current_dir = NULL;
436 }
437
438 if (img_struct->project_current_dir)
439 {
440 g_free(img_struct->project_current_dir);
441 img_struct->project_current_dir = NULL;
442 }
443
444 if (img_struct->project_filename)
445 {
446 g_free(img_struct->project_filename);
447 img_struct->project_filename = NULL;
448 }
449 }
450
img_ask_user_confirmation(img_window_struct * img_struct,gchar * msg)451 gint img_ask_user_confirmation(img_window_struct *img_struct, gchar *msg)
452 {
453 GtkWidget *dialog;
454 gint response;
455
456 dialog = gtk_message_dialog_new(GTK_WINDOW(img_struct->imagination_window),GTK_DIALOG_MODAL,GTK_MESSAGE_QUESTION,GTK_BUTTONS_OK_CANCEL, "%s.", msg);
457 gtk_window_set_title(GTK_WINDOW(dialog),"Imagination");
458 response = gtk_dialog_run (GTK_DIALOG (dialog));
459 gtk_widget_destroy (GTK_WIDGET (dialog));
460 return response;
461 }
462
img_quit_application(GtkWidget * widget,GdkEvent * event,img_window_struct * img_struct)463 gboolean img_quit_application(GtkWidget *widget, GdkEvent *event, img_window_struct *img_struct)
464 {
465 gint response;
466
467 if (img_struct->project_is_modified)
468 {
469 response = img_ask_user_confirmation( img_struct, _("You didn't save your slideshow yet. Are you sure you want to close it?"));
470 if (response != GTK_RESPONSE_OK)
471 return TRUE;
472 }
473 if( img_save_window_settings( img_struct ) )
474 return( TRUE );
475 img_free_allocated_memory(img_struct);
476
477 /* Unloads the plugins */
478 g_slist_foreach(img_struct->plugin_list,(GFunc)g_module_close,NULL);
479 g_slist_free(img_struct->plugin_list);
480
481 return FALSE;
482 }
483
img_file_chooser_add_preview(img_window_struct * img_struct)484 static void img_file_chooser_add_preview(img_window_struct *img_struct)
485 {
486 GtkWidget *vbox;
487
488 vbox = gtk_vbox_new (FALSE, 5);
489 gtk_container_set_border_width (GTK_CONTAINER(vbox), 10);
490
491 img_struct->preview_image = gtk_image_new ();
492
493 img_struct->dim_label = gtk_label_new (NULL);
494 img_struct->size_label = gtk_label_new (NULL);
495
496 gtk_box_pack_start (GTK_BOX (vbox), img_struct->preview_image, FALSE, TRUE, 0);
497 gtk_box_pack_start (GTK_BOX (vbox), img_struct->dim_label, FALSE, TRUE, 0);
498 gtk_box_pack_start (GTK_BOX (vbox), img_struct->size_label, FALSE, TRUE, 0);
499 gtk_widget_show_all (vbox);
500
501 gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER(img_struct->import_slide_chooser), vbox);
502 gtk_file_chooser_set_preview_widget_active (GTK_FILE_CHOOSER(img_struct->import_slide_chooser), FALSE);
503
504 g_signal_connect (img_struct->import_slide_chooser, "update-preview",G_CALLBACK (img_update_preview_file_chooser), img_struct);
505 }
506
img_update_preview_file_chooser(GtkFileChooser * file_chooser,img_window_struct * img_struct)507 static void img_update_preview_file_chooser(GtkFileChooser *file_chooser,img_window_struct *img_struct)
508 {
509 gchar *filename,*size;
510 gboolean has_preview = FALSE;
511 gint width,height;
512 GdkPixbuf *pixbuf;
513 GdkPixbufFormat *pixbuf_format;
514
515 filename = gtk_file_chooser_get_filename(file_chooser);
516 if (filename == NULL)
517 {
518 gtk_file_chooser_set_preview_widget_active (file_chooser, has_preview);
519 return;
520 }
521 pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, 93, 70, TRUE, NULL);
522 has_preview = (pixbuf != NULL);
523 if (has_preview)
524 {
525 pixbuf_format = gdk_pixbuf_get_file_info(filename,&width,&height);
526 gtk_image_set_from_pixbuf (GTK_IMAGE(img_struct->preview_image), pixbuf);
527 g_object_unref (pixbuf);
528
529 size = g_strdup_printf(ngettext("%d x %d pixels", "%d x %d pixels", height),width,height);
530 gtk_label_set_text(GTK_LABEL(img_struct->dim_label),size);
531 g_free(size);
532 }
533 g_free(filename);
534 gtk_file_chooser_set_preview_widget_active (file_chooser, has_preview);
535 }
536
img_delete_selected_slides(GtkMenuItem * item,img_window_struct * img_struct)537 void img_delete_selected_slides(GtkMenuItem *item,img_window_struct *img_struct)
538 {
539 GList *selected, *bak;
540 GtkTreeIter iter;
541 GtkTreeModel *model;
542 slide_struct *entry;
543
544 model = GTK_TREE_MODEL( img_struct->thumbnail_model );
545
546 selected = gtk_icon_view_get_selected_items(GTK_ICON_VIEW(img_struct->active_icon));
547 if (selected == NULL)
548 return;
549
550 /* Free the slide struct for each slide and remove it from the iconview */
551 bak = selected;
552 g_signal_handlers_block_by_func( (gpointer)img_struct->thumbnail_iconview,
553 (gpointer)img_iconview_selection_changed,
554 img_struct );
555 g_signal_handlers_block_by_func( (gpointer)img_struct->over_icon,
556 (gpointer)img_iconview_selection_changed,
557 img_struct );
558 while (selected)
559 {
560 gtk_tree_model_get_iter(model, &iter,selected->data);
561 gtk_tree_model_get(model, &iter,1,&entry,-1);
562 img_free_slide_struct( entry );
563 gtk_list_store_remove(GTK_LIST_STORE(img_struct->thumbnail_model),&iter);
564 img_struct->slides_nr--;
565 selected = selected->next;
566 }
567 g_signal_handlers_unblock_by_func( (gpointer)img_struct->thumbnail_iconview,
568 (gpointer)img_iconview_selection_changed,
569 img_struct );
570 g_signal_handlers_unblock_by_func( (gpointer)img_struct->over_icon,
571 (gpointer)img_iconview_selection_changed,
572 img_struct );
573 g_list_foreach (bak, (GFunc)gtk_tree_path_free, NULL);
574 g_list_free(bak);
575
576 img_set_statusbar_message(img_struct,0);
577 cairo_surface_destroy( img_struct->current_image );
578 img_struct->current_image = NULL;
579 gtk_widget_queue_draw( img_struct->image_area );
580 img_struct->project_is_modified = TRUE;
581 img_iconview_selection_changed(GTK_ICON_VIEW(img_struct->active_icon),img_struct);
582 }
583
584 void
img_rotate_slides_left(GtkWidget * widget,img_window_struct * img)585 img_rotate_slides_left( GtkWidget *widget,
586 img_window_struct *img )
587 {
588 img_rotate_selected_slides( img, TRUE );
589 }
590
591 void
img_rotate_slides_right(GtkWidget * widget,img_window_struct * img)592 img_rotate_slides_right( GtkWidget *widget,
593 img_window_struct *img )
594 {
595 img_rotate_selected_slides( img, FALSE );
596 }
597
598 static void
img_rotate_selected_slides(img_window_struct * img,gboolean clockwise)599 img_rotate_selected_slides( img_window_struct *img,
600 gboolean clockwise )
601 {
602 GtkTreeModel *model;
603 GtkTreeIter iter;
604 GList *selected,
605 *bak;
606 GdkPixbuf *thumb;
607 slide_struct *info_slide;
608
609 /* Obtain the selected slideshow filename */
610 model = GTK_TREE_MODEL( img->thumbnail_model );
611 selected = gtk_icon_view_get_selected_items(
612 GTK_ICON_VIEW( img->active_icon ) );
613
614 if( selected == NULL)
615 return;
616
617 gtk_widget_show(img->progress_bar);
618
619 bak = selected;
620 while (selected)
621 {
622 ImgAngle angle;
623
624 gtk_tree_model_get_iter( model, &iter, selected->data );
625 gtk_tree_model_get( model, &iter, 1, &info_slide, -1 );
626
627 angle = ( info_slide->angle + ( clockwise ? 1 : -1 ) ) % 4;
628 img_rotate_slide( info_slide, angle, GTK_PROGRESS_BAR( img->progress_bar ) );
629
630 /* Display the rotated image in thumbnails iconview */
631 img_scale_image( info_slide->r_filename, img->video_ratio, 88, 0,
632 img->distort_images, img->background_color,
633 &thumb, NULL );
634 gtk_list_store_set( img->thumbnail_model, &iter, 0, thumb, -1 );
635 selected = selected->next;
636 }
637 gtk_widget_hide(img->progress_bar);
638 g_list_foreach (bak, (GFunc)gtk_tree_path_free, NULL);
639 g_list_free(bak);
640
641 /* If no slide is selected currently, simply return */
642 if( ! img->current_slide )
643 return;
644
645 cairo_surface_destroy( img->current_image );
646
647 /* Respect quality settings */
648 if( img->low_quality )
649 img_scale_image( img->current_slide->r_filename, img->video_ratio,
650 0, img->video_size[1], img->distort_images,
651 img->background_color, NULL, &img->current_image );
652 else
653 img_scale_image( img->current_slide->r_filename, img->video_ratio,
654 0, 0, img->distort_images,
655 img->background_color, NULL, &img->current_image );
656
657 gtk_widget_queue_draw( img->image_area );
658 }
659
660 /* Rotate clockwise */
img_rotate_pixbuf(GdkPixbuf * original,GtkProgressBar * progress,ImgAngle angle)661 static GdkPixbuf *img_rotate_pixbuf( GdkPixbuf *original,
662 GtkProgressBar *progress,
663 ImgAngle angle )
664 {
665 GdkPixbuf *new;
666 gint w, h, r1, r2, channels, bps;
667 GdkColorspace colorspace;
668 gboolean alpha;
669 guchar *pixels1, *pixels2;
670 gint i, j;
671
672 /* Get data from source */
673 g_object_get( G_OBJECT( original ), "width", &w,
674 "height", &h,
675 "rowstride", &r1,
676 "n_channels", &channels,
677 "bits_per_sample", &bps,
678 "colorspace", &colorspace,
679 "has_alpha", &alpha,
680 "pixels", &pixels1,
681 NULL );
682
683 switch( angle )
684 {
685 case ANGLE_0:
686 g_object_ref( G_OBJECT( original ) );
687 new = original;
688 break;
689
690 case ANGLE_90:
691 /* Create new rotated image */
692 new = gdk_pixbuf_new( colorspace, alpha, bps, h, w );
693 g_object_get( G_OBJECT( new ), "rowstride", &r2,
694 "pixels", &pixels2,
695 NULL );
696
697 /* Copy data, applying transormation along the way */
698 for( j = 0; j < h; j++ )
699 {
700 for( i = 0; i < w; i++ )
701 {
702 int source = i * channels + r1 * j;
703 int dest = j * channels + r2 * ( w - i - 1 );
704 int n;
705
706 for( n = 0; n < channels; n++ )
707 pixels2[dest + n] = pixels1[source + n];
708 }
709
710 if( j % 100 )
711 continue;
712
713 /* Update progress bar */
714 gtk_progress_bar_set_fraction( progress, (gdouble)( j + 1 ) / h );
715 while( gtk_events_pending() )
716 gtk_main_iteration();
717 }
718 break;
719
720 case ANGLE_180:
721 /* Create new rotated image */
722 new = gdk_pixbuf_new( colorspace, alpha, bps, w, h );
723 g_object_get( G_OBJECT( new ), "rowstride", &r2,
724 "pixels", &pixels2,
725 NULL );
726
727 /* Copy data, applying transormation along the way */
728 for( j = 0; j < h; j++ )
729 {
730 for( i = 0; i < w; i++ )
731 {
732 int source = i * channels + r1 * j;
733 int dest = ( w - i - 1 ) * channels + r2 * ( h - j - 1 );
734 int n;
735
736 for( n = 0; n < channels; n++ )
737 pixels2[dest + n] = pixels1[source + n];
738 }
739
740 if( j % 100 )
741 continue;
742
743 /* Update progress bar */
744 gtk_progress_bar_set_fraction( progress, (gdouble)( j + 1 ) / h );
745 while( gtk_events_pending() )
746 gtk_main_iteration();
747 }
748 break;
749
750 case ANGLE_270:
751 /* Create new rotated image */
752 new = gdk_pixbuf_new( colorspace, alpha, bps, h, w );
753 g_object_get( G_OBJECT( new ), "rowstride", &r2,
754 "pixels", &pixels2,
755 NULL );
756
757 /* Copy data, applying transormation along the way */
758 for( j = 0; j < h; j++ )
759 {
760 for( i = 0; i < w; i++ )
761 {
762 int source = i * channels + r1 * j;
763 int dest = ( h - j - 1 ) * channels + r2 * i;
764 int n;
765
766 for( n = 0; n < channels; n++ )
767 pixels2[dest + n] = pixels1[source + n];
768 }
769
770 if( j % 100 )
771 continue;
772
773 /* Update progress bar */
774 gtk_progress_bar_set_fraction( progress, (gdouble)( j + 1 ) / h );
775 while( gtk_events_pending() )
776 gtk_main_iteration();
777 }
778 break;
779 }
780
781 return( new );
782 }
783
img_show_about_dialog(GtkMenuItem * item,img_window_struct * img_struct)784 void img_show_about_dialog (GtkMenuItem *item,img_window_struct *img_struct)
785 {
786 static GtkWidget *about = NULL;
787 static gchar version[] = VERSION "-" REVISION;
788 const char *authors[] = {"\nDevelopers:\nGiuseppe Torelli <colossus73@gmail.com>\nTadej Borovšak <tadeboro@gmail.com>\nRobert Chéramy <robert@cheramy.net>\n\nImagination logo:\nhttp://linuxgraphicsusers.com\n\nInsert Transitions Family:\nJean-Pierre Redonnet <inphilly@gmail.com>",NULL};
789 //const char *documenters[] = {NULL};
790
791 if (about == NULL)
792 {
793 about = gtk_about_dialog_new ();
794 gtk_about_dialog_set_url_hook(img_about_dialog_activate_link, NULL, NULL);
795 gtk_window_set_position (GTK_WINDOW (about),GTK_WIN_POS_CENTER_ON_PARENT);
796 gtk_window_set_transient_for (GTK_WINDOW (about),GTK_WINDOW (img_struct->imagination_window));
797 gtk_window_set_destroy_with_parent (GTK_WINDOW (about),TRUE);
798 g_object_set (about,
799 "name", "Imagination",
800 "version", strcmp(REVISION, "-1") == 0 ? VERSION : version,
801 "copyright","Copyright \xC2\xA9 2009 Giuseppe Torelli",
802 "comments","A simple and lightweight DVD slideshow maker",
803 "authors",authors,
804 "documenters",NULL,
805 "translator_credits",_("translator-credits"),
806 "logo_icon_name","imagination",
807 "website","http://imagination.sf.net",
808 "license","Copyright \xC2\xA9 2009 Giuseppe Torelli - Colossus <colossus73@gmail.com>\n\n"
809 "This is free software; you can redistribute it and/or\n"
810 "modify it under the terms of the GNU Library General Public License as\n"
811 "published by the Free Software Foundation; either version 2 of the\n"
812 "License,or (at your option) any later version.\n"
813 "\n"
814 "This software is distributed in the hope that it will be useful,\n"
815 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
816 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
817 "Library General Public License for more details.\n"
818 "\n"
819 "You should have received a copy of the GNU Library General Public\n"
820 "License along with the Gnome Library; see the file COPYING.LIB. If not,\n"
821 "write to the Free Software Foundation,Inc.,59 Temple Place - Suite 330,\n"
822 "Boston,MA 02111-1307,USA.\n",
823 NULL);
824 }
825 gtk_dialog_run ( GTK_DIALOG(about));
826 gtk_widget_hide (about);
827 }
828
img_about_dialog_activate_link(GtkAboutDialog * dialog,const gchar * link,gpointer data)829 static void img_about_dialog_activate_link(GtkAboutDialog * dialog, const gchar *link, gpointer data)
830 {
831 /* Replace xdg-open with GTK+ equivalent */
832 gtk_show_uri( NULL, link, GDK_CURRENT_TIME, NULL );
833 }
834
img_start_stop_preview(GtkWidget * button,img_window_struct * img)835 void img_start_stop_preview(GtkWidget *button, img_window_struct *img)
836 {
837 GtkTreeIter iter, prev;
838 GtkTreePath *path = NULL;
839 slide_struct *entry;
840 GtkTreeModel *model;
841 GList *list = NULL;
842
843 /* If no images are present, abort */
844 if( img->slides_nr == 0 )
845 return;
846
847 if(img->export_is_running)
848 return;
849
850 if (img->preview_is_running)
851 {
852 /* Preview is already running */
853
854 /* Remove timeout function from main loop */
855 g_source_remove(img->source_id);
856
857 /* Clean resources used by preview and prepare application for
858 * next preview. */
859 img_clean_after_preview(img);
860 }
861 else
862 {
863 /* Start the preview */
864 if( img->mode == 1 )
865 {
866 img->auto_switch = TRUE;
867 img_switch_mode( img, 0 );
868 }
869
870 model = GTK_TREE_MODEL( img->thumbnail_model );
871 list = gtk_icon_view_get_selected_items(
872 GTK_ICON_VIEW( img->thumbnail_iconview ) );
873 if( list )
874 gtk_icon_view_get_cursor( GTK_ICON_VIEW(img->thumbnail_iconview),
875 &path, NULL);
876 if( list )
877 {
878 /* Start preview from this slide */
879 if( path )
880 gtk_tree_model_get_iter( model, &iter, path );
881 g_list_foreach( list, (GFunc)gtk_tree_path_free, NULL );
882 g_list_free( list );
883 }
884 else
885 {
886 /* Start preview from the beginning */
887 if( ! gtk_tree_model_get_iter_first( model, &iter ) )
888 return;
889 }
890 img->cur_ss_iter = iter;
891
892 /* Replace button and menu images */
893 img_swap_toolbar_images( img, FALSE );
894
895 /* Load the first image in the pixbuf */
896 gtk_tree_model_get( model, &iter, 1, &entry, -1);
897
898 if( ! entry->o_filename )
899 {
900 img_scale_gradient( entry->gradient, entry->g_start_point,
901 entry->g_stop_point, entry->g_start_color,
902 entry->g_stop_color, img->video_size[0],
903 img->video_size[1], NULL, &img->image2 );
904 }
905 /* Respect quality settings */
906 else if( img->low_quality )
907 img_scale_image( entry->r_filename, img->video_ratio,
908 0, img->video_size[1], img->distort_images,
909 img->background_color, NULL, &img->image2 );
910 else
911 img_scale_image( entry->r_filename, img->video_ratio,
912 0, 0, img->distort_images,
913 img->background_color, NULL, &img->image2 );
914
915 /* Load first stop point */
916 img->point2 = (ImgStopPoint *)( entry->no_points ?
917 entry->points->data :
918 NULL );
919
920 img->work_slide = entry;
921 img->cur_point = NULL;
922
923 /* If we started our preview from beginning, create empty pixbuf and
924 * fill it with background color. Else load image that is before
925 * currently selected slide. */
926 if( path != NULL && gtk_tree_path_prev( path ) )
927 {
928 gtk_tree_model_get_iter( model, &prev, path );
929 gtk_tree_model_get( model, &prev, 1, &entry, -1 );
930
931 if( ! entry->o_filename )
932 {
933 img_scale_gradient( entry->gradient, entry->g_start_point,
934 entry->g_stop_point, entry->g_start_color,
935 entry->g_stop_color, img->video_size[0],
936 img->video_size[1], NULL, &img->image1 );
937 }
938 /* Respect quality settings */
939 else if( img->low_quality )
940 img_scale_image( entry->r_filename, img->video_ratio,
941 0, img->video_size[1], img->distort_images,
942 img->background_color, NULL, &img->image1 );
943 else
944 img_scale_image( entry->r_filename, img->video_ratio,
945 0, 0, img->distort_images,
946 img->background_color, NULL, &img->image1 );
947
948 /* Load last stop point */
949 img->point1 = (ImgStopPoint *)( entry->no_points ?
950 g_list_last( entry->points )->data :
951 NULL );
952 }
953 else
954 {
955 cairo_t *cr;
956
957 img->image1 = cairo_image_surface_create( CAIRO_FORMAT_RGB24,
958 img->video_size[0],
959 img->video_size[1] );
960 cr = cairo_create( img->image1 );
961 cairo_set_source_rgb( cr, img->background_color[0],
962 img->background_color[1],
963 img->background_color[2] );
964 cairo_paint( cr );
965 cairo_destroy( cr );
966 }
967 if( path )
968 gtk_tree_path_free( path );
969
970 /* Add transition timeout function */
971 img->preview_is_running = TRUE;
972 img->total_nr_frames = img->total_secs * img->preview_fps;
973 img->displayed_frame = 0;
974 img->next_slide_off = 0;
975 img_calc_next_slide_time_offset( img, img->preview_fps );
976
977 /* Create surfaces to be passed to transition renderer */
978 img->image_from = cairo_image_surface_create( CAIRO_FORMAT_RGB24,
979 img->video_size[0],
980 img->video_size[1] );
981 img->image_to = cairo_image_surface_create( CAIRO_FORMAT_RGB24,
982 img->video_size[0],
983 img->video_size[1] );
984 img->exported_image = cairo_image_surface_create( CAIRO_FORMAT_RGB24,
985 img->video_size[0],
986 img->video_size[1] );
987
988 img->source_id = g_timeout_add( 1000 / img->preview_fps,
989 (GSourceFunc)img_transition_timeout,
990 img );
991 }
992 return;
993 }
994
img_goto_first_slide(GtkWidget * button,img_window_struct * img)995 void img_goto_first_slide(GtkWidget *button, img_window_struct *img)
996 {
997 GtkTreeIter iter;
998 GtkTreePath *path;
999 GtkTreeModel *model;
1000 gchar *slide = NULL;
1001
1002 model = GTK_TREE_MODEL( img->thumbnail_model );
1003 if ( ! gtk_tree_model_get_iter_first(model,&iter))
1004 return;
1005
1006 slide = g_strdup_printf("%d", 1);
1007 gtk_entry_set_text(GTK_ENTRY(img->slide_number_entry), slide);
1008 g_free(slide);
1009 gtk_icon_view_unselect_all(GTK_ICON_VIEW (img->active_icon));
1010 path = gtk_tree_path_new_from_indices(0,-1);
1011 gtk_icon_view_set_cursor (GTK_ICON_VIEW (img->active_icon), path, NULL, FALSE);
1012 gtk_icon_view_select_path (GTK_ICON_VIEW (img->active_icon), path);
1013 gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (img->active_icon), path, FALSE, 0, 0);
1014 gtk_tree_path_free (path);
1015 }
1016
img_goto_prev_slide(GtkWidget * button,img_window_struct * img)1017 void img_goto_prev_slide(GtkWidget *button, img_window_struct *img)
1018 {
1019 GtkTreeModel *model;
1020 GtkTreePath *path;
1021 GList *icons_selected = NULL;
1022 gchar *slide = NULL;
1023 gint slide_nr;
1024
1025 icons_selected = gtk_icon_view_get_selected_items(GTK_ICON_VIEW(img->active_icon) );
1026 if( ! icons_selected )
1027 return;
1028
1029 model = GTK_TREE_MODEL( img->thumbnail_model );
1030 slide_nr = gtk_tree_path_get_indices(icons_selected->data)[0];
1031
1032 if (slide_nr == 0)
1033 return;
1034
1035 slide = g_strdup_printf("%d", slide_nr);
1036 gtk_entry_set_text(GTK_ENTRY(img->slide_number_entry), slide);
1037 g_free(slide);
1038 gtk_icon_view_unselect_all(GTK_ICON_VIEW (img->active_icon));
1039 path = gtk_tree_path_new_from_indices(--slide_nr,-1);
1040
1041 gtk_icon_view_set_cursor (GTK_ICON_VIEW (img->active_icon), path, NULL, FALSE);
1042 gtk_icon_view_select_path (GTK_ICON_VIEW (img->active_icon), path);
1043 gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (img->active_icon), path, FALSE, 0, 0);
1044 gtk_tree_path_free (path);
1045
1046 g_list_foreach (icons_selected, (GFunc) gtk_tree_path_free, NULL);
1047 g_list_free (icons_selected);
1048 }
1049
img_goto_next_slide(GtkWidget * button,img_window_struct * img)1050 void img_goto_next_slide(GtkWidget *button, img_window_struct *img)
1051 {
1052 GtkTreeModel *model;
1053 GtkTreePath *path;
1054 GList *icons_selected = NULL;
1055 gchar *slide = NULL;
1056 gint slide_nr;
1057
1058 icons_selected = gtk_icon_view_get_selected_items(GTK_ICON_VIEW(img->active_icon) );
1059 if( ! icons_selected )
1060 return;
1061
1062 /* Now get previous iter :) */
1063 model = GTK_TREE_MODEL( img->thumbnail_model );
1064 slide_nr = gtk_tree_path_get_indices(icons_selected->data)[0];
1065
1066 if (slide_nr == (img->slides_nr-1) )
1067 return;
1068
1069 gtk_icon_view_unselect_all(GTK_ICON_VIEW (img->active_icon));
1070 path = gtk_tree_path_new_from_indices(++slide_nr, -1);
1071
1072 slide = g_strdup_printf("%d", slide_nr + 1);
1073 gtk_entry_set_text(GTK_ENTRY(img->slide_number_entry), slide);
1074 g_free(slide);
1075 gtk_icon_view_set_cursor (GTK_ICON_VIEW (img->active_icon), path, NULL, FALSE);
1076 gtk_icon_view_select_path (GTK_ICON_VIEW (img->active_icon), path);
1077 gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (img->active_icon), path, FALSE, 0, 0);
1078 gtk_tree_path_free (path);
1079
1080 g_list_foreach (icons_selected, (GFunc) gtk_tree_path_free, NULL);
1081 g_list_free (icons_selected);
1082 }
1083
1084
img_goto_last_slide(GtkWidget * button,img_window_struct * img)1085 void img_goto_last_slide(GtkWidget *button, img_window_struct *img)
1086 {
1087 GtkTreeIter iter;
1088 GtkTreePath *path;
1089 GtkTreeModel *model;
1090 gchar *slide = NULL;
1091
1092 model = GTK_TREE_MODEL( img->thumbnail_model );
1093 if ( ! gtk_tree_model_get_iter_first(model,&iter))
1094 return;
1095
1096 slide = g_strdup_printf("%d", img->slides_nr);
1097 gtk_entry_set_text(GTK_ENTRY(img->slide_number_entry), slide);
1098 g_free(slide);
1099 gtk_icon_view_unselect_all(GTK_ICON_VIEW (img->active_icon));
1100 path = gtk_tree_path_new_from_indices(img->slides_nr - 1, -1);
1101 gtk_icon_view_set_cursor (GTK_ICON_VIEW (img->active_icon), path, NULL, FALSE);
1102 gtk_icon_view_select_path (GTK_ICON_VIEW (img->active_icon), path);
1103 gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (img->active_icon), path, FALSE, 0, 0);
1104 gtk_tree_path_free (path);
1105 }
1106
img_on_drag_data_received(GtkWidget * widget,GdkDragContext * context,int x,int y,GtkSelectionData * data,unsigned int info,unsigned int time,img_window_struct * img)1107 void img_on_drag_data_received (GtkWidget *widget,GdkDragContext *context,int x,int y,GtkSelectionData *data,unsigned int info,unsigned int time, img_window_struct *img)
1108 {
1109 gchar **pictures = NULL;
1110 gchar *filename;
1111 GtkWidget *dialog;
1112 GdkPixbuf *thumb;
1113 GtkTreeIter iter;
1114 gint len = 0, slides_cnt = 0, actual_slides;
1115 slide_struct *slide_info;
1116
1117 pictures = gtk_selection_data_get_uris(data);
1118 if (pictures == NULL)
1119 {
1120 dialog = gtk_message_dialog_new(GTK_WINDOW(img->imagination_window),GTK_DIALOG_MODAL,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,_("Sorry, I could not perform the operation!"));
1121 gtk_window_set_title(GTK_WINDOW(dialog),"Imagination");
1122 gtk_dialog_run (GTK_DIALOG (dialog));
1123 gtk_widget_destroy (GTK_WIDGET (dialog));
1124 gtk_drag_finish(context,FALSE,FALSE,time);
1125 return;
1126 }
1127 actual_slides = img->slides_nr;
1128 gtk_drag_finish (context,TRUE,FALSE,time);
1129 while(pictures[len])
1130 {
1131 filename = g_filename_from_uri (pictures[len],NULL,NULL);
1132 if( img_scale_image( filename, img->video_ratio, 88, 0,
1133 img->distort_images, img->background_color,
1134 &thumb, NULL ) )
1135 {
1136 slide_info = img_create_new_slide();
1137 if (slide_info)
1138 {
1139 img_set_slide_file_info( slide_info, filename );
1140 gtk_list_store_append (img->thumbnail_model,&iter);
1141 gtk_list_store_set (img->thumbnail_model, &iter, 0, thumb, 1, slide_info, -1);
1142 g_object_unref (thumb);
1143 slides_cnt++;
1144 }
1145 }
1146 g_free(filename);
1147 len++;
1148 }
1149 if (slides_cnt > 0)
1150 {
1151 img->slides_nr += slides_cnt;
1152 img->project_is_modified = TRUE;
1153 img_set_total_slideshow_duration(img);
1154 img_set_statusbar_message(img, 0);
1155 }
1156 g_strfreev (pictures);
1157
1158 /* Select the first slide */
1159 if (actual_slides == 0)
1160 img_goto_first_slide(NULL, img);
1161
1162 /* Select the first loaded slide if a previous set of slides was loaded */
1163 else
1164 img_select_nth_slide(img, actual_slides);
1165 }
1166
1167 /*
1168 * img_on_expose_event:
1169 * @widget: preview GtkDrawingArea
1170 * @event: expose event info
1171 * @img: global img_window_struct structure
1172 *
1173 * This function is responsible for all of the drawing on preview area, thus it
1174 * should handle "edit mode" (when user is constructing slide show), "preview
1175 * mode" (when user is previewing his work) and "export mode" (when export is in
1176 * progress).
1177 *
1178 * This might be seen as an overkill for single function, but since all of the
1179 * actual rendering is done by helper functions, this function just merely
1180 * paints the results on screen.
1181 *
1182 * Return value: This function returns TRUE if the area has been painted, FALSE
1183 * otherwise (this way the default expose function is only called when no slide
1184 * is selected).
1185 */
1186 gboolean
img_on_expose_event(GtkWidget * widget,GdkEventExpose * event,img_window_struct * img)1187 img_on_expose_event( GtkWidget *widget,
1188 GdkEventExpose *event,
1189 img_window_struct *img )
1190 {
1191 cairo_t *cr;
1192
1193 /* If we're previewing or exporting, only paint frame that is being
1194 * currently produced. */
1195 if( img->preview_is_running || img->export_is_running > 2 )
1196 {
1197 gdouble factor;
1198
1199 cr = gdk_cairo_create( widget->window );
1200
1201 /* Do the drawing */
1202 factor = (gdouble)img->image_area->allocation.width /
1203 img->video_size[0];
1204 cairo_scale( cr, factor, factor );
1205 cairo_set_source_surface( cr, img->exported_image, 0, 0 );
1206 cairo_paint( cr );
1207
1208 cairo_destroy( cr );
1209 }
1210 else
1211 {
1212 if( ! img->current_image )
1213 /* Use default handler */
1214 return( FALSE );
1215
1216 cr = gdk_cairo_create( widget->window );
1217
1218 /* Do the drawing */
1219 img_draw_image_on_surface( cr, img->image_area->allocation.width,
1220 img->current_image, &img->current_point, img );
1221
1222 /* Render subtitle if present */
1223 if( img->current_slide->subtitle )
1224 img_render_subtitle( cr,
1225 img->video_size[0],
1226 img->video_size[1],
1227 img->image_area_zoom,
1228 img->current_slide->position,
1229 img->current_slide->placing,
1230 img->current_point.zoom,
1231 img->current_point.offx,
1232 img->current_point.offy,
1233 img->current_slide->subtitle,
1234 img->current_slide->font_desc,
1235 img->current_slide->font_color,
1236 img->current_slide->font_bgcolor,
1237 img->current_slide->anim,
1238 1.0 );
1239
1240 cairo_destroy( cr );
1241 }
1242
1243 return( TRUE );
1244 }
1245
1246 /*
1247 * img_draw_image_on_surface:
1248 * @cr: cairo context
1249 * @width: width of the surface that @cr draws on
1250 * @surface: cairo surface to be drawn on @cr
1251 * @point: stop point holding zoom and offsets
1252 * @img: global img_window_struct
1253 *
1254 * This function takes care of scaling and moving of @surface to fit properly on
1255 * cairo context passed in.
1256 */
1257 void
img_draw_image_on_surface(cairo_t * cr,gint width,cairo_surface_t * surface,ImgStopPoint * point,img_window_struct * img)1258 img_draw_image_on_surface( cairo_t *cr,
1259 gint width,
1260 cairo_surface_t *surface,
1261 ImgStopPoint *point,
1262 img_window_struct *img )
1263 {
1264 gdouble offxr, offyr; /* Relative offsets */
1265 gdouble factor_c; /* Scaling factor for cairo context */
1266 gdouble factor_o; /* Scalng factor for offset mods */
1267 gint cw; /* Width of the surface */
1268
1269 cw = cairo_image_surface_get_width( surface );
1270 factor_c = (gdouble)width / cw * point->zoom;
1271 factor_o = (gdouble)img->video_size[0] / cw * point->zoom;
1272
1273 offxr = point->offx / factor_o;
1274 offyr = point->offy / factor_o;
1275
1276 /* Make sure that matrix modifications are only visible from this function
1277 * and they don't interfere with text drawing. */
1278 cairo_save( cr );
1279 cairo_scale( cr, factor_c, factor_c );
1280 cairo_set_source_surface( cr, surface, offxr, offyr );
1281 cairo_paint( cr );
1282 cairo_restore( cr );
1283 }
1284
img_transition_timeout(img_window_struct * img)1285 static gboolean img_transition_timeout(img_window_struct *img)
1286 {
1287 /* If we output all transition slides (or if there is no slides to output in
1288 * transition part), connect still preview phase. */
1289 if( img->slide_cur_frame == img->slide_trans_frames )
1290 {
1291 img->source_id = g_timeout_add( 1000 / img->preview_fps,
1292 (GSourceFunc)img_still_timeout, img );
1293
1294 return FALSE;
1295 }
1296
1297 /* Render single frame */
1298 img_render_transition_frame( img );
1299
1300 /* Schedule our image redraw */
1301 gtk_widget_queue_draw( img->image_area );
1302
1303 /* Increment counters */
1304 img->slide_cur_frame++;
1305 img->displayed_frame++;
1306
1307 return TRUE;
1308 }
1309
img_still_timeout(img_window_struct * img)1310 static gboolean img_still_timeout(img_window_struct *img)
1311 {
1312 /* If there is next slide, connect transition preview, else finish
1313 * preview. */
1314 if( img->slide_cur_frame == img->slide_nr_frames )
1315 {
1316 if( img_prepare_pixbufs( img, TRUE ) )
1317 {
1318 img_calc_next_slide_time_offset( img, img->preview_fps );
1319 img->source_id = g_timeout_add( 1000 / img->preview_fps,
1320 (GSourceFunc)img_transition_timeout,
1321 img );
1322 }
1323 else
1324 {
1325 /* Clean resources used in preview and prepare application for
1326 * next preview. */
1327 img_clean_after_preview( img );
1328 }
1329
1330 /* Indicate that we must start fresh with new slide */
1331 img->cur_point = NULL;
1332
1333 return FALSE;
1334 }
1335
1336 /* Render frame */
1337 img_render_still_frame( img, img->preview_fps );
1338
1339 /* Increment counters */
1340 img->still_counter++;
1341 img->slide_cur_frame++;
1342 img->displayed_frame++;
1343
1344 /* Redraw */
1345 gtk_widget_queue_draw( img->image_area );
1346
1347 return( TRUE );
1348 }
1349
img_swap_toolbar_images(img_window_struct * img,gboolean flag)1350 static void img_swap_toolbar_images( img_window_struct *img,gboolean flag )
1351 {
1352 GtkWidget *tmp_image;
1353
1354 if( flag )
1355 {
1356 tmp_image = gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY,GTK_ICON_SIZE_MENU);
1357 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (img->preview_menu),tmp_image);
1358
1359 tmp_image = gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY,GTK_ICON_SIZE_LARGE_TOOLBAR);
1360 gtk_widget_show(tmp_image);
1361 gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(img->preview_button), tmp_image);
1362 gtk_widget_set_tooltip_text(img->preview_button,_("Starts the preview"));
1363 }
1364 else
1365 {
1366 tmp_image = gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP,GTK_ICON_SIZE_MENU);
1367 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (img->preview_menu),tmp_image);
1368
1369 tmp_image = gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP,GTK_ICON_SIZE_LARGE_TOOLBAR);
1370 gtk_widget_show(tmp_image);
1371 gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(img->preview_button), tmp_image);
1372 gtk_widget_set_tooltip_text(img->preview_button,_("Stops the preview"));
1373 }
1374 }
1375
img_clean_after_preview(img_window_struct * img)1376 static void img_clean_after_preview(img_window_struct *img)
1377 {
1378 /* Switch to right mode */
1379 if( img->auto_switch )
1380 {
1381 img_switch_mode( img, 1 );
1382 img->auto_switch = FALSE;
1383 }
1384
1385 /* Swap toolbar and menu icons */
1386 img_swap_toolbar_images( img, TRUE );
1387
1388 /* Indicate that preview is not running */
1389 img->preview_is_running = FALSE;
1390
1391 /* Destroy images that were used */
1392 cairo_surface_destroy( img->image1 );
1393 cairo_surface_destroy( img->image2 );
1394 cairo_surface_destroy( img->image_from );
1395 cairo_surface_destroy( img->image_to );
1396 cairo_surface_destroy( img->exported_image );
1397
1398 gtk_widget_queue_draw( img->image_area );
1399
1400 return;
1401 }
1402
img_choose_slideshow_filename(GtkWidget * widget,img_window_struct * img)1403 void img_choose_slideshow_filename(GtkWidget *widget, img_window_struct *img)
1404 {
1405 GtkWidget *fc;
1406 GtkFileChooserAction action = 0;
1407 gint response;
1408 gchar *filename = NULL;
1409 GtkFileFilter *project_filter, *all_files_filter;
1410
1411 /* Determine the mode of the chooser. */
1412 if (widget == img->open_menu || widget == img->open_button || widget == img->import_project_menu)
1413 action = GTK_FILE_CHOOSER_ACTION_OPEN;
1414 else if (widget == img->save_as_menu || widget == img->save_menu || widget == img->save_button)
1415 action = GTK_FILE_CHOOSER_ACTION_SAVE;
1416
1417 /* close old slideshow if we import */
1418 if (widget == img->open_menu || widget == img->open_button)
1419 {
1420 if (img->project_is_modified)
1421 if (GTK_RESPONSE_OK != img_ask_user_confirmation(img, _("You didn't save your slideshow yet. Are you sure you want to close it?")))
1422 return;
1423 img_close_slideshow(widget, img);
1424 }
1425
1426 /* If user wants to save empty slideshow, simply abort */
1427 if( img->slides_nr == 0 && action == GTK_FILE_CHOOSER_ACTION_SAVE )
1428 return;
1429
1430 if (img->project_filename == NULL || widget == img->save_as_menu || action == GTK_FILE_CHOOSER_ACTION_OPEN)
1431 {
1432 fc = gtk_file_chooser_dialog_new (action == GTK_FILE_CHOOSER_ACTION_OPEN ? _("Load an Imagination slideshow project") :
1433 _("Save an Imagination slideshow project"),
1434 GTK_WINDOW (img->imagination_window),
1435 action,
1436 GTK_STOCK_CANCEL,
1437 GTK_RESPONSE_CANCEL,
1438 action == GTK_FILE_CHOOSER_ACTION_OPEN ? GTK_STOCK_OPEN : GTK_STOCK_SAVE,
1439 GTK_RESPONSE_ACCEPT,NULL);
1440
1441 /* Filter .img files */
1442 project_filter = gtk_file_filter_new ();
1443 gtk_file_filter_set_name(project_filter, _("Imagination projects"));
1444 gtk_file_filter_add_pattern(project_filter, "*.img");
1445 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fc), project_filter);
1446
1447 /* All files filter */
1448 all_files_filter = gtk_file_filter_new ();
1449 gtk_file_filter_set_name(all_files_filter, _("All files"));
1450 gtk_file_filter_add_pattern(all_files_filter, "*");
1451 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fc), all_files_filter);
1452
1453 if (widget == img->save_as_menu || (widget == img->save_menu && img->project_filename == NULL))
1454 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER (fc), "unknown.img");
1455
1456 if (img->project_current_dir)
1457 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fc),img->project_current_dir);
1458
1459
1460 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER (fc),TRUE);
1461 response = gtk_dialog_run (GTK_DIALOG (fc));
1462 if (response == GTK_RESPONSE_ACCEPT)
1463 {
1464 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1465 if( ! filename )
1466 {
1467 gtk_widget_destroy(fc);
1468 return;
1469 }
1470 if (img->project_current_dir)
1471 g_free(img->project_current_dir);
1472 img->project_current_dir = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(fc));
1473
1474 }
1475 else if (response == GTK_RESPONSE_CANCEL || GTK_RESPONSE_DELETE_EVENT)
1476 {
1477 gtk_widget_destroy(fc);
1478 return;
1479 }
1480 gtk_widget_destroy(fc);
1481 }
1482
1483 if( ! filename )
1484 filename = g_strdup( img->project_filename );
1485
1486 if (action == GTK_FILE_CHOOSER_ACTION_OPEN)
1487 img_load_slideshow( img, filename );
1488 else
1489 img_save_slideshow( img, filename );
1490
1491 g_free( filename );
1492 }
1493
img_close_slideshow(GtkWidget * widget,img_window_struct * img)1494 void img_close_slideshow(GtkWidget *widget, img_window_struct *img)
1495 {
1496 /* When called from close_menu, ask for confirmation */
1497 if (img->project_is_modified && widget == img->close_menu)
1498 {
1499 if (GTK_RESPONSE_OK != img_ask_user_confirmation(img, _("You didn't save your slideshow yet. Are you sure you want to close it?")))
1500 return;
1501 }
1502 img->project_is_modified = FALSE;
1503 img_free_allocated_memory(img);
1504 img_set_window_title(img,NULL);
1505 img_set_statusbar_message(img,0);
1506 if( img->current_image )
1507 cairo_surface_destroy( img->current_image );
1508 img->current_image = NULL;
1509 gtk_widget_queue_draw( img->image_area );
1510 gtk_label_set_text(GTK_LABEL (img->total_time_data),"");
1511
1512 /* Reset slideshow properties */
1513 img->distort_images = TRUE;
1514 img->background_color[0] = 0;
1515 img->background_color[1] = 0;
1516 img->background_color[2] = 0;
1517 img->final_transition.speed = NORMAL;
1518 img->final_transition.render = NULL;
1519
1520 /* Disable the video tab */
1521 img_disable_videotab (img);
1522
1523 gtk_entry_set_text(GTK_ENTRY(img->slide_number_entry), "");
1524 }
1525
img_move_audio_up(GtkButton * button,img_window_struct * img)1526 void img_move_audio_up( GtkButton *button, img_window_struct *img )
1527 {
1528 GtkTreeSelection *sel;
1529 GtkTreeModel *model;
1530 GtkTreeIter iter1, iter2;
1531
1532 /* We need path, since there is no gtk_tree_model_iter_prev function!! */
1533 GtkTreePath *path;
1534
1535 /* First we need to get selected iter. This function won't work if
1536 * selection's mode is set to GTK_SELECTION_MULTIPLE!! */
1537 sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( img->music_file_treeview ) );
1538 if( ! gtk_tree_selection_get_selected( sel, &model, &iter1 ) )
1539 return;
1540
1541 /* Now get previous iter and swap two items if previous iter exists. */
1542 path = gtk_tree_model_get_path( model, &iter1 );
1543 if( gtk_tree_path_prev( path ) )
1544 {
1545 gtk_tree_model_get_iter( model, &iter2, path );
1546 gtk_list_store_swap( GTK_LIST_STORE( model ), &iter1, &iter2 );
1547 }
1548 gtk_tree_path_free( path );
1549 }
1550
img_move_audio_down(GtkButton * button,img_window_struct * img)1551 void img_move_audio_down( GtkButton *button, img_window_struct *img )
1552 {
1553 GtkTreeSelection *sel;
1554 GtkTreeModel *model;
1555 GtkTreeIter iter1, iter2;
1556
1557 /* First we need to get selected iter. This function won't work if
1558 * selection's mode is set to GTK_SELECTION_MULTIPLE!! */
1559 sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( img->music_file_treeview ) );
1560 if( ! gtk_tree_selection_get_selected( sel, &model, &iter1 ) )
1561 return;
1562
1563 /* Get next iter and swap rows if iter exists. */
1564 iter2 = iter1;
1565 if( gtk_tree_model_iter_next( model, &iter2 ) )
1566 gtk_list_store_swap( GTK_LIST_STORE( model ), &iter1, &iter2 );
1567 }
1568
1569 /*
1570 * img_ken_burns_zoom_changed:
1571 * @range: GtkRange that will provide proper value for us
1572 * @img: global img_window_strct structure
1573 *
1574 * This function modifies current zoom value and queues redraw of preview area.
1575 *
1576 * To keep image center in focus, we also do some calculation on offsets.
1577 *
1578 * IMPORTANT: This function is part of the Ken Burns effect and doesn't change
1579 * preview area size!! If you're looking for function that does that,
1580 * img_image_area_change_zoom is the thing to look at.
1581 */
1582 void
img_ken_burns_zoom_changed(GtkRange * range,img_window_struct * img)1583 img_ken_burns_zoom_changed( GtkRange *range,
1584 img_window_struct *img )
1585 {
1586 /* Store old zoom for calcutaions */
1587 gdouble old_zoom = img->current_point.zoom;
1588
1589 img->current_point.zoom = gtk_range_get_value( range );
1590
1591 /* If zoom is 1, reset parameters to avoid drift. */
1592 if( img->current_point.zoom < 1.00005 )
1593 {
1594 img->maxoffx = 0;
1595 img->maxoffy = 0;
1596 img->current_point.offx = 0;
1597 img->current_point.offy = 0;
1598 }
1599 else
1600 {
1601 gdouble fracx, fracy;
1602 gint tmpoffx, tmpoffy;
1603 gdouble aw = img->video_size[0];
1604 gdouble ah = img->video_size[1];
1605 gdouble aw2 = aw / 2;
1606 gdouble ah2 = ah / 2;
1607
1608 fracx = (gdouble)( aw2 - img->current_point.offx ) / ( aw2 * old_zoom );
1609 fracy = (gdouble)( ah2 - img->current_point.offy ) / ( ah2 * old_zoom );
1610 img->maxoffx = aw * ( 1 - img->current_point.zoom );
1611 img->maxoffy = ah * ( 1 - img->current_point.zoom );
1612 tmpoffx = aw2 * ( 1 - fracx * img->current_point.zoom );
1613 tmpoffy = ah2 * ( 1 - fracy * img->current_point.zoom );
1614
1615 img->current_point.offx = CLAMP( tmpoffx, img->maxoffx, 0 );
1616 img->current_point.offy = CLAMP( tmpoffy, img->maxoffy, 0 );
1617 }
1618
1619 gtk_widget_queue_draw( img->image_area );
1620 }
1621
1622 /*
1623 * img_image_area_button_press:
1624 * @widget: image area
1625 * @event: event description
1626 * @img: global img_window_struct structure
1627 *
1628 * This function stores initial coordinates of button press that we'll be
1629 * needing for drag emulation.
1630 *
1631 * Return value: TRUE, indicating that we handled this event.
1632 */
1633 gboolean
img_image_area_button_press(GtkWidget * widget,GdkEventButton * event,img_window_struct * img)1634 img_image_area_button_press( GtkWidget *widget,
1635 GdkEventButton *event,
1636 img_window_struct *img )
1637 {
1638 if( event->button != 1 )
1639 return( FALSE );
1640
1641 img->x = event->x;
1642 img->y = event->y;
1643 img->bak_offx = img->current_point.offx;
1644 img->bak_offy = img->current_point.offy;
1645
1646 return( TRUE );
1647 }
1648
1649 /*
1650 * img_image_area_motion:
1651 * @widget: image area
1652 * @event: event description
1653 * @img: global img_window_struct structure
1654 *
1655 * This function calculates offsets from stored button press coordinates and
1656 * queue redraw of the preview area.
1657 *
1658 * Return value: TRUE if moune button 1 has been pressed during drag, else
1659 * FALSE.
1660 */
1661 gboolean
img_image_area_motion(GtkWidget * widget,GdkEventMotion * event,img_window_struct * img)1662 img_image_area_motion( GtkWidget *widget,
1663 GdkEventMotion *event,
1664 img_window_struct *img )
1665 {
1666 gdouble deltax,
1667 deltay;
1668
1669 deltax = ( event->x - img->x ) / img->image_area_zoom;
1670 deltay = ( event->y - img->y ) / img->image_area_zoom;
1671
1672 img->current_point.offx = CLAMP( deltax + img->bak_offx, img->maxoffx, 0 );
1673 img->current_point.offy = CLAMP( deltay + img->bak_offy, img->maxoffy, 0 );
1674
1675 gtk_widget_queue_draw( img->image_area );
1676
1677 return( TRUE );
1678 }
1679
1680 /* Zoom callback functions */
1681 void
img_zoom_in(GtkWidget * item,img_window_struct * img)1682 img_zoom_in( GtkWidget *item,
1683 img_window_struct *img )
1684 {
1685 if( img->mode == 0 )
1686 img_image_area_change_zoom( 0.1, FALSE, img );
1687 else
1688 img_overview_change_zoom( 0.1, FALSE, img );
1689 }
1690
1691 void
img_zoom_out(GtkWidget * item,img_window_struct * img)1692 img_zoom_out( GtkWidget *item,
1693 img_window_struct *img )
1694 {
1695 if( img->mode == 0 )
1696 img_image_area_change_zoom( - 0.1, FALSE, img );
1697 else
1698 img_overview_change_zoom( - 0.1, FALSE, img );
1699 }
1700
1701 void
img_zoom_reset(GtkWidget * item,img_window_struct * img)1702 img_zoom_reset( GtkWidget *item,
1703 img_window_struct *img )
1704 {
1705 if( img->mode == 0 )
1706 img_image_area_change_zoom( 0, TRUE, img );
1707 else
1708 img_overview_change_zoom( 0, TRUE, img );
1709 }
1710
1711 void
img_zoom_fit(GtkWidget * item,img_window_struct * img)1712 img_zoom_fit( GtkWidget *item,
1713 img_window_struct *img )
1714 {
1715 gdouble step, level1, level2;
1716
1717 if( img->mode == 0 )
1718 {
1719 /* we want to fit the frame into prev_root. Frame = video + 4 px */
1720 level1 = (float)img->prev_root->allocation.width / (img->video_size[0] + 4);
1721 level2 = (float)img->prev_root->allocation.height / (img->video_size[1] + 4);
1722 if (level1 < level2)
1723 /* step is relative to zoom level 1 */
1724 step = level1 - 1;
1725 else
1726 step = level2 - 1;
1727
1728 img_image_area_change_zoom( 0, TRUE, img );
1729 img_image_area_change_zoom( step, FALSE, img );
1730 }
1731 else
1732 img_overview_change_zoom( 0, TRUE, img );
1733 }
1734
1735 /*
1736 * img_image_area_change_zoom:
1737 * @step: amount of zoom to be changed
1738 * @reset: do we want to reset zoom level
1739 * @img: global img_widget_struct structure
1740 *
1741 * This function will increase/decrease/reset zoom level. If @step is less than
1742 * zero, preview area will zoom out, if @step is bigger that zero, image area
1743 * will zoom in.
1744 *
1745 * If @reset is TRUE, @step value is ignored and zoom reset to 1.
1746 *
1747 * Zoom level of image area is in interval [0.1, 5], but this can be easily
1748 * changed by adjusting bounds array values. If the zoom would be set outside of
1749 * this interval, it is clamped in between those two values.
1750 */
1751 static void
img_image_area_change_zoom(gdouble step,gboolean reset,img_window_struct * img)1752 img_image_area_change_zoom( gdouble step,
1753 gboolean reset,
1754 img_window_struct *img )
1755 {
1756 static gdouble bounds[] = { 0.1, 5.0 };
1757
1758 if( reset )
1759 img->image_area_zoom = 1;
1760 else
1761 img->image_area_zoom = CLAMP( img->image_area_zoom + step,
1762 bounds[0], bounds[1] );
1763
1764 /* Apply change */
1765 gtk_widget_set_size_request( img->image_area,
1766 img->video_size[0] * img->image_area_zoom,
1767 img->video_size[1] * img->image_area_zoom );
1768 }
1769
1770 static void
img_overview_change_zoom(gdouble step,gboolean reset,img_window_struct * img)1771 img_overview_change_zoom( gdouble step,
1772 gboolean reset,
1773 img_window_struct *img )
1774 {
1775 static gdouble bounds[] = { 0.1, 3.0 };
1776 GtkTreeModel *model;
1777
1778 if( reset )
1779 img->overview_zoom = 1;
1780 else
1781 img->overview_zoom = CLAMP( img->overview_zoom + step,
1782 bounds[0], bounds[1] );
1783
1784 /* Apply change */
1785 g_object_get( G_OBJECT( img->over_icon ), "model", &model, NULL );
1786 g_object_set( G_OBJECT( img->over_icon ), "model", NULL, NULL );
1787 g_object_set( img->over_cell, "zoom", img->overview_zoom, NULL );
1788 g_object_set( G_OBJECT( img->over_icon ), "model", model, NULL );
1789 g_object_unref( G_OBJECT( model ) );
1790 }
1791
1792 void
img_quality_toggled(GtkCheckMenuItem * item,img_window_struct * img)1793 img_quality_toggled( GtkCheckMenuItem *item,
1794 img_window_struct *img )
1795 {
1796 img->low_quality = gtk_check_menu_item_get_active( item );
1797 }
1798
1799 void
img_add_stop_point(GtkButton * button,img_window_struct * img)1800 img_add_stop_point( GtkButton *button,
1801 img_window_struct *img )
1802 {
1803 ImgStopPoint *point;
1804 GList *tmp;
1805
1806 if (img->current_slide == NULL)
1807 return;
1808
1809 /* Create new point */
1810 point = g_slice_new( ImgStopPoint );
1811 *point = img->current_point;
1812 point->time = gtk_spin_button_get_value_as_int(
1813 GTK_SPIN_BUTTON( img->ken_duration ) );
1814
1815 /* Append it to the list */
1816 tmp = img->current_slide->points;
1817 tmp = g_list_append( tmp, point );
1818 img->current_slide->points = tmp;
1819 img->current_slide->cur_point = img->current_slide->no_points;
1820 img->current_slide->no_points++;
1821
1822 /* Update display */
1823 img_update_stop_display( img, FALSE );
1824 img_ken_burns_update_sensitivity( img, TRUE,
1825 img->current_slide->no_points );
1826
1827 /* Sync timings */
1828 img_sync_timings( img->current_slide, img );
1829 }
1830
1831 void
img_update_stop_point(GtkButton * button,img_window_struct * img)1832 img_update_stop_point( GtkButton *button,
1833 img_window_struct *img )
1834 {
1835 ImgStopPoint *point;
1836
1837 if( img->current_slide == NULL )
1838 return;
1839
1840 /* Get selected point */
1841 point = g_list_nth_data( img->current_slide->points,
1842 img->current_slide->cur_point );
1843
1844 /* Update data */
1845 *point = img->current_point;
1846 point->time = gtk_spin_button_get_value_as_int(
1847 GTK_SPIN_BUTTON( img->ken_duration ) );
1848
1849 /* Update display */
1850 img_update_stop_display( img, FALSE );
1851
1852 /* Sync timings */
1853 img_sync_timings( img->current_slide, img );
1854 }
1855
1856 void
img_delete_stop_point(GtkButton * button,img_window_struct * img)1857 img_delete_stop_point( GtkButton *button,
1858 img_window_struct *img )
1859 {
1860 GList *node;
1861
1862 if( img->current_slide == NULL )
1863 return;
1864
1865 /* Get selected node and free it */
1866 node = g_list_nth( img->current_slide->points,
1867 img->current_slide->cur_point );
1868 g_slice_free( ImgStopPoint, node->data );
1869 img->current_slide->points =
1870 g_list_delete_link( img->current_slide->points, node );
1871
1872 /* Update counters */
1873 img->current_slide->no_points--;
1874 img->current_slide->cur_point = MIN( img->current_slide->cur_point,
1875 img->current_slide->no_points - 1 );
1876
1877 /* Update display */
1878 img_update_stop_display( img, TRUE );
1879 img_ken_burns_update_sensitivity( img, TRUE,
1880 img->current_slide->no_points );
1881
1882 /* Sync timings */
1883 img_sync_timings( img->current_slide, img );
1884 }
1885
1886 void
img_update_stop_display(img_window_struct * img,gboolean update_pos)1887 img_update_stop_display( img_window_struct *img,
1888 gboolean update_pos )
1889 {
1890 gchar *string;
1891 gint full;
1892
1893 /* Disable/enable slide duration */
1894 gtk_widget_set_sensitive( img->duration,
1895 img->current_slide->no_points == 0 );
1896
1897 /* Set slide duration */
1898 full = img_calc_slide_duration_points( img->current_slide->points,
1899 img->current_slide->no_points );
1900 if( ! full )
1901 full = img->current_slide->duration;
1902 gtk_spin_button_set_value( GTK_SPIN_BUTTON( img->duration ), full );
1903
1904 /* Set point count */
1905 string = g_strdup_printf( "%d", img->current_slide->no_points );
1906 gtk_label_set_text( GTK_LABEL( img->total_stop_points_label), string );
1907 g_free( string );
1908
1909 /* If no point is set yet, use default values */
1910 if( img->current_slide->no_points )
1911 {
1912 ImgStopPoint *point;
1913
1914 /* Set current point */
1915 string = g_strdup_printf( "%d", img->current_slide->cur_point + 1 );
1916 gtk_entry_set_text( GTK_ENTRY( img->ken_entry ), string );
1917 g_free( string );
1918
1919 /* Set duration of this point */
1920 point = (ImgStopPoint *)g_list_nth_data( img->current_slide->points,
1921 img->current_slide->cur_point );
1922 gtk_spin_button_set_value( GTK_SPIN_BUTTON( img->ken_duration ),
1923 point->time );
1924
1925 /* Set zoom value */
1926 gtk_range_set_value( GTK_RANGE( img->ken_zoom ), point->zoom );
1927
1928 /* Do we need to refresh current stop point on screen */
1929 if( update_pos )
1930 img->current_point = *point;
1931 }
1932 else
1933 {
1934 ImgStopPoint point = { 1, 0, 0, 1.0 };
1935
1936 gtk_entry_set_text( GTK_ENTRY( img->ken_entry ), "" );
1937 gtk_spin_button_set_value( GTK_SPIN_BUTTON( img->ken_duration ), 1 );
1938 gtk_range_set_value( GTK_RANGE( img->ken_zoom ), 1.0 );
1939 if( update_pos )
1940 img->current_point = point;
1941 }
1942
1943 /* Force update on preview area */
1944 gtk_widget_queue_draw( img->image_area );
1945 }
1946
1947 void
img_update_subtitles_widgets(img_window_struct * img)1948 img_update_subtitles_widgets( img_window_struct *img )
1949 {
1950 gchar *string;
1951 GdkColor color;
1952 gdouble *f_colors;
1953
1954 /* Block all handlers */
1955 g_signal_handlers_block_by_func( img->slide_text_buffer,
1956 img_queue_subtitle_update, img );
1957 g_signal_handlers_block_by_func( img->sub_font,
1958 img_text_font_set, img );
1959 g_signal_handlers_block_by_func( img->sub_color,
1960 img_font_color_changed, img );
1961 g_signal_handlers_block_by_func( img->sub_bgcolor,
1962 img_font_bgcolor_changed, img );
1963 g_signal_handlers_block_by_func( img->sub_anim,
1964 img_text_anim_set, img );
1965 g_signal_handlers_block_by_func( img->sub_anim_duration,
1966 img_combo_box_anim_speed_changed, img );
1967 g_signal_handlers_block_by_func( img->sub_placing,
1968 img_placing_changed, img );
1969 g_signal_handlers_block_by_func( img->sub_pos, img_text_pos_changed, img );
1970
1971 /* Update text field */
1972 string = ( img->current_slide->subtitle ?
1973 img->current_slide->subtitle :
1974 "" );
1975 g_object_set( G_OBJECT( img->slide_text_buffer ), "text", string, NULL );
1976
1977 /* Update font button */
1978 string = pango_font_description_to_string(img->current_slide->font_desc);
1979 gtk_font_button_set_font_name(GTK_FONT_BUTTON(img->sub_font), string);
1980 g_free(string);
1981
1982 /* Update color button */
1983 f_colors = img->current_slide->font_color;
1984 color.red = (gint)( f_colors[0] * 0xffff );
1985 color.green = (gint)( f_colors[1] * 0xffff );
1986 color.blue = (gint)( f_colors[2] * 0xffff );
1987 gtk_color_button_set_color( GTK_COLOR_BUTTON( img->sub_color ), &color );
1988 gtk_color_button_set_alpha( GTK_COLOR_BUTTON( img->sub_color ),
1989 (gint)(f_colors[3] * 0xffff ) );
1990
1991 /* Update background color button */
1992 f_colors = img->current_slide->font_bgcolor;
1993 color.red = (gint)( f_colors[0] * 0xffff );
1994 color.green = (gint)( f_colors[1] * 0xffff );
1995 color.blue = (gint)( f_colors[2] * 0xffff );
1996 gtk_color_button_set_color( GTK_COLOR_BUTTON( img->sub_bgcolor ), &color );
1997 gtk_color_button_set_alpha( GTK_COLOR_BUTTON( img->sub_bgcolor ),
1998 (gint)(f_colors[3] * 0xffff ) );
1999
2000 /* Update animation */
2001 gtk_combo_box_set_active( GTK_COMBO_BOX( img->sub_anim ),
2002 img->current_slide->anim_id );
2003
2004 /* Update duration */
2005 gtk_spin_button_set_value( GTK_SPIN_BUTTON( img->sub_anim_duration ),
2006 img->current_slide->anim_duration );
2007
2008 /* Update placing */
2009 gtk_combo_box_set_active( GTK_COMBO_BOX( img->sub_placing ),
2010 img->current_slide->placing );
2011
2012 /* Update position */
2013 img_table_button_set_active_item( IMG_TABLE_BUTTON( img->sub_pos ),
2014 img->current_slide->position );
2015
2016 /* Unblock all handlers */
2017 g_signal_handlers_unblock_by_func( img->slide_text_buffer,
2018 img_queue_subtitle_update, img );
2019 g_signal_handlers_unblock_by_func( img->sub_font,
2020 img_text_font_set, img );
2021 g_signal_handlers_unblock_by_func( img->sub_color,
2022 img_font_color_changed, img );
2023 g_signal_handlers_unblock_by_func( img->sub_bgcolor,
2024 img_font_bgcolor_changed, img );
2025 g_signal_handlers_unblock_by_func( img->sub_anim,
2026 img_text_anim_set, img );
2027 g_signal_handlers_unblock_by_func( img->sub_anim_duration,
2028 img_combo_box_anim_speed_changed, img );
2029 g_signal_handlers_unblock_by_func( img->sub_placing,
2030 img_placing_changed, img );
2031 g_signal_handlers_unblock_by_func( img->sub_pos,
2032 img_text_pos_changed, img );
2033 }
2034
2035 void
img_goto_prev_point(GtkButton * button,img_window_struct * img)2036 img_goto_prev_point( GtkButton *button,
2037 img_window_struct *img )
2038 {
2039 if( img->current_slide && img->current_slide->no_points )
2040 {
2041 img->current_slide->cur_point =
2042 CLAMP( img->current_slide->cur_point - 1,
2043 0, img->current_slide->no_points - 1 );
2044
2045 img_update_stop_display( img, TRUE );
2046 }
2047 }
2048
2049 void
img_goto_next_point(GtkButton * button,img_window_struct * img)2050 img_goto_next_point( GtkButton *button,
2051 img_window_struct *img )
2052 {
2053 if( img->current_slide && img->current_slide->no_points )
2054 {
2055 img->current_slide->cur_point =
2056 CLAMP( img->current_slide->cur_point + 1,
2057 0, img->current_slide->no_points - 1 );
2058
2059 img_update_stop_display( img, TRUE );
2060 }
2061 }
2062
2063 void
img_goto_point(GtkEntry * entry,img_window_struct * img)2064 img_goto_point ( GtkEntry *entry,
2065 img_window_struct *img )
2066 {
2067 const gchar *string;
2068 gint number;
2069
2070 string = gtk_entry_get_text( entry );
2071 number = (gint)strtol( string, NULL, 10 );
2072
2073 if( img->current_slide && img->current_slide->no_points )
2074 {
2075 img->current_slide->cur_point =
2076 CLAMP( number - 1, 0, img->current_slide->no_points - 1 );
2077
2078 img_update_stop_display( img, TRUE );
2079 }
2080 }
2081
2082
2083 void
img_calc_current_ken_point(ImgStopPoint * res,ImgStopPoint * from,ImgStopPoint * to,gdouble progress,gint mode)2084 img_calc_current_ken_point( ImgStopPoint *res,
2085 ImgStopPoint *from,
2086 ImgStopPoint *to,
2087 gdouble progress,
2088 gint mode )
2089 {
2090 gdouble fracx, /* Factor for x offset */
2091 fracy, /* Factor for y offset */
2092 fracz; /* Factor for zoom */
2093
2094 switch( mode )
2095 {
2096 case( 0 ): /* Linear mode */
2097 fracx = progress;
2098 fracy = progress;
2099 fracz = progress;
2100 break;
2101
2102 case( 1 ): /* Acceleration mode */
2103 break;
2104
2105 case( 2 ): /* Deceleration mode */
2106 break;
2107 }
2108
2109 res->offx = from->offx * ( 1 - fracx ) + to->offx * fracx;
2110 res->offy = from->offy * ( 1 - fracy ) + to->offy * fracy;
2111 res->zoom = from->zoom * ( 1 - fracz ) + to->zoom * fracz;
2112 }
2113
img_clipboard_cut_copy_operation(img_window_struct * img,ImgClipboardMode mode)2114 void img_clipboard_cut_copy_operation(img_window_struct *img, ImgClipboardMode mode)
2115 {
2116 GtkClipboard *img_clipboard;
2117 GList *selected = NULL;
2118 GtkTargetEntry targets[] =
2119 {
2120 { "application/imagination-info-list", 0, 0 }
2121 };
2122
2123 selected =
2124 gtk_icon_view_get_selected_items(GTK_ICON_VIEW(img->active_icon));
2125 if (selected == NULL)
2126 return;
2127
2128 img_clipboard = gtk_clipboard_get (IMG_CLIPBOARD);
2129
2130 /* Let's delete the GList if the user selechooses Cut/Copy again instead of Paste */
2131 if (img->selected_paths)
2132 {
2133 g_list_foreach (img->selected_paths, (GFunc)gtk_tree_path_free, NULL);
2134 g_list_free (img->selected_paths);
2135 }
2136 img->selected_paths = selected;
2137 img->clipboard_mode = mode;
2138
2139 gtk_clipboard_set_with_data ( img_clipboard,
2140 targets, G_N_ELEMENTS (targets),
2141 (GtkClipboardGetFunc) img_clipboard_get,
2142 NULL, img);
2143 }
2144
img_clipboard_get(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,img_window_struct * img)2145 void img_clipboard_get (GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, img_window_struct *img)
2146 {
2147 if (selection_data->target != IMG_INFO_LIST)
2148 return;
2149
2150 gtk_selection_data_set (selection_data, selection_data->target, 8, (guchar *) img->selected_paths, sizeof(GList) * g_list_length(img->selected_paths) );
2151 }
2152
img_clipboard_clear(GtkClipboard * clipboard,img_window_struct * img)2153 void img_clipboard_clear (GtkClipboard *clipboard, img_window_struct *img)
2154 {
2155 img_message (img, FALSE, "I'm here\n");
2156 //gtk_clipboard_clear(clipboard);
2157 }
2158
2159 void
img_add_empty_slide(GtkMenuItem * item,img_window_struct * img)2160 img_add_empty_slide( GtkMenuItem *item,
2161 img_window_struct *img )
2162 {
2163 /* This structure retains values across invocations */
2164 static ImgEmptySlide slide = { { 0, 0, 0 }, /* Start color */
2165 { 1, 1, 1 }, /* Stop color */
2166 { 0, 0 }, /* Start point (l) */
2167 { -1, 0 }, /* Stop point (l) */
2168 { 0, 0 }, /* Start point (r) */
2169 { 0, 0 }, /* Stop point (r) */
2170 0, /* Drag */
2171 0, /* Gradient type */
2172 NULL, /* Color button */
2173 NULL, /* Preview area */
2174 { NULL, NULL, NULL } /* Radio buttons */
2175 };
2176
2177 GList *where_to_insert = NULL;
2178
2179 /* Widgets */
2180 GtkWidget *dialog,
2181 *vbox,
2182 *frame,
2183 *table,
2184 *radio1,
2185 *radio2,
2186 *radio3,
2187 *color1,
2188 *color2,
2189 *preview,
2190 *hbox;
2191 GdkColor color;
2192 gint i, w, h, pos;
2193
2194 dialog = gtk_dialog_new_with_buttons(
2195 _("Create empty slide"),
2196 GTK_WINDOW( img->imagination_window ),
2197 GTK_DIALOG_MODAL,
2198 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2199 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2200 NULL );
2201
2202 gtk_button_box_set_layout (GTK_BUTTON_BOX (GTK_DIALOG (dialog)->action_area), GTK_BUTTONBOX_SPREAD);
2203 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
2204 vbox = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
2205
2206 frame = gtk_frame_new( _("Empty slide options:") );
2207 gtk_box_pack_start( GTK_BOX( vbox ), frame, TRUE, TRUE, 5 );
2208 gtk_container_set_border_width( GTK_CONTAINER( frame ), 5 );
2209
2210 hbox = gtk_hbox_new( FALSE, 6 );
2211 gtk_container_add( GTK_CONTAINER( frame ), hbox );
2212
2213 table = gtk_table_new( 4, 2, TRUE );
2214 gtk_box_pack_start( GTK_BOX( hbox ), table, FALSE, FALSE, 10 );
2215
2216 radio1 = gtk_radio_button_new_with_mnemonic( NULL, _("Use _solid color") );
2217 gtk_table_attach( GTK_TABLE( table ), radio1, 0, 2, 0, 1,
2218 GTK_FILL, GTK_FILL, 0, 0 );
2219 slide.radio[0] = radio1;
2220
2221 radio2 = gtk_radio_button_new_with_mnemonic_from_widget(
2222 GTK_RADIO_BUTTON( radio1 ), _("Use _linear gradient") );
2223 gtk_table_attach( GTK_TABLE( table ), radio2, 0, 2, 1, 2,
2224 GTK_FILL, GTK_FILL, 0, 0 );
2225 slide.radio[1] = radio2;
2226
2227 radio3 = gtk_radio_button_new_with_mnemonic_from_widget(
2228 GTK_RADIO_BUTTON( radio1 ), _("Use _radial gradient") );
2229 gtk_table_attach( GTK_TABLE( table ), radio3, 0, 2, 2, 3,
2230 GTK_FILL, GTK_FILL, 0, 0 );
2231 slide.radio[2] = radio3;
2232
2233 gtk_toggle_button_set_active(
2234 GTK_TOGGLE_BUTTON( slide.radio[slide.gradient] ), TRUE );
2235 for( i = 0; i < 3; i++ )
2236 g_signal_connect( G_OBJECT( slide.radio[i] ), "toggled",
2237 G_CALLBACK( img_gradient_toggled ), &slide );
2238
2239 color.red = (gint)( slide.c_start[0] * 0xffff );
2240 color.green = (gint)( slide.c_start[1] * 0xffff );
2241 color.blue = (gint)( slide.c_start[2] * 0xffff );
2242 color1 = gtk_color_button_new_with_color( &color );
2243 gtk_table_attach( GTK_TABLE( table ), color1, 0, 1, 3, 4,
2244 GTK_FILL, GTK_FILL, 0, 0 );
2245 g_signal_connect( G_OBJECT( color1 ), "color-set",
2246 G_CALLBACK( img_gradient_color_set ), &slide );
2247
2248 color.red = (gint)( slide.c_stop[0] * 0xffff );
2249 color.green = (gint)( slide.c_stop[1] * 0xffff );
2250 color.blue = (gint)( slide.c_stop[2] * 0xffff );
2251 color2 = gtk_color_button_new_with_color( &color );
2252 gtk_table_attach( GTK_TABLE( table ), color2, 1, 2, 3, 4,
2253 GTK_FILL, GTK_FILL, 0, 0 );
2254 gtk_widget_set_sensitive( color2, (gboolean)slide.gradient );
2255 g_signal_connect( G_OBJECT( color2 ), "color-set",
2256 G_CALLBACK( img_gradient_color_set ), &slide );
2257
2258 frame = gtk_frame_new( _("Preview") );
2259 gtk_box_pack_start( GTK_BOX( hbox ), frame, TRUE, TRUE, 5 );
2260
2261 w = img->video_size[0] / 2;
2262 h = img->video_size[1] / 2;
2263 preview = gtk_drawing_area_new();
2264 gtk_widget_set_size_request( preview, w, h );
2265 gtk_widget_add_events( preview, GDK_BUTTON1_MOTION_MASK |
2266 GDK_BUTTON_PRESS_MASK |
2267 GDK_BUTTON_RELEASE_MASK );
2268 gtk_container_add( GTK_CONTAINER( frame ), preview );
2269 g_signal_connect( G_OBJECT( preview ), "expose-event",
2270 G_CALLBACK( img_gradient_expose ), &slide );
2271 g_signal_connect( G_OBJECT( preview ), "button-press-event",
2272 G_CALLBACK( img_gradient_press ), &slide );
2273 g_signal_connect( G_OBJECT( preview ), "button-release-event",
2274 G_CALLBACK( img_gradient_release ), &slide );
2275 g_signal_connect( G_OBJECT( preview ), "motion-notify-event",
2276 G_CALLBACK( img_gradient_move ), &slide );
2277
2278 /* Show all */
2279 gtk_widget_show_all( dialog );
2280
2281 /* Fill internal structure */
2282 slide.color2 = color2;
2283 slide.preview = preview;
2284 if( slide.pl_stop[0] < 0 )
2285 {
2286 slide.pl_stop[0] = (gdouble)w;
2287 slide.pl_stop[1] = (gdouble)h;
2288 slide.pr_start[0] = w * 0.5;
2289 slide.pr_start[1] = h * 0.5;
2290 }
2291
2292 if( gtk_dialog_run( GTK_DIALOG( dialog ) ) == GTK_RESPONSE_ACCEPT )
2293 {
2294 GtkTreeIter iter;
2295 slide_struct *slide_info;
2296 GdkPixbuf *thumb;
2297
2298 slide_info = img_create_new_slide();
2299 if( slide_info )
2300 {
2301 gdouble p_start[2],
2302 p_stop[2];
2303
2304 /* Convert gradient points into relative offsets (this enables us to
2305 * scale gradient on any surface size) */
2306 if( slide.gradient < 2 ) /* solid and linear */
2307 {
2308 p_start[0] = slide.pl_start[0] / w;
2309 p_start[1] = slide.pl_start[1] / h;
2310 p_stop[0] = slide.pl_stop[0] / w;
2311 p_stop[1] = slide.pl_stop[1] / h;
2312 }
2313 else /* Radial gradient */
2314 {
2315 p_start[0] = slide.pr_start[0] / w;
2316 p_start[1] = slide.pr_start[1] / h;
2317 p_stop[0] = slide.pr_stop[0] / w;
2318 p_stop[1] = slide.pr_stop[1] / h;
2319 }
2320
2321 /* Update slide info */
2322 img_set_slide_gradient_info( slide_info, slide.gradient,
2323 slide.c_start, slide.c_stop,
2324 p_start, p_stop );
2325
2326 /* Create thumbnail */
2327 img_scale_gradient( slide.gradient, p_start, p_stop,
2328 slide.c_start, slide.c_stop,
2329 88, 72,
2330 &thumb, NULL );
2331
2332 /* Add slide to store */
2333 where_to_insert = gtk_icon_view_get_selected_items(GTK_ICON_VIEW(img->active_icon));
2334 if (where_to_insert)
2335 {
2336 pos = gtk_tree_path_get_indices(where_to_insert->data)[0]+1;
2337 gtk_list_store_insert_with_values(img->thumbnail_model, &iter,
2338 pos,
2339 0, thumb,
2340 1, slide_info,
2341 2, NULL,
2342 3, FALSE,
2343 -1 );
2344 g_list_foreach (where_to_insert, (GFunc)gtk_tree_path_free, NULL);
2345 g_list_free (where_to_insert);
2346 }
2347 else
2348 {
2349 gtk_list_store_append( img->thumbnail_model, &iter );
2350 gtk_list_store_set(img->thumbnail_model, &iter, 0, thumb, 1, slide_info, 2, NULL, 3, FALSE, -1 );
2351 }
2352 g_object_unref( G_OBJECT( thumb ) );
2353 img->slides_nr++;
2354 img_set_total_slideshow_duration( img );
2355
2356 img_select_nth_slide( img, img->slides_nr );
2357 }
2358 }
2359 gtk_widget_destroy( dialog );
2360 }
2361
2362 static void
img_gradient_toggled(GtkToggleButton * button,ImgEmptySlide * slide)2363 img_gradient_toggled( GtkToggleButton *button,
2364 ImgEmptySlide *slide )
2365 {
2366 GtkWidget *widget = GTK_WIDGET( button );
2367 gint i;
2368
2369 if( ! gtk_toggle_button_get_active( button ) )
2370 return;
2371
2372 for( i = 0; widget != slide->radio[i]; i++ )
2373 ;
2374
2375 slide->gradient = i;
2376
2377 gtk_widget_set_sensitive( slide->color2, (gboolean)i );
2378
2379 gtk_widget_queue_draw( slide->preview );
2380 }
2381
2382 static void
img_gradient_color_set(GtkColorButton * button,ImgEmptySlide * slide)2383 img_gradient_color_set( GtkColorButton *button,
2384 ImgEmptySlide *slide )
2385 {
2386 GdkColor color;
2387 gdouble *my_color;
2388
2389 gtk_color_button_get_color( button, &color );
2390
2391 if( (GtkWidget *)button == slide->color2 )
2392 my_color = slide->c_stop;
2393 else
2394 my_color = slide->c_start;
2395
2396 my_color[0] = (gdouble)color.red / 0xffff;
2397 my_color[1] = (gdouble)color.green / 0xffff;
2398 my_color[2] = (gdouble)color.blue / 0xffff;
2399
2400 gtk_widget_queue_draw( slide->preview );
2401 }
2402
2403 static gboolean
img_gradient_expose(GtkWidget * widget,GdkEventExpose * expose,ImgEmptySlide * slide)2404 img_gradient_expose( GtkWidget *widget,
2405 GdkEventExpose *expose,
2406 ImgEmptySlide *slide )
2407 {
2408 cairo_t *cr;
2409 cairo_pattern_t *pattern;
2410 gint w, h;
2411 gdouble radius, diffx, diffy;
2412
2413 gdk_drawable_get_size( expose->window, &w, &h );
2414 cr = gdk_cairo_create( expose->window );
2415 switch( slide->gradient )
2416 {
2417 case 0:
2418 cairo_set_source_rgb( cr, slide->c_start[0],
2419 slide->c_start[1],
2420 slide->c_start[2] );
2421 cairo_paint( cr );
2422 break;
2423
2424 case 1:
2425 pattern = cairo_pattern_create_linear( slide->pl_start[0],
2426 slide->pl_start[1],
2427 slide->pl_stop[0],
2428 slide->pl_stop[1] );
2429 cairo_pattern_add_color_stop_rgb( pattern, 0,
2430 slide->c_start[0],
2431 slide->c_start[1],
2432 slide->c_start[2] );
2433 cairo_pattern_add_color_stop_rgb( pattern, 1,
2434 slide->c_stop[0],
2435 slide->c_stop[1],
2436 slide->c_stop[2] );
2437 cairo_set_source( cr, pattern );
2438 cairo_paint( cr );
2439 cairo_pattern_destroy( pattern );
2440
2441 /* Paint indicators */
2442 cairo_rectangle( cr, slide->pl_start[0] - 7,
2443 slide->pl_start[1] - 7,
2444 15, 15 );
2445 cairo_rectangle( cr, slide->pl_stop[0] - 7,
2446 slide->pl_stop[1] - 7,
2447 15, 15 );
2448 cairo_set_source_rgb( cr, 0, 0, 0 );
2449 cairo_stroke_preserve( cr );
2450 cairo_set_source_rgb( cr, 1, 1, 1 );
2451 cairo_fill( cr );
2452 break;
2453
2454 case 2:
2455 diffx = ABS( slide->pr_start[0] - slide->pr_stop[0] );
2456 diffy = ABS( slide->pr_start[1] - slide->pr_stop[1] );
2457 radius = sqrt( pow( diffx, 2 ) + pow( diffy, 2 ) );
2458 pattern = cairo_pattern_create_radial( slide->pr_start[0],
2459 slide->pr_start[1],
2460 0,
2461 slide->pr_start[0],
2462 slide->pr_start[1],
2463 radius );
2464 cairo_pattern_add_color_stop_rgb( pattern, 0,
2465 slide->c_start[0],
2466 slide->c_start[1],
2467 slide->c_start[2] );
2468 cairo_pattern_add_color_stop_rgb( pattern, 1,
2469 slide->c_stop[0],
2470 slide->c_stop[1],
2471 slide->c_stop[2] );
2472 cairo_set_source( cr, pattern );
2473 cairo_paint( cr );
2474 cairo_pattern_destroy( pattern );
2475
2476 /* Paint indicators */
2477 cairo_rectangle( cr, slide->pr_start[0] - 7,
2478 slide->pr_start[1] - 7,
2479 15, 15 );
2480 cairo_rectangle( cr, slide->pr_stop[0] - 7,
2481 slide->pr_stop[1] - 7,
2482 15, 15 );
2483 cairo_set_source_rgb( cr, 0, 0, 0 );
2484 cairo_stroke_preserve( cr );
2485 cairo_set_source_rgb( cr, 1, 1, 1 );
2486 cairo_fill( cr );
2487 break;
2488 }
2489 cairo_destroy( cr );
2490
2491 return( TRUE );
2492 }
2493
2494 static gboolean
img_gradient_press(GtkWidget * widget,GdkEventButton * button,ImgEmptySlide * slide)2495 img_gradient_press( GtkWidget *widget,
2496 GdkEventButton *button,
2497 ImgEmptySlide *slide )
2498 {
2499 if( button->button != 1 )
2500 return( FALSE );
2501
2502 switch( slide->gradient )
2503 {
2504 case 1:
2505 if( button->x < ( slide->pl_start[0] + 8 ) &&
2506 button->x > ( slide->pl_start[0] - 8 ) &&
2507 button->y < ( slide->pl_start[1] + 8 ) &&
2508 button->y > ( slide->pl_start[1] - 8 ) )
2509 {
2510 slide->drag = 1;
2511 }
2512 else if( button->x < ( slide->pl_stop[0] + 8 ) &&
2513 button->x > ( slide->pl_stop[0] - 8 ) &&
2514 button->y < ( slide->pl_stop[1] + 8 ) &&
2515 button->y > ( slide->pl_stop[1] - 8 ) )
2516 {
2517 slide->drag = 2;
2518 }
2519 break;
2520
2521 case 2:
2522 if( button->x < ( slide->pr_start[0] + 8 ) &&
2523 button->x > ( slide->pr_start[0] - 8 ) &&
2524 button->y < ( slide->pr_start[1] + 8 ) &&
2525 button->y > ( slide->pr_start[1] - 8 ) )
2526 {
2527 slide->drag = 1;
2528 }
2529 else if( button->x < ( slide->pr_stop[0] + 8 ) &&
2530 button->x > ( slide->pr_stop[0] - 8 ) &&
2531 button->y < ( slide->pr_stop[1] + 8 ) &&
2532 button->y > ( slide->pr_stop[1] - 8 ) )
2533 {
2534 slide->drag = 2;
2535 }
2536 break;
2537 }
2538
2539 return( TRUE );
2540 }
2541
2542 static gboolean
img_gradient_release(GtkWidget * widget,GdkEventButton * button,ImgEmptySlide * slide)2543 img_gradient_release( GtkWidget *widget,
2544 GdkEventButton *button,
2545 ImgEmptySlide *slide )
2546 {
2547 slide->drag = 0;
2548
2549 return( TRUE );
2550 }
2551
2552 static gboolean
img_gradient_move(GtkWidget * widget,GdkEventMotion * motion,ImgEmptySlide * slide)2553 img_gradient_move( GtkWidget *widget,
2554 GdkEventMotion *motion,
2555 ImgEmptySlide *slide )
2556 {
2557 gint w, h;
2558
2559 if( ! slide->drag )
2560 return( FALSE );
2561
2562 gdk_drawable_get_size( motion->window, &w, &h );
2563 switch( slide->gradient )
2564 {
2565 case 1:
2566 if( slide->drag == 1 )
2567 {
2568 slide->pl_start[0] = CLAMP( motion->x, 0, w );
2569 slide->pl_start[1] = CLAMP( motion->y, 0, h );
2570 }
2571 else
2572 {
2573 slide->pl_stop[0] = CLAMP( motion->x, 0, w );
2574 slide->pl_stop[1] = CLAMP( motion->y, 0, h );
2575 }
2576 break;
2577
2578 case 2:
2579 if( slide->drag == 1 )
2580 {
2581 slide->pr_start[0] = CLAMP( motion->x, 0, w );
2582 slide->pr_start[1] = CLAMP( motion->y, 0, h );
2583 }
2584 else
2585 {
2586 slide->pr_stop[0] = CLAMP( motion->x, 0, w );
2587 slide->pr_stop[1] = CLAMP( motion->y, 0, h );
2588 }
2589 break;
2590 }
2591 gtk_widget_queue_draw( slide->preview );
2592
2593 return( TRUE );
2594 }
2595
2596 gboolean
img_save_window_settings(img_window_struct * img)2597 img_save_window_settings( img_window_struct *img )
2598 {
2599 GKeyFile *kf;
2600 gchar *group = "Interface settings";
2601 gchar *rc_file, *rc_path, *contents;
2602 int w, h, g, f; /* Width, height, gutter, flags */
2603 gboolean max;
2604
2605 gtk_window_get_size( GTK_WINDOW( img->imagination_window ), &w, &h );
2606 g = gtk_paned_get_position( GTK_PANED( img->paned ) );
2607 f = gdk_window_get_state( gtk_widget_get_window( img->imagination_window ) );
2608 max = f & GDK_WINDOW_STATE_MAXIMIZED;
2609
2610 /* If window is maximized, store sizes that are a bit smaller than full
2611 * screen, else making window non-fullscreen will have no effect. */
2612 if( max )
2613 {
2614 w -= 100;
2615 h -= 100;
2616 }
2617
2618 kf = g_key_file_new();
2619 g_key_file_set_integer( kf, group, "width", w );
2620 g_key_file_set_integer( kf, group, "height", h );
2621 g_key_file_set_integer( kf, group, "gutter", g );
2622 g_key_file_set_integer( kf, group, "mode", img->mode );
2623 g_key_file_set_double( kf, group, "zoom_p", img->image_area_zoom );
2624 g_key_file_set_double( kf, group, "zoom_o", img->overview_zoom );
2625 g_key_file_set_boolean( kf, group, "quality", img->low_quality );
2626 g_key_file_set_boolean( kf, group, "max", max );
2627 g_key_file_set_integer( kf, group, "preview", img->preview_fps );
2628
2629 rc_path = g_build_filename( g_get_home_dir(), ".config",
2630 "imagination", NULL );
2631 rc_file = g_build_filename( rc_path, "imaginationrc", NULL );
2632 contents = g_key_file_to_data( kf, NULL, NULL );
2633 g_key_file_free( kf );
2634
2635 g_mkdir_with_parents( rc_path, S_IRWXU );
2636 g_file_set_contents( rc_file, contents, -1, NULL );
2637
2638 g_free( contents );
2639 g_free( rc_file );
2640 g_free( rc_path );
2641
2642 return( FALSE );
2643 }
2644
2645 gboolean
img_load_window_settings(img_window_struct * img)2646 img_load_window_settings( img_window_struct *img )
2647 {
2648 GKeyFile *kf;
2649 gchar *group = "Interface settings";
2650 gchar *rc_file;
2651 int w, h, g, m; /* Width, height, gutter, mode */
2652 gboolean max;
2653
2654 rc_file = g_build_filename( g_get_home_dir(), ".config",
2655 "imagination", "imaginationrc", NULL );
2656 if( ! g_file_test( rc_file, G_FILE_TEST_EXISTS ) )
2657 return( FALSE );
2658
2659 kf = g_key_file_new();
2660 g_key_file_load_from_file( kf, rc_file, G_KEY_FILE_NONE, NULL );
2661
2662 w = g_key_file_get_integer( kf, group, "width", NULL );
2663 h = g_key_file_get_integer( kf, group, "height", NULL );
2664 g = g_key_file_get_integer( kf, group, "gutter", NULL );
2665 m = g_key_file_get_integer( kf, group, "mode", NULL );
2666 img->image_area_zoom = g_key_file_get_double( kf, group, "zoom_p", NULL );
2667 img->overview_zoom = g_key_file_get_double( kf, group, "zoom_o", NULL );
2668 img->low_quality = g_key_file_get_boolean( kf, group, "quality", NULL );
2669 max = g_key_file_get_boolean( kf, group, "max", NULL );
2670
2671 /* New addition to environment settings */
2672 img->preview_fps = g_key_file_get_integer( kf, group, "preview", NULL );
2673 if( ! img->preview_fps )
2674 img->preview_fps = PREVIEW_FPS_DEFAULT;
2675
2676 g_key_file_free( kf );
2677
2678 /* Update mode */
2679 img->mode = - 1;
2680
2681 img_switch_mode( img, m );
2682 if (m == 0)
2683 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (img->menu_preview_mode), TRUE);
2684 else
2685 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (img->menu_overview_mode), TRUE);
2686 /* Update window size and gutter position */
2687 gtk_window_set_default_size( GTK_WINDOW( img->imagination_window ), w, h );
2688 gtk_paned_set_position( GTK_PANED( img->paned ), g );
2689 if( max )
2690 gtk_window_maximize( GTK_WINDOW( img->imagination_window ) );
2691
2692 /* Update zoom display */
2693 gtk_widget_set_size_request( img->image_area,
2694 img->video_size[0] * img->image_area_zoom,
2695 img->video_size[1] * img->image_area_zoom );
2696 g_object_set( img->over_cell, "zoom", img->overview_zoom, NULL );
2697
2698 return( TRUE );
2699 }
2700
2701 void
img_set_window_default_settings(img_window_struct * img)2702 img_set_window_default_settings( img_window_struct *img )
2703 {
2704 img->image_area_zoom = 1.0;
2705 img->overview_zoom = 1.0;
2706 img->low_quality = TRUE;
2707 img->preview_fps = PREVIEW_FPS_DEFAULT;
2708
2709 /* Update mode */
2710 img->mode = - 1;
2711 img_switch_mode( img, 0 );
2712
2713 /* Update window size and gutter position */
2714 gtk_window_set_default_size( GTK_WINDOW( img->imagination_window ),
2715 800, 600 );
2716 gtk_paned_set_position( GTK_PANED( img->paned ), 500 );
2717 }
2718
2719 void
img_rotate_slide(slide_struct * slide,ImgAngle angle,GtkProgressBar * progress)2720 img_rotate_slide( slide_struct *slide,
2721 ImgAngle angle,
2722 GtkProgressBar *progress )
2723 {
2724 gchar *filename;
2725
2726 /* If this slide is gradient, do nothing */
2727 if( ! slide->o_filename )
2728 return;
2729
2730 /* If the angle is ANGLE_0, then simply copy original filename into rotated
2731 * filename. */
2732 if( angle )
2733 {
2734 GdkPixbuf *image,
2735 *rotated;
2736 gint handle;
2737 GError *error = NULL;
2738
2739 image = gdk_pixbuf_new_from_file( slide->o_filename, NULL );
2740 if( progress )
2741 rotated = img_rotate_pixbuf( image, progress, angle );
2742 else
2743 rotated = gdk_pixbuf_rotate_simple( image, angle * 90 );
2744 g_object_unref( image );
2745
2746 handle = g_file_open_tmp( "img-XXXXXX.jpg", &filename, NULL );
2747 close( handle );
2748 if( ! gdk_pixbuf_save( rotated, filename, "jpeg", &error, NULL ) )
2749 {
2750 g_message( "%s.", error->message );
2751 g_error_free( error );
2752 g_free( filename );
2753 filename = g_strdup( slide->r_filename );
2754 }
2755 g_object_unref( rotated );
2756 }
2757 else
2758 filename = g_strdup( slide->o_filename );
2759
2760 /* Delete any temporary image that is present from previous rotation */
2761 if( slide->angle )
2762 unlink( slide->r_filename );
2763 g_free( slide->r_filename );
2764 slide->r_filename = filename;
2765 slide->angle = angle;
2766 }
2767
2768 void
img_notebook_switch_page(GtkNotebook * notebook,GtkNotebookPage * page,guint page_num,img_window_struct * img)2769 img_notebook_switch_page (GtkNotebook *notebook,
2770 GtkNotebookPage *page,
2771 guint page_num,
2772 img_window_struct *img)
2773 {
2774 /* When message page is viewed, set it back to black */
2775 if (page_num == img->message_page)
2776 {
2777 PangoAttrList * pango_list = pango_attr_list_new();
2778 PangoAttribute * pango_attr = pango_attr_weight_new (PANGO_WEIGHT_NORMAL);
2779 pango_attr_list_insert(pango_list, pango_attr);
2780 gtk_label_set_attributes(GTK_LABEL(img->message_label), pango_list);
2781 pango_attr_list_unref (pango_list);
2782 }
2783 }