1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*- */
2
3 #include <glib.h>
4 #include <gtk/gtk.h>
5 #include <assert.h>
6 #include <stdio.h>
7
8 // Library under test.
9 #include <src/gtkanimview.h>
10 #include <src/gtkimagescrollwin.h>
11 #include <src/gtkimagetooldragger.h>
12 #include <src/gtkimagetoolpainter.h>
13 #include <src/gtkimagetoolselector.h>
14
15 // Defines for backwards compatibility with GTK+ 2.6
16 #if !GTK_CHECK_VERSION(2,8,0)
17 #define GTK_STOCK_FULLSCREEN ""
18 #endif
19
20 //////////////////////////////////////////////////////////////////////
21 ///// Global data ////////////////////////////////////////////////////
22 //////////////////////////////////////////////////////////////////////
23 static GtkFileChooserDialog *open_dialog = NULL;
24 static GtkAnimView *view = NULL;
25 static GtkWindow *main_window;
26 static GtkActionGroup *default_group = NULL;
27 static GtkActionGroup *image_group = NULL;
28 static GtkActionGroup *transform_group = NULL;
29 static gboolean is_fullscreen = FALSE;
30 static GtkWidget *statusbar = NULL;
31
32 // Label that displays the active selection.
33 static GtkWidget *sel_info_label = NULL;
34
35 // Tools
36 static GtkIImageTool *dragger = NULL;
37 static GtkIImageTool *selector = NULL;
38 static GtkIImageTool *painter = NULL;
39
40 // Context ID:s for the Statusbar
41 int help_msg_cid = -1;
42 int image_info_cid = -1;
43
44 //////////////////////////////////////////////////////////////////////
45 ///// Opener dialog //////////////////////////////////////////////////
46 //////////////////////////////////////////////////////////////////////
47 static void
init_open_dialog()48 init_open_dialog ()
49 {
50 open_dialog = (GtkFileChooserDialog *)
51 gtk_file_chooser_dialog_new ("Open Image",
52 main_window,
53 GTK_FILE_CHOOSER_ACTION_OPEN,
54 GTK_STOCK_CANCEL,
55 GTK_RESPONSE_CANCEL,
56 GTK_STOCK_OPEN,
57 GTK_RESPONSE_ACCEPT,
58 NULL);
59 }
60
61 //////////////////////////////////////////////////////////////////////
62 ///// ImageViewerApp /////////////////////////////////////////////////
63 //////////////////////////////////////////////////////////////////////
64 static void
push_image_info(char * basename,GdkPixbufAnimation * anim)65 push_image_info (char *basename,
66 GdkPixbufAnimation *anim)
67 {
68 int width = gdk_pixbuf_animation_get_width (anim);
69 int height = gdk_pixbuf_animation_get_height (anim);
70 char *msg = g_strdup_printf ("%s, %d x %d pixels",
71 basename, width, height);
72 gtk_statusbar_push (GTK_STATUSBAR (statusbar), image_info_cid, msg);
73 g_free (msg);
74 }
75
76
77 static void
load_filename(char * path)78 load_filename (char *path)
79 {
80 GdkPixbufAnimation *anim = gdk_pixbuf_animation_new_from_file (path,
81 NULL);
82 if (!anim)
83 {
84 printf ("No anim!\n");
85 return;
86 }
87 gtk_anim_view_set_anim (view, anim);
88 g_object_unref (anim);
89
90 char *basename = g_path_get_basename (path);
91 gtk_window_set_title (main_window, basename);
92 push_image_info (basename, anim);
93 g_free (basename);
94
95 gtk_action_group_set_sensitive (image_group, TRUE);
96
97 /* Only active the transform_group if the loaded object is a single
98 image -- transformations cannot be applied to animations. */
99 gboolean is_image = gdk_pixbuf_animation_is_static_image (anim);
100 gtk_action_group_set_sensitive (transform_group, is_image);
101 }
102
103 //////////////////////////////////////////////////////////////////////
104 ///// Callbacks //////////////////////////////////////////////////////
105 //////////////////////////////////////////////////////////////////////
106 static void
sel_changed_cb(GtkImageToolSelector * selector,GtkLabel * label)107 sel_changed_cb (GtkImageToolSelector *selector,
108 GtkLabel *label)
109 {
110 GdkRectangle sel;
111 gtk_image_tool_selector_get_selection (selector, &sel);
112 if (!sel.width || !sel.height)
113 gtk_label_set_text (label, "");
114 else
115 {
116 char *text = g_strdup_printf ("%s", gdk_rectangle_to_str (sel));
117 gtk_label_set_text (label, text);
118 g_free (text);
119 }
120 }
121
122 static void
change_image_tool_cb(GtkAction * action,GtkRadioAction * current)123 change_image_tool_cb (GtkAction *action,
124 GtkRadioAction *current)
125 {
126 int value = gtk_radio_action_get_current_value (current);
127 GtkIImageTool *tool = selector;
128 if (value == 10)
129 tool = dragger;
130 else if (value == 30)
131 tool = painter;
132 gtk_image_view_set_tool (GTK_IMAGE_VIEW (view), tool);
133 if (value == 20)
134 sel_changed_cb (GTK_IMAGE_TOOL_SELECTOR (selector),
135 GTK_LABEL (sel_info_label));
136 else
137 gtk_label_set_text (GTK_LABEL (sel_info_label), "");
138 }
139
140 static void
zoom_in_cb()141 zoom_in_cb ()
142 {
143 gtk_image_view_zoom_in (GTK_IMAGE_VIEW (view));
144 }
145
146 static void
zoom_out_cb()147 zoom_out_cb ()
148 {
149 gtk_image_view_zoom_out (GTK_IMAGE_VIEW (view));
150 }
151
152 static void
zoom_100_cb()153 zoom_100_cb ()
154 {
155 gtk_image_view_set_zoom (GTK_IMAGE_VIEW (view), 1.0);
156 }
157
158 static void
zoom_to_fit_cb()159 zoom_to_fit_cb ()
160 {
161 gtk_image_view_set_fitting (GTK_IMAGE_VIEW (view), TRUE);
162 }
163
164 static void
open_image_cb(GtkAction * action)165 open_image_cb (GtkAction *action)
166 {
167 if (!open_dialog)
168 init_open_dialog ();
169 if (gtk_dialog_run (GTK_DIALOG (open_dialog)) == GTK_RESPONSE_ACCEPT)
170 {
171 char *fname;
172 fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (open_dialog));
173 load_filename (fname);
174 g_free (fname);
175 }
176 gtk_widget_hide (GTK_WIDGET (open_dialog));
177 }
178
179 static void
fullscreen_cb()180 fullscreen_cb ()
181 {
182 // I do not have the patience to implement all things you do to
183 // fullscreen for real. This is a faked approximation.
184 is_fullscreen = !is_fullscreen;
185 if (is_fullscreen)
186 gtk_window_fullscreen (main_window);
187 else
188 gtk_window_unfullscreen (main_window);
189
190 gtk_image_view_set_show_cursor (GTK_IMAGE_VIEW (view), !is_fullscreen);
191 gtk_image_view_set_show_frame (GTK_IMAGE_VIEW (view), !is_fullscreen);
192 gtk_image_view_set_black_bg (GTK_IMAGE_VIEW (view), is_fullscreen);
193 }
194
195 static void
transform_cb()196 transform_cb ()
197 {
198 GdkPixbuf *pixbuf = gtk_image_view_get_pixbuf (GTK_IMAGE_VIEW (view));
199 guchar *pixels = gdk_pixbuf_get_pixels (pixbuf);
200 int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
201 int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
202 for (int y = 0; y < gdk_pixbuf_get_height (pixbuf); y++)
203 for (int x = 0; x < gdk_pixbuf_get_width (pixbuf); x++)
204 {
205 guchar *p = pixels + y * rowstride + x * n_channels;
206 for (int n = 0; n < 3; n++)
207 p[n] ^= 0xff;
208 }
209 gtk_image_view_damage_pixels (GTK_IMAGE_VIEW (view), NULL);
210 }
211
212 static void
change_zoom_quality_cb(GtkAction * action,GtkRadioAction * current)213 change_zoom_quality_cb (GtkAction *action,
214 GtkRadioAction *current)
215 {
216 if (gtk_radio_action_get_current_value (current))
217 gtk_image_view_set_interpolation (GTK_IMAGE_VIEW (view),
218 GDK_INTERP_BILINEAR);
219 else
220 gtk_image_view_set_interpolation (GTK_IMAGE_VIEW (view),
221 GDK_INTERP_NEAREST);
222 }
223
224 static void
change_transp_type_cb(GtkAction * action,GtkRadioAction * current)225 change_transp_type_cb (GtkAction *action,
226 GtkRadioAction *current)
227 {
228 int color = 0;
229 GtkImageTransp transp = gtk_radio_action_get_current_value (current);
230 if (transp == GTK_IMAGE_TRANSP_COLOR)
231 color = 0x000000;
232 gtk_image_view_set_transp (GTK_IMAGE_VIEW (view), transp, color);
233 }
234
235 static void
menu_item_select_cb(GtkMenuItem * proxy)236 menu_item_select_cb (GtkMenuItem *proxy)
237 {
238 GtkAction *action = g_object_get_data (G_OBJECT (proxy), "gtk-action");
239
240 char *msg;
241 g_object_get (G_OBJECT (action), "tooltip", &msg, NULL);
242
243 if (msg)
244 {
245 gtk_statusbar_push (GTK_STATUSBAR (statusbar), help_msg_cid, msg);
246 g_free (msg);
247 }
248 }
249
250 static void
menu_item_deselect_cb(GtkMenuItem * item)251 menu_item_deselect_cb (GtkMenuItem *item)
252 {
253 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), help_msg_cid);
254 }
255
256 static void
connect_proxy_cb(GtkUIManager * ui,GtkAction * action,GtkWidget * proxy)257 connect_proxy_cb (GtkUIManager *ui,
258 GtkAction *action,
259 GtkWidget *proxy)
260 {
261 if (!GTK_IS_MENU_ITEM (proxy))
262 return;
263 g_signal_connect (proxy, "select", G_CALLBACK (menu_item_select_cb), NULL);
264 g_signal_connect (proxy, "deselect",
265 G_CALLBACK (menu_item_deselect_cb), NULL);
266 }
267
268 static void
disconnect_proxy_cb(GtkUIManager * ui,GtkAction * action,GtkWidget * proxy)269 disconnect_proxy_cb (GtkUIManager *ui,
270 GtkAction *action,
271 GtkWidget *proxy)
272 {
273 if (!GTK_IS_MENU_ITEM (proxy))
274 return;
275 g_signal_handlers_disconnect_by_func (proxy,
276 G_CALLBACK (menu_item_select_cb),
277 NULL);
278 g_signal_handlers_disconnect_by_func (proxy,
279 G_CALLBACK (menu_item_deselect_cb),
280 NULL);
281 }
282
283 static void
zoom_changed_cb(GtkImageView * view,GtkLabel * label)284 zoom_changed_cb (GtkImageView *view,
285 GtkLabel *label)
286 {
287 gdouble zoom = gtk_image_view_get_zoom (view);
288 char *text = g_strdup_printf ("%d%%", (int)(zoom * 100.0));
289 gtk_label_set_text (label, text);
290 g_free (text);
291 }
292
293 static void
kill_app_cb(void)294 kill_app_cb (void)
295 {
296 /* Kill the widgets. */
297 gtk_widget_destroy (GTK_WIDGET (main_window));
298 if (open_dialog)
299 gtk_widget_destroy (GTK_WIDGET (open_dialog));
300 gtk_main_quit ();
301 }
302
303 //////////////////////////////////////////////////////////////////////
304 ///// MainWindow /////////////////////////////////////////////////////
305 //////////////////////////////////////////////////////////////////////
306 static GtkWindow *
main_window_new(GtkWidget * widget,int width,int height)307 main_window_new (GtkWidget *widget,
308 int width,
309 int height)
310 {
311 GtkWindow *window = (GtkWindow *)gtk_window_new (GTK_WINDOW_TOPLEVEL);
312 gtk_window_set_default_size (window, width, height);
313 gtk_container_add (GTK_CONTAINER (window), widget);
314 g_signal_connect (G_OBJECT (window), "delete_event",
315 G_CALLBACK (kill_app_cb), NULL);
316 return window;
317 }
318
319 //////////////////////////////////////////////////////////////////////
320 ///// UI Setup ///////////////////////////////////////////////////////
321 //////////////////////////////////////////////////////////////////////
322 static const GtkActionEntry default_actions[] = {
323 {"FileMenu", NULL, "_File"},
324 {
325 "Open",
326 GTK_STOCK_OPEN,
327 "_Open image",
328 NULL,
329 "Open an image",
330 G_CALLBACK (open_image_cb)
331 },
332 {
333 "Quit",
334 GTK_STOCK_QUIT,
335 "_Quit me!",
336 NULL,
337 "Quit the program",
338 G_CALLBACK (kill_app_cb)
339 },
340 {"EditMenu", NULL, "_Edit"},
341 {"ViewMenu", NULL, "_View"},
342 {"TranspMenu", NULL, "_Transparency"}
343 };
344
345 static const GtkRadioActionEntry quality_actions[] = {
346 {
347 "QualityHigh",
348 NULL,
349 "_High Quality",
350 NULL,
351 "Use high quality zoom",
352 TRUE
353 },
354 {
355 "QualityLow",
356 NULL,
357 "_Low Quality",
358 NULL,
359 "Use low quality zoom",
360 FALSE
361 }
362 };
363
364 static const GtkRadioActionEntry transp_actions[] = {
365 {
366 "TranspGrid",
367 NULL,
368 "Square _Grid",
369 NULL,
370 "Draw a grid on transparent parts",
371 GTK_IMAGE_TRANSP_GRID
372 },
373 {
374 "TranspBackground",
375 NULL,
376 "_Background",
377 NULL,
378 "Draw background color on transparent parts",
379 GTK_IMAGE_TRANSP_BACKGROUND
380 },
381 {
382 "TranspBlack",
383 NULL,
384 "_Black",
385 NULL,
386 "Draw black color on transparent parts",
387 GTK_IMAGE_TRANSP_COLOR
388 }
389 };
390
391 static const GtkActionEntry image_actions[] = {
392 {
393 "ZoomIn",
394 GTK_STOCK_ZOOM_IN,
395 "Zoom _In",
396 "<control>plus",
397 "Zoom in one step",
398 G_CALLBACK (zoom_in_cb)
399 },
400 {
401 "ZoomOut",
402 GTK_STOCK_ZOOM_OUT,
403 "Zoom _Out",
404 "<control>minus",
405 "Zoom out one step",
406 G_CALLBACK (zoom_out_cb)
407 },
408 {
409 "ZoomNormal",
410 GTK_STOCK_ZOOM_100,
411 "_Normal Size",
412 "<control>0",
413 "Set zoom to natural size of the image",
414 G_CALLBACK (zoom_100_cb)
415 },
416 {
417 "ZoomFit",
418 GTK_STOCK_ZOOM_FIT,
419 "Best _Fit",
420 NULL,
421 "Adapt zoom to fit image",
422 G_CALLBACK (zoom_to_fit_cb)
423 },
424 {
425 "Fullscreen",
426 GTK_STOCK_FULLSCREEN,
427 "_Fullscreen Mode",
428 "F11",
429 "View image in fullscreen",
430 G_CALLBACK (fullscreen_cb)
431 }
432 };
433
434 static const GtkRadioActionEntry image_tools[] = {
435 {
436 "DraggerTool",
437 GTK_STOCK_REFRESH,
438 "_Drag",
439 NULL,
440 "Use the hand tool",
441 10
442 },
443 {
444 "SelectorTool",
445 GTK_STOCK_MEDIA_PAUSE,
446 "_Select",
447 NULL,
448 "Use the rectangular selection tool",
449 20
450 },
451 {
452 "PainterTool",
453 GTK_STOCK_MEDIA_PLAY,
454 "_Paint",
455 NULL,
456 "Use the painter tool",
457 30
458 }
459 };
460
461 static const GtkActionEntry transform_actions[] = {
462 {
463 "Transform",
464 NULL,
465 "_Transform",
466 "<control>T",
467 "Apply an XOR transformation to the image",
468 G_CALLBACK (transform_cb)
469 }
470 };
471
472 gchar *ui_info =
473 "<ui>"
474 " <menubar name = 'MenuBar'>"
475 " <menu action = 'FileMenu'>"
476 " <menuitem action = 'Open'/>"
477 " <menuitem action = 'Quit'/>"
478 " </menu>"
479 " <menu action = 'EditMenu'>"
480 " <menuitem action = 'Transform'/>"
481 " <separator/>"
482 " <menuitem action = 'DraggerTool'/>"
483 " <menuitem action = 'SelectorTool'/>"
484 " <menuitem action = 'PainterTool'/>"
485 " </menu>"
486 " <menu action = 'ViewMenu'>"
487 " <menuitem action = 'Fullscreen'/>"
488 " <separator/>"
489 " <menuitem action = 'ZoomIn'/>"
490 " <menuitem action = 'ZoomOut'/>"
491 " <menuitem action = 'ZoomNormal'/>"
492 " <menuitem action = 'ZoomFit'/>"
493 " <separator/>"
494 " <menu action = 'TranspMenu'>"
495 " <menuitem action = 'TranspGrid'/>"
496 " <menuitem action = 'TranspBackground'/>"
497 " <menuitem action = 'TranspBlack'/>"
498 " </menu>"
499 " <separator/>"
500 " <menuitem action = 'QualityHigh'/>"
501 " <menuitem action = 'QualityLow'/>"
502 " </menu>"
503 " </menubar>"
504 " <toolbar name = 'ToolBar'>"
505 " <toolitem action='Quit'/>"
506 " <toolitem action='Open'/>"
507 " <separator/>"
508 " <toolitem action='DraggerTool'/>"
509 " <toolitem action='SelectorTool'/>"
510 " <toolitem action='PainterTool'/>"
511 " <separator/>"
512 " <toolitem action='ZoomIn'/>"
513 " <toolitem action='ZoomOut'/>"
514 " <toolitem action='ZoomNormal'/>"
515 " <toolitem action='ZoomFit'/>"
516 " </toolbar>"
517 "</ui>";
518
519
520 static void
parse_ui(GtkUIManager * uimanager)521 parse_ui (GtkUIManager *uimanager)
522 {
523 GError *err;
524 if (!gtk_ui_manager_add_ui_from_string (uimanager, ui_info, -1, &err))
525 {
526 g_warning ("Unable to create menus: %s", err->message);
527 g_free (err);
528 }
529 }
530
531 static void
add_action_groups(GtkUIManager * uimanager)532 add_action_groups (GtkUIManager *uimanager)
533 {
534 // Setup the default group.
535 default_group = gtk_action_group_new ("default");
536 gtk_action_group_add_actions (default_group,
537 default_actions,
538 G_N_ELEMENTS (default_actions),
539 NULL);
540 gtk_action_group_add_radio_actions (default_group,
541 image_tools,
542 G_N_ELEMENTS (image_tools),
543 10,
544 G_CALLBACK (change_image_tool_cb),
545 NULL);
546 gtk_ui_manager_insert_action_group (uimanager, default_group, 0);
547
548 // Setup the image group.
549 image_group = gtk_action_group_new ("image");
550 gtk_action_group_add_actions (image_group,
551 image_actions,
552 G_N_ELEMENTS (image_actions),
553 NULL);
554 gtk_action_group_add_radio_actions (image_group,
555 quality_actions,
556 G_N_ELEMENTS (quality_actions),
557 TRUE,
558 G_CALLBACK (change_zoom_quality_cb),
559 NULL);
560 gtk_action_group_add_radio_actions (image_group,
561 transp_actions,
562 G_N_ELEMENTS (transp_actions),
563 GTK_IMAGE_TRANSP_GRID,
564 G_CALLBACK (change_transp_type_cb),
565 NULL);
566 gtk_action_group_set_sensitive (image_group, FALSE);
567 gtk_ui_manager_insert_action_group (uimanager, image_group, 0);
568
569 // Transform group
570 transform_group = gtk_action_group_new ("transform");
571 gtk_action_group_add_actions (transform_group,
572 transform_actions,
573 G_N_ELEMENTS (transform_actions),
574 NULL);
575 gtk_action_group_set_sensitive (transform_group, FALSE);
576 gtk_ui_manager_insert_action_group (uimanager, transform_group, 0);
577 }
578
579 static GtkWidget *
setup_layout(GtkUIManager * uimanager)580 setup_layout (GtkUIManager *uimanager)
581 {
582 GtkWidget *box = gtk_vbox_new (FALSE, 0);
583
584 GtkWidget *menu = gtk_ui_manager_get_widget (uimanager, "/MenuBar");
585 gtk_box_pack_start (GTK_BOX (box), menu, FALSE, FALSE, 0);
586
587 GtkWidget *toolbar = gtk_ui_manager_get_widget (uimanager, "/ToolBar");
588 gtk_box_pack_start (GTK_BOX (box), toolbar, FALSE, FALSE, 0);
589
590 GtkWidget *scroll_win = gtk_image_scroll_win_new (GTK_IMAGE_VIEW (view));
591
592 gtk_box_pack_start (GTK_BOX (box), scroll_win, TRUE, TRUE, 0);
593
594 statusbar = gtk_statusbar_new ();
595
596 // A label in the statusbar that displays the current selection if
597 // there is one.
598 GtkWidget *sel_info_frame = gtk_frame_new (NULL);
599 gtk_frame_set_shadow_type (GTK_FRAME (sel_info_frame), GTK_SHADOW_IN);
600
601 sel_info_label = gtk_label_new ("");
602 gtk_container_add (GTK_CONTAINER (sel_info_frame), sel_info_label);
603
604 g_signal_connect (G_OBJECT (selector), "selection_changed",
605 G_CALLBACK (sel_changed_cb), sel_info_label);
606
607 gtk_box_pack_start (GTK_BOX (statusbar), sel_info_frame, FALSE, FALSE, 0);
608
609 // A label in the statusbar that displays the current zoom. It
610 // updates its text when the zoom-changed signal is fired from the
611 // view.
612 GtkWidget *zoom_info_frame = gtk_frame_new (NULL);
613 gtk_frame_set_shadow_type (GTK_FRAME (zoom_info_frame), GTK_SHADOW_IN);
614
615 GtkWidget *zoom_info_label = gtk_label_new ("100%");
616 gtk_container_add (GTK_CONTAINER (zoom_info_frame), zoom_info_label);
617
618 g_signal_connect (G_OBJECT (view), "zoom_changed",
619 G_CALLBACK (zoom_changed_cb), zoom_info_label);
620
621 gtk_box_pack_start (GTK_BOX (statusbar), zoom_info_frame, FALSE, FALSE, 0);
622
623 gtk_box_pack_end (GTK_BOX (box), statusbar, FALSE, FALSE, 0);
624 return box;
625 }
626
627 static void
setup_main_window()628 setup_main_window ()
629 {
630 GtkUIManager *uimanager = gtk_ui_manager_new ();
631
632 g_signal_connect (uimanager, "connect_proxy",
633 G_CALLBACK (connect_proxy_cb), NULL);
634 g_signal_connect (uimanager, "disconnect_proxy",
635 G_CALLBACK (disconnect_proxy_cb), NULL);
636
637
638
639 add_action_groups (uimanager);
640 parse_ui (uimanager);
641
642 GtkAccelGroup *accels = gtk_ui_manager_get_accel_group (uimanager);
643 assert (accels);
644
645 GtkWidget *vbox = setup_layout (uimanager);
646 main_window = main_window_new (vbox, 700, 500);
647 gtk_window_add_accel_group (main_window, accels);
648
649 gtk_widget_grab_focus (GTK_WIDGET (view));
650
651 // Setup context ID:s
652 help_msg_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar),
653 "help_msg");
654 image_info_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar),
655 "image_info");
656 g_object_unref (uimanager);
657 }
658
659 int
main(int argc,char * argv[])660 main (int argc,
661 char *argv[])
662 {
663 char **filenames = NULL;
664 GOptionEntry options[] = {
665 {
666 G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY,
667 &filenames, NULL, "[FILE...]"
668 },
669 {NULL}
670 };
671 GOptionContext *ctx = g_option_context_new ("Sample image viewer");
672 g_option_context_add_main_entries (ctx, options, "example1");
673 g_option_context_parse (ctx, &argc, &argv, NULL);
674 g_option_context_free (ctx);
675
676 gtk_init (&argc, &argv);
677
678 view = GTK_ANIM_VIEW (gtk_anim_view_new ());
679
680 dragger = gtk_image_tool_dragger_new (GTK_IMAGE_VIEW (view));
681 selector = gtk_image_tool_selector_new (GTK_IMAGE_VIEW (view));
682 painter = gtk_image_tool_painter_new (GTK_IMAGE_VIEW (view));
683
684 setup_main_window ();
685
686 if (filenames)
687 load_filename (filenames[0]);
688
689 gtk_widget_show_all (GTK_WIDGET (main_window));
690 gtk_main ();
691 return 0;
692 }
693
694