1 // gtk.cpp: Gnome ToolKit graphical user interface, for Gnash.
2 //
3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
4 //   2011 Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 //
20 
21 #ifdef HAVE_CONFIG_H
22 #include "gnashconfig.h"
23 #endif
24 
25 #include "gtksup.h"
26 #include "revno.h"
27 
28 #include <iostream>
29 #include <string>
30 #include <utility>
31 #include <gtk/gtk.h>
32 #include <gdk/gdk.h>
33 #include <gdk/gdkkeysyms.h>
34 #ifdef HAVE_VA_VA_H
35 # include <va/va.h>
36 # include "vaapi_utils.h"
37 #endif
38 #ifdef HAVE_VA_VA_X11_H
39 # include <va/va_x11.h>
40 #endif
41 #ifdef HAVE_X11_X_H
42 # include <X11/keysym.h>
43 # include <gdk/gdkx.h>
44 # include <X11/Xlib.h>
45 #endif
46 
47 #include "log.h"
48 #include "gui.h"
49 #include "rc.h"
50 #include "sound_handler.h"
51 #include "Renderer.h"
52 #include "RunResources.h"
53 #include "VM.h"
54 #include "GnashEnums.h"
55 #include "MediaHandler.h"
56 #include "gtk_canvas.h"
57 
58 #ifdef HAVE_VA_VA_H
59 extern VAStatus va_getDriverName(VADisplay dpy, char **driver_name);
60 #endif
61 
62 namespace gnash
63 {
64 
65 // Forward declarations
66 namespace {
67 
68     // Menu Item callbacks
69     void menuSound(GtkMenuItem *menuitem, gpointer instance);
70     void menuFullscreen(GtkMenuItem *menuitem, gpointer instance);
71     void menuRestart(GtkMenuItem *menuitem, gpointer instance);
72     void menuQuit(GtkMenuItem *menuitem, gpointer instance);
73     void menuPlay(GtkMenuItem *menuitem, gpointer instance);
74     void menuPause(GtkMenuItem *menuitem, gpointer instance);
75     void menuStop(GtkMenuItem *menuitem, gpointer instance);
76     void menuAbout(GtkMenuItem *menuitem, gpointer instance);
77     void menuOpenFile(GtkMenuItem *menuitem, gpointer instance);
78     void menuPreferences(GtkMenuItem *menuitem, gpointer instance);
79     void menuMovieInfo(GtkMenuItem *menuitem, gpointer instance);
80     void menuRefreshView(GtkMenuItem *menuitem, gpointer instance);
81     void menuShowUpdatedRegions(GtkMenuItem *menuitem, gpointer instance);
82     void menuQualityLow(GtkMenuItem *menuitem, gpointer instance);
83     void menuQualityMedium(GtkMenuItem *menuitem, gpointer instance);
84     void menuQualityHigh(GtkMenuItem *menuitem, gpointer instance);
85     void menuQualityBest(GtkMenuItem *menuitem, gpointer instance);
86 
87     void timeoutQuit(gpointer data);
88 
89     // Event handlers
90     gboolean realizeEvent(GtkWidget *widget, GdkEvent *event, gpointer data);
91     gboolean deleteEvent(GtkWidget *widget, GdkEvent *event, gpointer data);
92     gboolean configureEvent(GtkWidget *widget, GdkEventConfigure *event,
93                                     gpointer data);
94     gboolean keyPressEvent(GtkWidget *widget, GdkEventKey *event,
95                                     gpointer data);
96     gboolean keyReleaseEvent(GtkWidget *widget, GdkEventKey *event,
97                                     gpointer data);
98     gboolean buttonPressEvent(GtkWidget *widget, GdkEventButton *event,
99                                        gpointer data);
100     gboolean buttonReleaseEvent(GtkWidget *widget, GdkEventButton *event,
101                                          gpointer data);
102     gboolean mouseWheelEvent(GtkWidget *widget, GdkEventScroll *event,
103                                        gpointer data);
104     gboolean motionNotifyEvent(GtkWidget *widget, GdkEventMotion *event,
105                                         gpointer data);
106     gboolean visibilityNotifyEvent(GtkWidget *widget, GdkEventVisibility *event,
107                                    gpointer data);
108     gint popupHandler(GtkWidget *widget, GdkEvent *event);
109 
110     gint popupHandlerAlt(GtkWidget *widget, GdkEvent *event);
111 
112     void openFile(GtkWidget *widget, gpointer data);
113 
114     void addPixmapDirectory(const gchar *directory);
115 
116     void addGnashIcon(GtkWindow* window);
117 
118     gchar* findPixmapFile(const gchar *filename);
119 
120     GdkPixbuf* createPixbuf(const gchar *filename);
121 
122     key::code gdk_to_gnash_key(guint key);
123 
124     int gdk_to_gnash_modifier(int key);
125 
126     //for use in popupHandler
127     bool _showMenuState;
128 
129 }
130 
~GtkGui()131 GtkGui::~GtkGui()
132 {
133 }
134 
GtkGui(unsigned long xid,float scale,bool loop,RunResources & r)135 GtkGui::GtkGui(unsigned long xid, float scale, bool loop, RunResources& r)
136     :
137     Gui(xid, scale, loop, r)
138     ,_window(0)
139     ,_resumeButton(0)
140     ,_overlay(0)
141     ,_canvas(0)
142     ,_visible(true)
143     ,_popup_menu(0)
144     ,_popup_menu_alt(0)
145     ,_menubar(0)
146     ,_vbox(0)
147     ,_exiting(false)
148     ,_advanceSourceTimer(0)
149 {
150 }
151 
152 bool
init(int argc,char ** argv[])153 GtkGui::init(int argc, char **argv[])
154 {
155 #ifdef HAVE_X11
156     if (!XInitThreads()) {
157         log_error(_("Failed to initialize X threading support\n"));
158         return false;
159     }
160 #endif
161     gtk_init(&argc, argv);
162 
163     addPixmapDirectory (PKGDATADIR);
164 
165     if (_xid) {
166 #ifdef _WIN32
167         _window = gtk_plug_new((void *)_xid);
168 #else
169         _window = gtk_plug_new(_xid);
170 #endif
171         // log_debug("Created XEmbedded window");
172     } else {
173         _window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
174         // log_debug ("Created top level window");
175     }
176 
177     addGnashIcon(GTK_WINDOW(_window));
178 
179     std::string hwaccel = _runResources.getHWAccelBackend();
180     std::string renderer = _runResources.getRenderBackend();
181 
182     if (renderer == "opengl") {
183         // See if our X11 server supports the DRI extension, otherwise
184         // there is no point in trying to use OpenGL.
185         bool dri = false;
186         if (checkX11Extension("DRI")) {
187             log_debug("DRI extension found");
188             dri = true;
189         }
190         bool glx = false;
191         // See if our X11 server supports the GLX extension, otherwise
192         // there is no point in trying to use OpenGL.
193         if (checkX11Extension("GLX")) {
194             log_debug("GLX extension found");
195             glx = true;
196         }
197         // If we don't have these extensions, don't bother with OpenGl,
198         // drop back to AGG.
199         if (!glx || !dri) {
200             log_error(_("This system lacks a hardware OpenGL driver!"));
201         }
202     }
203 
204     _canvas = gnash_canvas_new();
205     gnash_canvas_setup(GNASH_CANVAS(_canvas), hwaccel, renderer, argc, argv);
206     // Increase reference count to prevent its destruction (which could happen
207     // later if we remove it from its container).
208     g_object_ref(G_OBJECT(_canvas));
209 
210     _resumeButton = gtk_button_new();
211     gtk_container_add(GTK_CONTAINER(_resumeButton),
212             gtk_label_new(_("Click to play")));
213     gtk_widget_show_all(_resumeButton);
214 
215     // Same here.
216     g_object_ref(G_OBJECT(_resumeButton));
217 
218     // This callback indirectly results in playHook() being called.
219     g_signal_connect(_resumeButton, "clicked", G_CALLBACK(menuPlay), this);
220 
221     createMenu();
222     createMenuAlt();
223 
224     // A vertical box is used to allow display of the menu bar and paused widget
225     _vbox = gtk_vbox_new(FALSE, 0);
226     gtk_widget_show(_vbox);
227     gtk_container_add(GTK_CONTAINER(_window), _vbox);
228 
229 #if defined(USE_MENUS)
230     if ( ! _xid ) {
231         createMenuBar();
232     }
233 #endif
234 
235     gtk_box_pack_start(GTK_BOX(_vbox), _canvas, TRUE, TRUE, 0);
236 
237     setupEvents();
238 
239     gtk_widget_realize(_window);
240     gtk_widget_show(_canvas);
241     gtk_widget_show(_window);
242 
243     _renderer = gnash_canvas_get_renderer(GNASH_CANVAS(_canvas));
244     _runResources.setRenderer(_renderer);
245 
246     // The first time stop() was called, stopHook() might not have had a chance
247     // to do anything, because GTK+ wasn't garanteed to be initialised.
248     //if (isStopped()) stopHook();
249 
250     return true;
251 }
252 
253 bool
run()254 GtkGui::run()
255 {
256     // Kick-start before setting the interval timeout
257     advance_movie(this);
258 
259     if (!_exiting)
260     {
261         gtk_main();
262     }
263     gtk_widget_destroy(_window);
264     return true;
265 }
266 
267 void
setTimeout(unsigned int timeout)268 GtkGui::setTimeout(unsigned int timeout)
269 {
270     g_timeout_add(timeout, (GSourceFunc)timeoutQuit, this);
271 }
272 
273 
274 void
error(const std::string & msg)275 GtkGui::error(const std::string& msg)
276 {
277 
278     RcInitFile& rcfile = RcInitFile::getDefaultInstance();
279 
280     if (!rcfile.popupMessages()) {
281         return;
282     }
283 
284     GtkWidget* popup = gtk_dialog_new_with_buttons("Gnash Error",
285             GTK_WINDOW(_window),
286             static_cast<GtkDialogFlags>(GTK_DIALOG_DESTROY_WITH_PARENT),
287             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, nullptr);
288 
289     g_signal_connect_swapped(popup, "response", G_CALLBACK(gtk_widget_destroy),
290             popup);
291 
292 #if GTK_CHECK_VERSION(2,14,0)
293     GtkWidget* content = gtk_dialog_get_content_area(GTK_DIALOG(popup));
294 #else
295     GtkWidget* content = GTK_DIALOG(popup)->vbox;
296 #endif
297 
298     GtkWidget* label = gtk_label_new(msg.c_str());
299     gtk_widget_set_size_request(label, 400, 200);
300     gtk_label_set_line_wrap(GTK_LABEL(label), true);
301     gtk_box_pack_start(GTK_BOX(content), label, false, false, 0);
302     gtk_widget_show_all(popup);
303 }
304 
305 void
setClipboard(const std::string & copy)306 GtkGui::setClipboard(const std::string& copy)
307 {
308     GtkClipboard* cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
309     gtk_clipboard_clear(cb);
310     gtk_clipboard_set_text(cb, copy.c_str(), copy.size());
311 }
312 
313 void
setFullscreen()314 GtkGui::setFullscreen()
315 {
316 
317     if (_fullscreen) return;
318 
319     // Plugin
320     if (_xid) {
321 
322         // Create new window and make fullscreen
323         _overlay = gtk_window_new (GTK_WINDOW_TOPLEVEL);
324         addGnashIcon(GTK_WINDOW(_overlay));
325         gtk_window_fullscreen(GTK_WINDOW(_overlay));
326 
327         // Reparent drawing area from GtkPlug to fullscreen window
328         gtk_widget_realize(_overlay);
329         gtk_widget_reparent(_vbox, _overlay);
330 
331         // Apply key event callbacks to the new window.
332         setupWindowEvents();
333 
334         gtk_widget_show(_overlay);
335     }
336 
337     // Stand-alone
338     else {
339 
340         // This is a hack to fix another hack (see createWindow). If the minimum
341         // size (size request) is larger than the screen, fullscreen messes up.
342         // This way allows the drawing area to be shrunk, which is what we
343         // really want, but not only after we've gone fullscreen.
344         // It could be a good hack if it were done earlier.
345         // There really doesn't seem to be a proper way of setting the
346         // starting size of a widget but allowing it to be shrunk.
347         gtk_widget_set_size_request(_canvas, -1, -1);
348         gtk_window_fullscreen(GTK_WINDOW(_window));
349 
350         showMenu(false);
351     }
352 
353     _fullscreen = true;
354 }
355 
356 void
unsetFullscreen()357 GtkGui::unsetFullscreen()
358 {
359     if (!_fullscreen) return;
360 
361     // Plugin
362     if (_xid) {
363         gtk_widget_reparent(_vbox, _window);
364 
365         // Apply key event callbacks to the plugin instance.
366         setupWindowEvents();
367         if (_overlay) {
368             gtk_widget_destroy(_overlay);
369         }
370     }
371     else {
372         // Stand-alone
373         gtk_window_unfullscreen(GTK_WINDOW(_window));
374         showMenu(true);
375     }
376 
377     _fullscreen = false;
378 }
379 
380 void
hideMenu()381 GtkGui::hideMenu()
382 {
383     // Not showing menu anyway if it's a plugin
384     if (_fullscreen || _xid) return;
385 
386     // Stand-alone
387     showMenu(false);
388 }
389 
390 
391 void
setCursor(gnash_cursor_type newcursor)392 GtkGui::setCursor(gnash_cursor_type newcursor)
393 {
394 
395     if (!_mouseShown) return;
396 
397     GdkCursorType cursortype;
398 
399     switch (newcursor)
400     {
401         case CURSOR_HAND:
402             cursortype = GDK_HAND2;
403             break;
404         case CURSOR_INPUT:
405             cursortype = GDK_XTERM;
406             break;
407         default:
408             cursortype = GDK_LAST_CURSOR;
409     }
410 
411     GdkCursor* gdkcursor = nullptr;
412 
413     if (cursortype != GDK_LAST_CURSOR) {
414          gdkcursor = gdk_cursor_new(cursortype);
415     }
416 
417     // The parent of _drawingArea is different for the plugin in fullscreen
418     gdk_window_set_cursor(_canvas->window, gdkcursor);
419 
420     if (gdkcursor) {
421         gdk_cursor_unref(gdkcursor);
422     }
423 }
424 
425 // Returns whether the mouse was visible before call.
426 bool
showMouse(bool show)427 GtkGui::showMouse(bool show)
428 {
429 
430     bool state = _mouseShown;
431 
432     RcInitFile& rcfile = RcInitFile::getDefaultInstance();
433 
434     // Whether to forcibly show the mouse pointer even if the SWF file
435     // disables it. This allows touchscreen based SWF files to
436     // work on a normal non-touchscreen desktop.
437     if (rcfile.showMouse()) {
438         return true;
439     } else {
440         if (show == _mouseShown) {
441             return state;
442         }
443     }
444 
445     if (!show) {
446         GdkPixmap *pixmap;
447         GdkColor *color;
448 
449         color = g_new0(GdkColor, 1);
450         pixmap = gdk_pixmap_new(nullptr, 1, 1, 1);
451         GdkCursor* cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap,
452                                                     color, color, 0, 0);
453 
454         gdk_window_set_cursor (_canvas->window, cursor);
455 
456         g_free(color);
457         g_object_unref(pixmap);
458         gdk_cursor_unref(cursor);
459 
460         _mouseShown = false;
461 
462     }
463     else if (show) {
464         _mouseShown = true;
465     }
466 
467     return state;
468 }
469 
470 void
showMenu(bool show)471 GtkGui::showMenu(bool show)
472 {
473     RcInitFile& rcfile = RcInitFile::getDefaultInstance();
474 
475     // If we allow the swf author to set Stage.showMenu
476     if( !rcfile.ignoreShowMenu() ) {
477         _showMenuState = show;
478     }
479 
480 #ifdef USE_MENUS
481     if (!_menubar) {
482         return;
483     }
484 
485     if (show) {
486         gtk_widget_show(_menubar);
487     } else {
488         gtk_widget_hide(_menubar);
489     }
490 #endif
491 
492 }
493 
494 double
getPixelAspectRatio() const495 GtkGui::getPixelAspectRatio() const
496 {
497     GdkScreen* screen = gdk_screen_get_default();
498 
499     const std::pair<int, int> res = screenResolution();
500 
501     // Screen size / number of pixels = pixel size.
502     // The physical size of the screen may be reported wrongly by gdk (from X),
503     // but it's the best we have. This method agrees with the pp in my case.
504     double pixelAspectRatio =
505         (gdk_screen_get_height_mm(screen) / static_cast<double>(res.first)) /
506         (gdk_screen_get_width_mm(screen) / static_cast<double>(res.second));
507     return pixelAspectRatio;
508 }
509 
510 std::pair<int, int>
screenResolution() const511 GtkGui::screenResolution() const
512 {
513     return std::make_pair(gdk_screen_width(), gdk_screen_height());
514 }
515 
516 double
getScreenDPI() const517 GtkGui::getScreenDPI() const
518 {
519 #if GTK_CHECK_VERSION(2,10,0)
520     GdkScreen* screen = gdk_screen_get_default();
521     return gdk_screen_get_resolution(screen);
522 #else
523     return 0;
524 #endif
525 }
526 
527 // private
528 void
setupWindowEvents()529 GtkGui::setupWindowEvents()
530 {
531     g_signal_connect(gtk_widget_get_toplevel(_canvas),
532             "delete_event", G_CALLBACK(deleteEvent), this);
533     g_signal_connect(gtk_widget_get_toplevel(_canvas),
534             "key_press_event", G_CALLBACK(keyPressEvent), this);
535     g_signal_connect(gtk_widget_get_toplevel(_canvas),
536             "key_release_event", G_CALLBACK(keyReleaseEvent), this);
537 }
538 
539 // public virtual
540 bool
setupEvents()541 GtkGui::setupEvents()
542 {
543 
544     setupWindowEvents();
545 
546     gtk_widget_add_events(_canvas, GDK_EXPOSURE_MASK
547                         | GDK_VISIBILITY_NOTIFY_MASK
548                         | GDK_BUTTON_PRESS_MASK
549                         | GDK_BUTTON_RELEASE_MASK
550                         | GDK_KEY_RELEASE_MASK
551                         | GDK_KEY_PRESS_MASK
552                         | GDK_POINTER_MOTION_MASK);
553 
554     _showMenuState = true; //Default for showMenu
555 
556     g_signal_connect_swapped(_canvas, "button_press_event",
557                             G_CALLBACK(popupHandler), _popup_menu);
558 
559     g_signal_connect_swapped(_canvas, "button_press_event",
560                             G_CALLBACK(popupHandlerAlt), _popup_menu_alt);
561 
562     g_signal_connect(_canvas, "button_press_event",
563                    G_CALLBACK(buttonPressEvent), this);
564     g_signal_connect(_canvas, "button_release_event",
565                    G_CALLBACK(buttonReleaseEvent), this);
566     g_signal_connect(_canvas, "motion_notify_event",
567                    G_CALLBACK(motionNotifyEvent), this);
568     g_signal_connect(_canvas, "scroll_event",
569                    G_CALLBACK(mouseWheelEvent), this);
570     g_signal_connect(_canvas, "visibility-notify-event",
571                    G_CALLBACK(visibilityNotifyEvent), this);
572 
573     g_signal_connect_after(_canvas, "realize",
574                          G_CALLBACK (realizeEvent), nullptr);
575 
576     // connect_after because we are going to cause a rendering and the canvas
577     // widget should have had a chance to update the size of the render area
578     g_signal_connect_after(_canvas, "configure_event",
579                    G_CALLBACK (configureEvent), this);
580 
581     return true;
582 }
583 
584 void
grabFocus()585 GtkGui::grabFocus()
586 {
587     gtk_widget_grab_focus(GTK_WIDGET(_canvas));
588 }
589 
590 void
quitUI()591 GtkGui::quitUI()
592 {
593     stopAdvanceTimer();
594     // Unregister the callback registered by setTimeout, if any. This also
595     // removes other callbacks, but we're about to go away anyway.
596     while (g_source_remove_by_user_data(this)) {}
597 
598     _exiting = true;
599     if (gtk_main_level() > 0)
600     {
601         gtk_main_quit();
602     }
603 }
604 
605 /*private*/
606 void
startAdvanceTimer()607 GtkGui::startAdvanceTimer()
608 {
609     stopAdvanceTimer();
610 
611     _advanceSourceTimer = g_timeout_add_full(G_PRIORITY_LOW, _interval,
612             (GSourceFunc)advance_movie, this, nullptr);
613 
614     log_debug(_("Advance interval timer set to %d ms (~ %d FPS)"),
615             _interval, _interval ? 1000/_interval : 1000);
616 }
617 
618 /*private*/
619 void
stopAdvanceTimer()620 GtkGui::stopAdvanceTimer()
621 {
622     if (_advanceSourceTimer)
623     {
624         g_source_remove(_advanceSourceTimer);
625         _advanceSourceTimer = 0;
626     }
627 }
628 
629 void
setInterval(unsigned int interval)630 GtkGui::setInterval(unsigned int interval)
631 {
632     _interval = interval;
633 
634     if ( ! isStopped() ) {
635         startAdvanceTimer();
636     }
637 }
638 
639 ///////////////////////////////////////////////////////////////////////////////
640 ///////////////////////////////////////////////////////////////////////////////
641 ///                                                                         ///
642 ///                             Widget functions                            ///
643 ///                                                                         ///
644 ///////////////////////////////////////////////////////////////////////////////
645 ///////////////////////////////////////////////////////////////////////////////
646 
647 // Setup the menu bar for the top of the window frame.
648 bool
createMenuBar()649 GtkGui::createMenuBar()
650 {
651     _menubar = gtk_menu_bar_new();
652     gtk_widget_show(_menubar);
653     gtk_box_pack_start(GTK_BOX(_vbox), _menubar, FALSE, FALSE, 0);
654 
655     createFileMenu(_menubar);
656     createEditMenu(_menubar);
657     createViewMenu(_menubar);
658     createControlMenu(_menubar);
659     createHelpMenu(_menubar);
660 
661     return true;
662 }
663 
664 bool
createMenu()665 GtkGui::createMenu()
666 {
667     // A menu that pops up (normally) on a right-mouse click.
668 
669     _popup_menu = GTK_MENU(gtk_menu_new());
670 
671 #ifdef USE_MENUS
672     // If menus are disabled, these are not added to the popup menu
673     // either.
674     createFileMenu(GTK_WIDGET(_popup_menu));
675     createEditMenu(GTK_WIDGET(_popup_menu));
676     createViewMenu(GTK_WIDGET(_popup_menu));
677     createControlMenu(GTK_WIDGET(_popup_menu));
678 #endif
679     createHelpMenu(GTK_WIDGET(_popup_menu));
680 
681     GtkWidget *separator1 = gtk_separator_menu_item_new();
682     gtk_widget_show(separator1);
683     gtk_container_add (GTK_CONTAINER(_popup_menu), separator1);
684 
685     /// The sound handler is initialized after the Gui is created, and
686     /// may be disabled or enabled dynamically.
687     GtkCheckMenuItem *menusound =
688         GTK_CHECK_MENU_ITEM(gtk_check_menu_item_new_with_label(_("Sound")));
689     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menusound), TRUE);
690     gtk_menu_append(_popup_menu, GTK_WIDGET(menusound));
691     gtk_widget_show(GTK_WIDGET(menusound));
692     g_signal_connect(menusound, "activate", G_CALLBACK(menuSound), this);
693 
694     GtkWidget *separator2 = gtk_separator_menu_item_new();
695     gtk_widget_show(separator2);
696     gtk_container_add (GTK_CONTAINER(_popup_menu), separator2);
697 
698     GtkWidget *quit = gtk_image_menu_item_new_from_stock("gtk-quit", 0);
699     gtk_widget_show(quit);
700     gtk_container_add(GTK_CONTAINER(_popup_menu), quit);
701     g_signal_connect(quit, "activate", G_CALLBACK(menuQuit), this);
702 
703     return true;
704 }
705 
706 bool
createMenuAlt()707 GtkGui::createMenuAlt()
708 {
709     //An alternative short version of the popup menu
710 
711     _popup_menu_alt = GTK_MENU(gtk_menu_new());
712 
713 #ifdef USE_MENUS
714     // If menus are disabled, these are not added to the popup menu
715     // either.
716     createEditMenu(GTK_WIDGET(_popup_menu_alt));
717 #endif
718     createHelpMenu(GTK_WIDGET(_popup_menu_alt));
719 
720     GtkWidget *separator1 = gtk_separator_menu_item_new();
721     gtk_widget_show(separator1);
722     gtk_container_add (GTK_CONTAINER(_popup_menu_alt), separator1);
723 
724     GtkWidget *quit = gtk_image_menu_item_new_from_stock("gtk-quit", 0);
725     gtk_widget_show(quit);
726     gtk_container_add(GTK_CONTAINER(_popup_menu_alt), quit);
727     g_signal_connect(quit, "activate", G_CALLBACK(menuQuit), this);
728 
729     return true;
730 }
731 
732 void
resizeWindow(int width,int height)733 GtkGui::resizeWindow(int width, int height)
734 {
735     // log_debug("GtkGui: Window resize request received");
736 
737     if (!_xid) {
738 
739         // This sets the *minimum* size for the drawing area and thus will
740         // also resize the window if needed.
741         // Advantage: The window is sized correctly, no matter what other
742         // widgets are visible
743         // Disadvantage: The window will never be shrinked, which is bad.
744         gtk_widget_set_size_request(_canvas, width, height);
745     }
746 }
747 
748 bool
createWindow(const char * title,int width,int height,int xPosition,int yPosition)749 GtkGui::createWindow(const char *title, int width, int height,
750                      int xPosition, int yPosition)
751 {
752 // First call the old createWindow function and then set the title.
753 // In case there's some need to not setting the title.
754     bool ret = createWindow(width, height);
755     gtk_window_set_title(GTK_WINDOW(_window), title);
756 
757     // Move the window to correct position if requested by user.
758     int x, y;
759     gtk_window_get_position(GTK_WINDOW(_window), &x, &y);
760     if (xPosition > -1) x = xPosition; // -1 so we can tell if user requested window move
761     if (yPosition > -1) y = yPosition; // as 0 is also a valid coordinate.
762     gtk_window_move(GTK_WINDOW(_window), x, y);
763 
764     if (!_xid) {
765 
766         // This sets the *minimum* size for the drawing area and thus will
767         // also resize the window.
768         // Advantage: The window is sized correctly, no matter what other
769         // widgets are visible
770         // Disadvantage: The window cannot be shrinked, which is bad.
771         gtk_widget_set_size_request(_canvas, width, height);
772 
773     }
774     return ret;
775 }
776 
777 #ifdef USE_SWFTREE
778 
779 // This creates a GtkTree model for displaying movie info.
780 GtkTreeModel*
makeTreeModel(const std::unique_ptr<movie_root::InfoTree> & treepointer)781 GtkGui::makeTreeModel(const std::unique_ptr<movie_root::InfoTree>& treepointer)
782 {
783 
784     const movie_root::InfoTree& info = *treepointer;
785 
786     enum
787     {
788         STRING1_COLUMN,
789         STRING2_COLUMN,
790         NUM_COLUMNS
791     };
792 
793     GtkTreeStore *model = gtk_tree_store_new (NUM_COLUMNS,
794                          G_TYPE_STRING, G_TYPE_STRING);
795 
796     GtkTreeIter iter = {0, 0, 0, 0};
797     GtkTreeIter child_iter = {0, 0, 0, 0};
798     GtkTreeIter parent_iter = {0, 0, 0, 0};
799 
800     // Depth within the *GTK* tree.
801     int depth = 0;
802 
803     assert(info.depth(info.begin()) == 0); // seems assumed in the code below
804     for (movie_root::InfoTree::iterator i = info.begin(), e = info.end();
805             i != e; ++i) {
806 
807         const movie_root::InfoTree::value_type& p = *i;
808 
809         std::ostringstream os;
810         os << info.depth(i);
811 
812         int newdepth = info.depth(i);
813 
814         if (newdepth > depth) {
815             assert(newdepth == depth+1);
816             depth++;
817             iter=child_iter;
818         }
819 
820         if (newdepth < depth) {
821             int gap = depth - newdepth;
822             depth = newdepth;
823             while (gap--) {
824                 gtk_tree_model_iter_parent(GTK_TREE_MODEL(model),
825                         &parent_iter, &iter);
826                 iter = parent_iter;
827             }
828         }
829 
830         //Read in data from present node
831         if (depth == 0) gtk_tree_store_append(model, &child_iter, nullptr);
832         else gtk_tree_store_append(model, &child_iter, &iter);
833 
834         gtk_tree_store_set(model, &child_iter,
835                           STRING1_COLUMN, p.first.c_str(),   // "Variable"
836                           STRING2_COLUMN, p.second.c_str(),  // "Value"
837                           -1);
838 
839     }
840 
841     return GTK_TREE_MODEL(model);
842 
843 }
844 
845 #endif
846 
847 
848 ///////////////////////////////////////////////////////////////////////////////
849 ///////////////////////////////////////////////////////////////////////////////
850 ///                                                                         ///
851 ///                             Rendering stuff                             ///
852 ///                                                                         ///
853 ///////////////////////////////////////////////////////////////////////////////
854 ///////////////////////////////////////////////////////////////////////////////
855 
856 bool
createWindow(int width,int height)857 GtkGui::createWindow(int width, int height)
858 {
859     assert(_width>0);
860     assert(_height>0);
861 
862     _width = width;
863     _height = height;
864 
865     _validbounds.setTo(0, 0, _width, _height);
866 
867     return true;
868 }
869 
870 void
beforeRendering()871 GtkGui::beforeRendering()
872 {
873     gnash_canvas_before_rendering(GNASH_CANVAS(_canvas), getStage());
874 }
875 
876 void
renderBuffer()877 GtkGui::renderBuffer()
878 {
879     gdk_window_process_updates(_canvas->window, false);
880 }
881 
882 void
setInvalidatedRegions(const InvalidatedRanges & ranges)883 GtkGui::setInvalidatedRegions(const InvalidatedRanges& ranges)
884 {
885     // forward to renderer
886     //
887     // Why? Why have the region been invalidated ??
888     //   A: I don't understand this question.
889     // Was the renderer offscreen buffer also invalidated
890     // (need to rerender)?
891     //   A: Yes.
892     // Was only the 'onscreen' buffer be invalidated (no need to rerender,
893     // just to blit) ??
894     //   A: I don't understand this question.
895     //
896     // Clarification: the render (optionally) only draws to the invalidated
897     // (i.e., changed) part of the buffer. So we need to tell the renderer
898     // where that is. The renderer draws to the offscreen buffer. (Although
899     // that should be obvious!)
900     _renderer->set_invalidated_regions(ranges);
901 
902     for (unsigned rno=0; rno<ranges.size(); rno++) {
903         geometry::Range2d<int> bounds = Intersection(
904             _renderer->world_to_pixel(ranges.getRange(rno)),
905             _validbounds);
906 
907         // it may happen that a particular range is out of the screen, which
908         // will lead to bounds==null.
909         if (bounds.isNull()) continue;
910 
911         assert(bounds.isFinite());
912 
913         GdkRectangle rect;
914         rect.x = bounds.getMinX();
915         rect.y = bounds.getMinY();
916         rect.width = bounds.width();
917         rect.height = bounds.height();
918 
919         // We add the rectangle to the part of the window to be redrawn
920         // (also known as the "clipping" or "damaged" area). in renderBuffer(),
921         // we force a redraw.
922         gdk_window_invalidate_rect(_canvas->window, &rect, false);
923     }
924 
925 }
926 
927 
928 ///////////////////////////////////////////////////////////////////////////////
929 ///////////////////////////////////////////////////////////////////////////////
930 ///                                                                         ///
931 ///                             Dialogues                                   ///
932 ///                                                                         ///
933 ///////////////////////////////////////////////////////////////////////////////
934 ///////////////////////////////////////////////////////////////////////////////
935 
936 namespace {
937 
938 class PreferencesDialog
939 {
940 
941 public:
942 
943     PreferencesDialog(GtkWidget* window);
944 
945     void show();
946 
947 private:
948 
949     // A struct containing pointers to widgets for passing preference
950     // data from the dialogue
951     struct PrefWidgets {
952         GtkWidget *soundToggle;
953         GtkWidget *actionDumpToggle;
954         GtkWidget *parserDumpToggle;
955         GtkWidget *malformedSWFToggle;
956         GtkWidget *ASCodingErrorToggle;
957         GtkWidget *logfileName;
958         GtkWidget *writeLogToggle;
959         GtkWidget *verbosityScale;
960         GtkWidget *streamsTimeoutScale;
961         GtkWidget *localDomainToggle;
962         GtkWidget *localHostToggle;
963         GtkWidget *solReadOnlyToggle;
964         GtkWidget *solLocalDomainToggle;
965         GtkWidget *localConnectionToggle;
966         GtkWidget *insecureSSLToggle;
967         GtkWidget *solSandbox;
968         GtkWidget *osText;
969         GtkWidget *versionText;
970         GtkWidget *urlOpenerText;
971         GtkWidget *librarySize;
972         GtkWidget *startStoppedToggle;
973         GtkWidget *mediaDir;
974         GtkWidget *saveStreamingMediaToggle;
975         GtkWidget *saveLoadedMediaToggle;
976         GtkWidget *scriptsTimeout;
977         GtkWidget *scriptsRecursionLimit;
978         GtkWidget *lockScriptLimitsToggle;
979 
PrefWidgetsgnash::__anonded7254c0311::PreferencesDialog::PrefWidgets980         PrefWidgets()
981             :
982             soundToggle(0),
983             actionDumpToggle(0),
984             parserDumpToggle(0),
985             malformedSWFToggle(0),
986             ASCodingErrorToggle(0),
987             logfileName(0),
988             writeLogToggle(0),
989             verbosityScale(0),
990             streamsTimeoutScale(0),
991             localDomainToggle(0),
992             localHostToggle(0),
993             solReadOnlyToggle(0),
994             solLocalDomainToggle(0),
995             localConnectionToggle(0),
996             insecureSSLToggle(0),
997             solSandbox(0),
998             osText(0),
999             versionText(0),
1000             urlOpenerText(0),
1001             librarySize(0),
1002             startStoppedToggle(0),
1003             mediaDir(0),
1004             saveStreamingMediaToggle(0),
1005             saveLoadedMediaToggle(0),
1006             scriptsTimeout(0),
1007             scriptsRecursionLimit(0),
1008             lockScriptLimitsToggle(0)
1009         {}
1010 
1011     };
1012 
1013     static void handlePrefs(GtkWidget* widget, gint response, gpointer data);
1014 
1015     /// Network Tab
1016     void addNetworkTab();
1017 
1018     /// Logging Tab
1019     void addLoggingTab();
1020 
1021     void addSecurityTab();
1022 
1023     void addMediaTab();
1024 
1025     void addPlayerTab();
1026 
1027     GtkWidget* _window;
1028 
1029     PrefWidgets* _prefs;
1030 
1031     RcInitFile& _rcfile;
1032 
1033     GtkWidget* _prefsDialog;
1034 
1035     GtkWidget* _notebook;
1036 
1037 };
1038 
1039 
1040 // Callback to read values from the preferences dialogue and set rcfile
1041 // values accordingly.
1042 void
handlePrefs(GtkWidget * dialog,gint response,gpointer data)1043 PreferencesDialog::handlePrefs(GtkWidget* dialog, gint response, gpointer data)
1044 {
1045 
1046     PrefWidgets *prefs = static_cast<PrefWidgets*>(data);
1047 
1048     RcInitFile& _rcfile = RcInitFile::getDefaultInstance();
1049 
1050     if (response == GTK_RESPONSE_OK) {
1051 
1052         // For getting from const gchar* to std::string&
1053         std::string tmp;
1054 
1055         if (prefs->soundToggle) {
1056             _rcfile.useSound(gtk_toggle_button_get_active(
1057                         GTK_TOGGLE_BUTTON(prefs->soundToggle)));
1058         }
1059 
1060         if (prefs->saveLoadedMediaToggle) {
1061             _rcfile.saveLoadedMedia(
1062                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
1063                         prefs->saveLoadedMediaToggle)));
1064         }
1065 
1066         if (prefs->saveStreamingMediaToggle) {
1067             _rcfile.saveStreamingMedia(
1068                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
1069                         prefs->saveStreamingMediaToggle)));
1070         }
1071 
1072         if (prefs->mediaDir) {
1073             tmp = gtk_entry_get_text(GTK_ENTRY(prefs->mediaDir));
1074             _rcfile.setMediaDir(tmp);
1075         }
1076 
1077         if (prefs->actionDumpToggle) {
1078             _rcfile.useActionDump(
1079                 gtk_toggle_button_get_active(
1080                     GTK_TOGGLE_BUTTON(prefs->actionDumpToggle)));
1081         }
1082 
1083         if (prefs->parserDumpToggle) {
1084             _rcfile.useParserDump(
1085                 gtk_toggle_button_get_active(
1086                     GTK_TOGGLE_BUTTON(prefs->parserDumpToggle)));
1087         }
1088 
1089         if ( prefs->logfileName ) {
1090             tmp = gtk_entry_get_text(GTK_ENTRY(prefs->logfileName));
1091             _rcfile.setDebugLog(tmp);
1092         }
1093 
1094         if ( prefs->writeLogToggle ) {
1095             _rcfile.useWriteLog(
1096                 gtk_toggle_button_get_active(
1097                     GTK_TOGGLE_BUTTON(prefs->writeLogToggle)));
1098         }
1099 
1100         if ( prefs->verbosityScale ) {
1101             _rcfile.verbosityLevel(static_cast<int>(
1102                 gtk_range_get_value(GTK_RANGE(prefs->verbosityScale))));
1103         }
1104 
1105         if ( prefs->streamsTimeoutScale ) {
1106             _rcfile.setStreamsTimeout(
1107                 gtk_spin_button_get_value_as_int(
1108                     GTK_SPIN_BUTTON(prefs->streamsTimeoutScale)));
1109         }
1110 
1111         if ( prefs->ASCodingErrorToggle ) {
1112             _rcfile.showASCodingErrors(
1113                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
1114                         prefs->ASCodingErrorToggle)));
1115         }
1116 
1117         if ( prefs->malformedSWFToggle ) {
1118             _rcfile.showMalformedSWFErrors(
1119                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
1120                         prefs->malformedSWFToggle)));
1121         }
1122 
1123         if ( prefs->localHostToggle ) {
1124             _rcfile.useLocalHost(
1125                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
1126                         prefs->localHostToggle)));
1127         }
1128 
1129         if ( prefs->localDomainToggle ) {
1130             _rcfile.useLocalDomain(
1131                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
1132                         prefs->localDomainToggle)));
1133         }
1134 
1135         if ( prefs->solLocalDomainToggle ) {
1136             _rcfile.setSOLLocalDomain(
1137                 gtk_toggle_button_get_active(
1138                     GTK_TOGGLE_BUTTON(prefs->solLocalDomainToggle)));
1139         }
1140 
1141         if ( prefs->solReadOnlyToggle ) {
1142             _rcfile.setSOLReadOnly(
1143                 gtk_toggle_button_get_active(
1144                     GTK_TOGGLE_BUTTON(prefs->solReadOnlyToggle)));
1145         }
1146 
1147         if ( prefs->localConnectionToggle ) {
1148             _rcfile.setLocalConnection(
1149                 gtk_toggle_button_get_active(
1150                     GTK_TOGGLE_BUTTON(prefs->localConnectionToggle)));
1151         }
1152 
1153         if ( prefs->insecureSSLToggle ) {
1154             _rcfile.insecureSSL(
1155                 gtk_toggle_button_get_active(
1156                     GTK_TOGGLE_BUTTON(prefs->insecureSSLToggle)));
1157         }
1158 
1159         if ( prefs->solSandbox ) {
1160             tmp = gtk_entry_get_text(GTK_ENTRY(prefs->solSandbox));
1161             _rcfile.setSOLSafeDir(tmp);
1162         }
1163 
1164         if ( prefs->osText ) {
1165             tmp = gtk_entry_get_text(GTK_ENTRY(prefs->osText));
1166             _rcfile.setFlashSystemOS(tmp);
1167         }
1168 
1169         if ( prefs->versionText ) {
1170             tmp = gtk_entry_get_text(GTK_ENTRY(prefs->versionText));
1171             _rcfile.setFlashVersionString(tmp);
1172         }
1173 
1174         if ( prefs->librarySize ) {
1175             _rcfile.setMovieLibraryLimit(
1176                 gtk_spin_button_get_value_as_int(
1177                     GTK_SPIN_BUTTON(prefs->librarySize)));
1178         }
1179 
1180         if ( prefs->startStoppedToggle ) {
1181             _rcfile.startStopped(
1182                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(prefs->startStoppedToggle)));
1183         }
1184 
1185         if ( prefs->scriptsTimeout ) {
1186             _rcfile.setScriptsTimeout(
1187                 gtk_spin_button_get_value_as_int(
1188                     GTK_SPIN_BUTTON(prefs->scriptsTimeout)));
1189         }
1190 
1191         if ( prefs->scriptsRecursionLimit ) {
1192             _rcfile.setScriptsRecursionLimit(
1193                 gtk_spin_button_get_value_as_int(
1194                     GTK_SPIN_BUTTON(prefs->scriptsRecursionLimit)));
1195         }
1196 
1197         if ( prefs->lockScriptLimitsToggle ) {
1198             _rcfile.lockScriptLimits(
1199                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(prefs->lockScriptLimitsToggle)));
1200         }
1201 
1202         if ( prefs->urlOpenerText ) {
1203             tmp = gtk_entry_get_text(GTK_ENTRY(prefs->urlOpenerText));
1204             _rcfile.setURLOpenerFormat(tmp);
1205         }
1206 
1207 
1208         // Let _rcfile decide which file to update: generally the file
1209         // being used if specified in GNASHRC environment variable, or in
1210         // the user's home directory if that can be found.
1211         // TODO: We can also specify here which file should be written
1212         // by passing that instead. How might that best be done?
1213         _rcfile.updateFile();
1214 
1215         // Close the window when 'ok' is clicked
1216         gtk_widget_destroy(dialog);
1217     }
1218 
1219     else if (response == GTK_RESPONSE_CANCEL) {
1220         // Close the window when 'cancel' is clicked
1221         gtk_widget_destroy(dialog);
1222     }
1223 
1224     delete prefs;
1225 }
1226 
1227 void
show()1228 PreferencesDialog::show()
1229 {
1230     gtk_widget_show_all(_prefsDialog);
1231 }
1232 
PreferencesDialog(GtkWidget * window)1233 PreferencesDialog::PreferencesDialog(GtkWidget* window)
1234     :
1235     _window(window),
1236     _prefs(new PrefWidgets),
1237     _rcfile(RcInitFile::getDefaultInstance())
1238 {
1239     // Create top-level window
1240     _prefsDialog = gtk_dialog_new_with_buttons(
1241                     _("Gnash preferences"),
1242                     GTK_WINDOW(_window),
1243                     // Needs an explicit cast in C++
1244                     GtkDialogFlags(
1245                     GTK_DIALOG_DESTROY_WITH_PARENT |
1246                     GTK_DIALOG_NO_SEPARATOR),
1247                     // The buttons and their response codes:
1248                     GTK_STOCK_OK, GTK_RESPONSE_OK,
1249                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1250                     nullptr);
1251     // Add Gnash icon
1252     addGnashIcon(GTK_WINDOW(_prefsDialog));
1253 
1254     // Add notebook (tabs) to dialogue's vbox
1255     _notebook = gtk_notebook_new ();
1256     gtk_container_add (
1257             GTK_CONTAINER(GTK_DIALOG(_prefsDialog)->vbox), _notebook);
1258 
1259     // Pass the widgets containing settings to the callback function
1260     // when any button is clicked or when the dialogue is destroyed.
1261     g_signal_connect (_prefsDialog, "response", G_CALLBACK(&handlePrefs), _prefs);
1262 
1263     addLoggingTab();
1264     addSecurityTab();
1265     addNetworkTab();
1266     addMediaTab();
1267     addPlayerTab();
1268 }
1269 
1270 void
addNetworkTab()1271 PreferencesDialog::addNetworkTab()
1272 {
1273     GtkWidget *vbox = gtk_vbox_new (FALSE, 10);
1274 
1275     // Tab label
1276     GtkWidget *label = gtk_label_new_with_mnemonic (_("_Network"));
1277     gtk_notebook_append_page(GTK_NOTEBOOK(_notebook), GTK_WIDGET(vbox), label);
1278 
1279     // Network preferences
1280     label = gtk_label_new (_("<b>Network preferences</b>"));
1281     gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1282     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1283 
1284     // Streams timeout
1285     GtkWidget *timeoutbox = gtk_hbox_new (FALSE, 2);
1286     gtk_box_pack_start(GTK_BOX(vbox), timeoutbox, FALSE, FALSE, 0);
1287 
1288     label = gtk_label_new (_("Network timeout in seconds (0 for no timeout):"));
1289     gtk_box_pack_start(GTK_BOX(timeoutbox), label, FALSE, FALSE, 0);
1290     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
1291 
1292     _prefs->streamsTimeoutScale = gtk_spin_button_new_with_range(0, 300, 1);
1293     gtk_box_pack_start(GTK_BOX(timeoutbox), _prefs->streamsTimeoutScale, FALSE,
1294             FALSE, 0);
1295     // Align to _rcfile value:
1296     gtk_spin_button_set_value(GTK_SPIN_BUTTON(_prefs->streamsTimeoutScale),
1297             _rcfile.getStreamsTimeout());
1298 
1299 }
1300 
1301 void
addLoggingTab()1302 PreferencesDialog::addLoggingTab()
1303 {
1304     GtkWidget *loggingvbox = gtk_vbox_new (FALSE, 10);
1305 
1306     // Tab label
1307     GtkWidget *loggingtablabel = gtk_label_new_with_mnemonic (_("_Logging"));
1308     gtk_notebook_append_page(GTK_NOTEBOOK(_notebook), GTK_WIDGET(loggingvbox),
1309             loggingtablabel);
1310 
1311     // Logging options
1312     GtkWidget *logginglabel = gtk_label_new (_("<b>Logging options</b>"));
1313     gtk_label_set_use_markup (GTK_LABEL (logginglabel), TRUE);
1314     gtk_box_pack_start(GTK_BOX(loggingvbox), logginglabel, FALSE, FALSE, 0);
1315 
1316     GtkWidget *verbositylabel = gtk_label_new (_("Verbosity level:"));
1317     gtk_box_pack_start(GTK_BOX(loggingvbox), verbositylabel, FALSE, FALSE, 0);
1318     gtk_misc_set_alignment (GTK_MISC (verbositylabel), 0, 0.5);
1319 
1320     _prefs->verbosityScale = gtk_hscale_new(GTK_ADJUSTMENT(
1321                 gtk_adjustment_new(_rcfile.verbosityLevel(), 0, 10, 1, 0, 0)));
1322     gtk_scale_set_digits(GTK_SCALE(_prefs->verbosityScale), 0);
1323     gtk_range_set_update_policy(GTK_RANGE(
1324                 _prefs->verbosityScale), GTK_UPDATE_DISCONTINUOUS);
1325     gtk_box_pack_start(GTK_BOX(loggingvbox), _prefs->verbosityScale, FALSE,
1326             FALSE, 0);
1327 
1328     _prefs->writeLogToggle =
1329         gtk_check_button_new_with_mnemonic(_("Log to _file"));
1330     gtk_box_pack_start(GTK_BOX(loggingvbox), _prefs->writeLogToggle, FALSE,
1331             FALSE, 0);
1332     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_prefs->writeLogToggle),
1333             _rcfile.useWriteLog());
1334 
1335     GtkWidget *logfilelabel = gtk_label_new(_("Logfile name:"));
1336     gtk_box_pack_start(GTK_BOX(loggingvbox), logfilelabel, FALSE, FALSE, 0);
1337     gtk_misc_set_alignment(GTK_MISC(logfilelabel), 0, 0.5);
1338 
1339     _prefs->logfileName = gtk_entry_new();
1340     gtk_box_pack_start(GTK_BOX(loggingvbox), _prefs->logfileName, FALSE,
1341             FALSE, 0);
1342 
1343     // Put debug filename in the entry box
1344     gtk_entry_set_text(GTK_ENTRY(_prefs->logfileName),
1345             _rcfile.getDebugLog().c_str());
1346 
1347     _prefs->parserDumpToggle =
1348         gtk_check_button_new_with_mnemonic(_("Log _parser output"));
1349     gtk_box_pack_start(GTK_BOX(loggingvbox), _prefs->parserDumpToggle, FALSE,
1350             FALSE, 0);
1351     // Align button state with _rcfile
1352     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(_prefs->parserDumpToggle),
1353             _rcfile.useParserDump());
1354 
1355     _prefs->actionDumpToggle =
1356         gtk_check_button_new_with_mnemonic(_("Log SWF _actions"));
1357     gtk_box_pack_start(GTK_BOX(loggingvbox), _prefs->actionDumpToggle, FALSE,
1358             FALSE, 0);
1359     // Align button state with _rcfile
1360     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_prefs->actionDumpToggle),
1361             _rcfile.useActionDump());
1362 
1363     _prefs->malformedSWFToggle =
1364         gtk_check_button_new_with_mnemonic(_("Log malformed SWF _errors"));
1365     gtk_box_pack_start(GTK_BOX(loggingvbox), _prefs->malformedSWFToggle,
1366             FALSE, FALSE, 0);
1367     // Align button state with _rcfile
1368     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_prefs->malformedSWFToggle),
1369             _rcfile.showMalformedSWFErrors());
1370 
1371     _prefs->ASCodingErrorToggle = gtk_check_button_new_with_mnemonic(
1372             _("Log ActionScript _coding errors"));
1373     gtk_box_pack_start(GTK_BOX(loggingvbox), _prefs->ASCodingErrorToggle,
1374             FALSE, FALSE, 0);
1375     // Align button state with _rcfile
1376     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_prefs->ASCodingErrorToggle),
1377                 _rcfile.showASCodingErrors());
1378 
1379 }
1380 
1381 void
addSecurityTab()1382 PreferencesDialog::addSecurityTab()
1383 {
1384     // Security Tab
1385     GtkWidget *securityvbox = gtk_vbox_new (FALSE, 14);
1386 
1387     // Security tab title
1388     GtkWidget *securitytablabel = gtk_label_new_with_mnemonic (_("_Security"));
1389 
1390     gtk_notebook_append_page(GTK_NOTEBOOK(_notebook),
1391             GTK_WIDGET(securityvbox), securitytablabel);
1392 
1393     // Network connection
1394     GtkWidget *netconnectionslabel = gtk_label_new(
1395             _("<b>Network connections</b>"));
1396     gtk_label_set_use_markup(GTK_LABEL(netconnectionslabel), TRUE);
1397     gtk_box_pack_start(GTK_BOX(securityvbox), netconnectionslabel, FALSE,
1398             FALSE, 0);
1399 
1400     _prefs->localHostToggle = gtk_check_button_new_with_mnemonic(
1401             _("Connect only to local _host"));
1402     gtk_box_pack_start(GTK_BOX(securityvbox), _prefs->localHostToggle, FALSE,
1403             FALSE, 0);
1404     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_prefs->localHostToggle),
1405             _rcfile.useLocalHost());
1406 
1407     _prefs->localDomainToggle = gtk_check_button_new_with_mnemonic(
1408             _("Connect only to local _domain"));
1409     gtk_box_pack_start(GTK_BOX(securityvbox), _prefs->localDomainToggle,
1410             FALSE, FALSE, 0);
1411     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_prefs->localDomainToggle),
1412             _rcfile.useLocalDomain());
1413 
1414     _prefs->insecureSSLToggle = gtk_check_button_new_with_mnemonic(
1415             _("Disable SSL _verification"));
1416     gtk_box_pack_start(GTK_BOX(securityvbox), _prefs->insecureSSLToggle,
1417             FALSE, FALSE, 0);
1418     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_prefs->insecureSSLToggle),
1419             _rcfile.insecureSSL());
1420 
1421     GtkWidget *whitelistexpander =
1422         gtk_expander_new_with_mnemonic(_("_Whitelist"));
1423     gtk_box_pack_start(GTK_BOX(securityvbox), whitelistexpander, FALSE,
1424             FALSE, 0);
1425 
1426     GtkWidget *whitelistcomboboxentry1 = gtk_combo_box_entry_new_text();
1427     gtk_container_add(GTK_CONTAINER(whitelistexpander),
1428             whitelistcomboboxentry1);
1429 
1430     GtkWidget *blacklistexpander =
1431         gtk_expander_new_with_mnemonic(_("_Blacklist"));
1432     gtk_box_pack_start(GTK_BOX (securityvbox), blacklistexpander, FALSE,
1433             FALSE, 0);
1434 
1435     GtkWidget *blacklistcomboboxentry2 = gtk_combo_box_entry_new_text();
1436     gtk_container_add (GTK_CONTAINER(blacklistexpander),
1437             blacklistcomboboxentry2);
1438 
1439     // Privacy
1440     GtkWidget *privacylabel = gtk_label_new(_("<b>Privacy</b>"));
1441     gtk_label_set_use_markup(GTK_LABEL(privacylabel), TRUE);
1442     gtk_box_pack_start(GTK_BOX(securityvbox), privacylabel, FALSE, FALSE, 0);
1443 
1444     GtkWidget *solsandboxlabel = gtk_label_new(_("Shared objects directory:"));
1445     gtk_box_pack_start(GTK_BOX(securityvbox), solsandboxlabel, FALSE,
1446             FALSE, 0);
1447     gtk_misc_set_alignment(GTK_MISC (solsandboxlabel), 0, 0.5);
1448 
1449     _prefs->solSandbox = gtk_entry_new();
1450     gtk_entry_set_text(GTK_ENTRY(_prefs->solSandbox),
1451             _rcfile.getSOLSafeDir().c_str());
1452     gtk_box_pack_start(GTK_BOX(securityvbox), _prefs->solSandbox, FALSE,
1453             FALSE, 0);
1454 
1455     _prefs->solReadOnlyToggle = gtk_check_button_new_with_mnemonic(
1456                     _("Do _not write Shared Object files"));
1457     gtk_box_pack_start(GTK_BOX(securityvbox), _prefs->solReadOnlyToggle,
1458             FALSE, FALSE, 0);
1459     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_prefs->solReadOnlyToggle),
1460                 _rcfile.getSOLReadOnly());
1461 
1462     _prefs->solLocalDomainToggle = gtk_check_button_new_with_mnemonic(
1463                     _("Only _access local Shared Object files"));
1464     gtk_box_pack_start(GTK_BOX(securityvbox), _prefs->solLocalDomainToggle,
1465             FALSE, FALSE, 0);
1466     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
1467                 _prefs->solLocalDomainToggle), _rcfile.getSOLLocalDomain());
1468 
1469     _prefs->localConnectionToggle = gtk_check_button_new_with_mnemonic(
1470                     _("Disable Local _Connection object"));
1471     gtk_box_pack_start(GTK_BOX(securityvbox), _prefs->localConnectionToggle,
1472             FALSE, FALSE, 0);
1473     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
1474                 _prefs->localConnectionToggle), _rcfile.getLocalConnection());
1475 }
1476 
1477 void
addMediaTab()1478 PreferencesDialog::addMediaTab()
1479 {
1480     // Media Tab
1481     GtkWidget *mediavbox = gtk_vbox_new (FALSE, 2);
1482 
1483     // Media tab title
1484     GtkWidget *mediatablabel = gtk_label_new_with_mnemonic (_("_Media"));
1485 
1486     gtk_notebook_append_page(GTK_NOTEBOOK(_notebook),
1487             GTK_WIDGET(mediavbox), mediatablabel);
1488 
1489     // Sound
1490     GtkWidget *soundlabel = gtk_label_new(_("<b>Sound</b>"));
1491     gtk_label_set_use_markup(GTK_LABEL(soundlabel), TRUE);
1492     gtk_box_pack_start(GTK_BOX(mediavbox), soundlabel, FALSE, FALSE, 0);
1493 
1494     _prefs->soundToggle = gtk_check_button_new_with_mnemonic(
1495             _("Use sound _handler"));
1496     gtk_box_pack_start(GTK_BOX(mediavbox), _prefs->soundToggle, FALSE,
1497             FALSE, 0);
1498     // Align button state with rcfile
1499     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_prefs->soundToggle),
1500             _rcfile.useSound());
1501 
1502     // Save Media
1503     GtkWidget *savemedia = gtk_label_new(_("<b>Media Streams</b>"));
1504     gtk_label_set_use_markup(GTK_LABEL(savemedia), TRUE);
1505     gtk_box_pack_start(GTK_BOX(mediavbox), savemedia, FALSE, FALSE, 0);
1506 
1507     // Save streamed media Toggle
1508     _prefs->saveStreamingMediaToggle = gtk_check_button_new_with_mnemonic(
1509             _("Save media streams to disk"));
1510     gtk_box_pack_start (GTK_BOX(mediavbox), _prefs->saveStreamingMediaToggle,
1511             FALSE, FALSE, 0);
1512     // Align button state with rcfile
1513     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
1514             _prefs->saveStreamingMediaToggle), _rcfile.saveStreamingMedia());
1515 
1516     // Save loaded media Toggle
1517     _prefs->saveLoadedMediaToggle = gtk_check_button_new_with_mnemonic(
1518             _("Save dynamically loaded media to disk"));
1519     gtk_box_pack_start (GTK_BOX(mediavbox), _prefs->saveLoadedMediaToggle,
1520             FALSE, FALSE, 0);
1521     // Align button state with rcfile
1522     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
1523                 _prefs->saveLoadedMediaToggle), _rcfile.saveLoadedMedia());
1524 
1525     // Directory for saving media
1526     GtkWidget *mediastreamslabel = gtk_label_new(_("Saved media directory:"));
1527     gtk_box_pack_start(GTK_BOX(mediavbox), mediastreamslabel, FALSE,
1528             FALSE, 0);
1529     gtk_misc_set_alignment (GTK_MISC (mediastreamslabel), 0, 0.5);
1530 
1531     _prefs->mediaDir = gtk_entry_new();
1532     gtk_entry_set_text(GTK_ENTRY(_prefs->mediaDir),
1533             _rcfile.getMediaDir().c_str());
1534     gtk_box_pack_start(GTK_BOX(mediavbox), _prefs->mediaDir, FALSE,
1535             FALSE, 0);
1536 
1537 }
1538 
1539 void
addPlayerTab()1540 PreferencesDialog::addPlayerTab()
1541 {
1542     // Player Tab
1543     GtkWidget *playervbox = gtk_vbox_new (FALSE, 14);
1544 
1545     // Player tab title
1546     GtkWidget *playertablabel = gtk_label_new_with_mnemonic (_("_Player"));
1547 
1548     gtk_notebook_append_page(GTK_NOTEBOOK(_notebook),
1549             GTK_WIDGET(playervbox), playertablabel);
1550 
1551     // Player description
1552     GtkWidget *descriptionlabel = gtk_label_new (_("<b>Player description</b>"));
1553     gtk_label_set_use_markup (GTK_LABEL (descriptionlabel), TRUE);
1554     gtk_box_pack_start(GTK_BOX(playervbox), descriptionlabel, FALSE, FALSE, 0);
1555 
1556     // Version string
1557     GtkWidget *versionhbox = gtk_hbox_new (FALSE, 2);
1558     gtk_box_pack_start(GTK_BOX(playervbox), versionhbox, FALSE, FALSE, 0);
1559 
1560     GtkWidget *versionlabel = gtk_label_new (_("Player version:"));
1561     gtk_misc_set_alignment (GTK_MISC (versionlabel), 0, 0.5);
1562     gtk_box_pack_start(GTK_BOX(versionhbox), versionlabel, FALSE, FALSE, 0);
1563 
1564     _prefs->versionText = gtk_entry_new ();
1565     gtk_box_pack_start(GTK_BOX(versionhbox), _prefs->versionText,
1566             FALSE, FALSE, 0);
1567 
1568     // Put text in the entry box
1569     gtk_entry_set_text(GTK_ENTRY(_prefs->versionText),
1570             _rcfile.getFlashVersionString().c_str());
1571 
1572     // OS label
1573     GtkWidget *oshbox = gtk_hbox_new (FALSE, 2);
1574     gtk_box_pack_start(GTK_BOX(playervbox), oshbox, FALSE, FALSE, 0);
1575 
1576     GtkWidget *OSlabel = gtk_label_new (_("Operating system:"));
1577     gtk_misc_set_alignment (GTK_MISC (OSlabel), 0, 0.5);
1578     gtk_box_pack_start(GTK_BOX(oshbox), OSlabel, FALSE, FALSE, 0);
1579 
1580     _prefs->osText = gtk_entry_new ();
1581     gtk_box_pack_start(GTK_BOX(oshbox), _prefs->osText, FALSE, FALSE, 0);
1582     // Put text in the entry box
1583     gtk_entry_set_text(GTK_ENTRY(_prefs->osText),
1584             _rcfile.getFlashSystemOS().c_str());
1585 
1586     GtkWidget *OSadvicelabel = gtk_label_new (_("<i>If blank, Gnash will "
1587                            "detect your OS</i>"));
1588     gtk_label_set_use_markup (GTK_LABEL (OSadvicelabel), TRUE);
1589     gtk_misc_set_alignment (GTK_MISC (OSadvicelabel), 0, 0.5);
1590     gtk_box_pack_start(GTK_BOX(playervbox), OSadvicelabel, FALSE, FALSE, 0);
1591 
1592     // URL opener
1593     GtkWidget *urlopenerbox = gtk_hbox_new(FALSE, 2);
1594     gtk_box_pack_start(GTK_BOX(playervbox), urlopenerbox, FALSE, FALSE, 0);
1595 
1596     GtkWidget *urlopenerlabel = gtk_label_new (_("URL opener:"));
1597     gtk_misc_set_alignment (GTK_MISC (urlopenerlabel), 0, 0.5);
1598     gtk_box_pack_start(GTK_BOX(urlopenerbox), urlopenerlabel, FALSE, FALSE, 0);
1599 
1600     _prefs->urlOpenerText = gtk_entry_new ();
1601     gtk_box_pack_start(GTK_BOX(urlopenerbox), _prefs->urlOpenerText, FALSE,
1602             FALSE, 0);
1603     // Put text in the entry box
1604     gtk_entry_set_text(GTK_ENTRY(_prefs->urlOpenerText),
1605             _rcfile.getURLOpenerFormat().c_str());
1606 
1607     // Performance
1608     GtkWidget *performancelabel = gtk_label_new(_("<b>Performance</b>"));
1609     gtk_label_set_use_markup (GTK_LABEL (performancelabel), TRUE);
1610     gtk_box_pack_start(GTK_BOX(playervbox), performancelabel, FALSE, FALSE, 0);
1611 
1612     GtkWidget* qualitybox = gtk_hbox_new(FALSE, 2);
1613     gtk_box_pack_start(GTK_BOX(playervbox), qualitybox, FALSE, FALSE, 0);
1614 
1615     GtkWidget* qualityoptions = gtk_vbox_new(FALSE, 5);
1616     gtk_box_pack_start(GTK_BOX(qualitybox), qualityoptions, FALSE, FALSE, 0);
1617 
1618     // Library size
1619     GtkWidget *libraryhbox = gtk_hbox_new (FALSE, 2);
1620     gtk_box_pack_start(GTK_BOX(playervbox), libraryhbox, FALSE, FALSE, 0);
1621 
1622     GtkWidget *librarylabel = gtk_label_new (_("Max size of movie library:"));
1623     gtk_misc_set_alignment (GTK_MISC (librarylabel), 0, 0.5);
1624     gtk_box_pack_start(GTK_BOX(libraryhbox), librarylabel, FALSE, FALSE, 0);
1625 
1626     _prefs->librarySize = gtk_spin_button_new_with_range(0, 100, 1);
1627     gtk_box_pack_start(GTK_BOX(libraryhbox), _prefs->librarySize, FALSE,
1628             FALSE, 0);
1629     // Align to _rcfile value:
1630     gtk_spin_button_set_value(GTK_SPIN_BUTTON(_prefs->librarySize),
1631             _rcfile.getMovieLibraryLimit());
1632 
1633     { // Scripts timeout -- {
1634 
1635     GtkWidget *hbox = gtk_hbox_new (FALSE, 2);
1636     gtk_box_pack_start(GTK_BOX(playervbox), hbox, FALSE, FALSE, 0);
1637 
1638     GtkWidget *lbl = gtk_label_new (
1639         _("Max scripts execution time (in seconds):"));
1640     gtk_misc_set_alignment (GTK_MISC (lbl), 0, 0.5);
1641     gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 0);
1642 
1643     _prefs->scriptsTimeout = gtk_spin_button_new_with_range(0, 60, 1);
1644     gtk_box_pack_start(GTK_BOX(hbox), _prefs->scriptsTimeout, FALSE,
1645             FALSE, 0);
1646     // Align to _rcfile value:
1647     gtk_spin_button_set_value(GTK_SPIN_BUTTON(_prefs->scriptsTimeout),
1648             _rcfile.getScriptsTimeout());
1649 
1650     } // --}
1651 
1652     { // Scripts recursion limit -- {
1653 
1654     GtkWidget *hbox = gtk_hbox_new (FALSE, 2);
1655     gtk_box_pack_start(GTK_BOX(playervbox), hbox, FALSE, FALSE, 0);
1656 
1657     GtkWidget *lbl = gtk_label_new (
1658         _("Max scripts recursion limit (stack depth):"));
1659     gtk_misc_set_alignment (GTK_MISC (lbl), 0, 0.5);
1660     gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 0);
1661 
1662     _prefs->scriptsRecursionLimit = gtk_spin_button_new_with_range(0, 1024, 1);
1663     gtk_box_pack_start(GTK_BOX(hbox), _prefs->scriptsRecursionLimit, FALSE,
1664             FALSE, 0);
1665     // Align to _rcfile value:
1666     gtk_spin_button_set_value(GTK_SPIN_BUTTON(_prefs->scriptsRecursionLimit),
1667             _rcfile.getScriptsRecursionLimit());
1668 
1669     } // --}
1670 
1671     { // Scripts limits lock -- {
1672 
1673     _prefs->lockScriptLimitsToggle = gtk_check_button_new_with_mnemonic(
1674             _("Lock script limits so that SWF tags can't override"));
1675     gtk_box_pack_start (GTK_BOX(playervbox), _prefs->lockScriptLimitsToggle,
1676             FALSE, FALSE, 0);
1677     // Align button state with rcfile
1678     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
1679             _prefs->lockScriptLimitsToggle), _rcfile.lockScriptLimits());
1680 
1681     } // --}
1682 
1683 
1684     // Start-stopped toggle
1685     _prefs->startStoppedToggle = gtk_check_button_new_with_mnemonic (
1686                     _("Start _Gnash in pause mode"));
1687     gtk_box_pack_start(GTK_BOX(playervbox), _prefs->startStoppedToggle,
1688             FALSE, FALSE, 0);
1689     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_prefs->startStoppedToggle),
1690                 _rcfile.startStopped());
1691 }
1692 
1693 
1694 } // anonymous namespace
1695 
1696 
1697 void
showPreferencesDialog()1698 GtkGui::showPreferencesDialog()
1699 {
1700 
1701     PreferencesDialog preferencesDialog(_window);
1702     preferencesDialog.show();
1703 }
1704 
1705 void
showPropertiesDialog()1706 GtkGui::showPropertiesDialog()
1707 {
1708 
1709     GtkWidget *propsDialog = gtk_dialog_new_with_buttons(
1710                         _("Movie properties"),
1711                         GTK_WINDOW(_window),
1712                         // The cast is necessary if there is more
1713                         // than one option.
1714                         GtkDialogFlags(
1715                         GTK_DIALOG_DESTROY_WITH_PARENT),
1716                         // Just a 'close' button
1717                         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1718                         nullptr);
1719 
1720     // Not too small... But I'd rather not have to specify
1721     // a size in pixels.
1722     gtk_window_set_default_size(GTK_WINDOW(propsDialog), 500, 300);
1723 
1724     // Suggest to the window manager to allow "maximize"
1725     // As there can be (will be) a lot of data.
1726     gtk_window_set_type_hint(GTK_WINDOW(propsDialog),
1727                             GDK_WINDOW_TYPE_HINT_NORMAL);
1728 
1729     addGnashIcon(GTK_WINDOW(propsDialog));
1730 
1731     // Destroy the window when a button is clicked.
1732     g_signal_connect (propsDialog, "response",
1733                G_CALLBACK(gtk_widget_destroy), nullptr);
1734 
1735     GtkWidget *propsvbox = gtk_vbox_new (FALSE, 1);
1736     gtk_container_add(GTK_CONTAINER(
1737                         GTK_DIALOG(propsDialog)->vbox), propsvbox);
1738 
1739 #ifdef USE_SWFTREE
1740 
1741     std::unique_ptr<movie_root::InfoTree> infoptr = getMovieInfo();
1742 
1743     GtkWidget *scrollwindow1 = gtk_scrolled_window_new(0, 0);
1744     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwindow1),
1745                       GTK_POLICY_AUTOMATIC,
1746                       GTK_POLICY_AUTOMATIC);
1747 
1748     gtk_box_pack_start(GTK_BOX (propsvbox), scrollwindow1, TRUE, TRUE, 0);
1749 
1750     enum
1751     {
1752         STRING1_COLUMN,
1753         STRING2_COLUMN
1754     };
1755 
1756     GtkTreeModel *model = makeTreeModel(infoptr);
1757 
1758     GtkWidget *treeview = gtk_tree_view_new_with_model (model);
1759 
1760     g_object_unref (model);
1761 
1762     ///
1763     /// Tree view behaviour.
1764 
1765     /// Search on "variable" column
1766     gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), TRUE);
1767     gtk_tree_view_set_search_column (GTK_TREE_VIEW(treeview), 0);
1768 
1769     /// Nice shading
1770     gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
1771 
1772     gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview), TRUE);
1773 
1774     // Add columns:
1775 
1776     // 'Variable' column:
1777     GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1778     gtk_tree_view_insert_column_with_attributes(
1779             GTK_TREE_VIEW(treeview),
1780             -1, _("Variable"),
1781             renderer, "text",
1782             STRING1_COLUMN,
1783             nullptr);
1784 
1785     // 'Value' column:
1786     // Set to be 'editable' so that the data can be selected and
1787     // copied; it can't actually be edited, though.
1788     renderer = gtk_cell_renderer_text_new ();
1789     g_object_set (renderer, "xalign", 0.0, "editable", TRUE, nullptr);
1790     gtk_tree_view_insert_column_with_attributes(
1791             GTK_TREE_VIEW(treeview),
1792             -1, _("Value"),
1793             renderer, "text",
1794             STRING2_COLUMN,
1795             nullptr);
1796 
1797     //Add tree to scrollwindow.
1798     gtk_container_add(GTK_CONTAINER(scrollwindow1), treeview);
1799 
1800 #endif
1801 
1802     gtk_widget_show_all(propsDialog);
1803 
1804 }
1805 
1806 // \brief Show info about gnash
1807 void
showAboutDialog()1808 GtkGui::showAboutDialog()
1809 {
1810     const gchar *documentors[] = {
1811         "Rob Savoye",
1812         "Sandro Santilli",
1813         "Ann Barcomb",
1814         nullptr
1815     };
1816 
1817     const gchar *artists[] = {
1818         "Jason Savoye",
1819         nullptr
1820     };
1821 
1822     const gchar *authors[] = {
1823         "Rob Savoye",
1824         "Sandro Santilli",
1825         "Bastiaan Jacques",
1826         "Tomas Groth",
1827         "Udo Giacomozzi",
1828         "Hannes Mayr",
1829         "Markus Gothe",
1830         "Vitaly Alexeev",
1831         "John Gilmore",
1832         "Zou Lunkai",
1833         "Benjamin Wolsey",
1834         "Russ Nelson",
1835         "Dossy Shiobara",
1836         "Jonathan Crider",
1837         "Ben Limmer",
1838         "Bob Naugle",
1839         "Si Liu",
1840         "Sharad Desai",
1841         nullptr
1842     };
1843 
1844 	const std::string license =
1845         _("This program is free software; you can redistribute it and/or modify\n"
1846          "it under the terms of the GNU General Public License as published by\n"
1847          "the Free Software Foundation; either version 3 of the License, or\n"
1848          "(at your option) any later version.\n\n"
1849          "This program is distributed in the hope that it will be useful,\n"
1850          "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1851          "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1852          "GNU General Public License for more details.\n"
1853          "You should have received a copy of the GNU General Public License\n"
1854          "along with this program; if not, write to the Free Software\n"
1855          "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301\n"
1856          "USA or visit http://www.gnu.org/licenses/.");
1857 
1858     media::MediaHandler* m = _runResources.mediaHandler();
1859 
1860     std::string comments =
1861         _("Gnash is the GNU SWF Player based on GameSWF.");
1862     comments.append(_("\nRenderer: "));
1863     comments.append(_renderer->description());
1864     comments.append(_("\nHardware Acceleration: "));
1865     comments.append(HWACCEL_CONFIG);
1866     comments.append(_("\nGUI: "));
1867     comments.append("GTK2"); // gtk of course!
1868     comments.append(_("\nMedia: "));
1869     comments.append(m ? m->description() : "no media handler");
1870 
1871     GdkPixbuf* logo_pixbuf = createPixbuf("GnashG.png");
1872 
1873     // gtk-2.8.20 (Debian 4.0) doesn't work fine with
1874     // the gtk_show_about_dialog() call [ omits info ].
1875     // See bug #24426.
1876     GtkWidget* aboutWidget = gtk_about_dialog_new();
1877     addGnashIcon(GTK_WINDOW(aboutWidget));
1878     GtkAboutDialog* about = GTK_ABOUT_DIALOG(aboutWidget);
1879 
1880     gtk_about_dialog_set_name (about, "Gnash");
1881     std::string version = VERSION;
1882     version.append("\n");
1883     version.append("(");
1884     version.append(BRANCH_NICK);
1885     version.append("-");
1886     version.append(BRANCH_REVNO);
1887     version.append("-");
1888     version.append(COMMIT_ID);
1889     version.append(")");
1890 
1891     gtk_about_dialog_set_version(about, version.c_str());
1892     gtk_about_dialog_set_copyright(about, "Copyright (C) 2005, 2006, 2007, "
1893             "2008, 2009, 2010, 2011, 2012, 2013 The Free Software Foundation");
1894     gtk_about_dialog_set_comments (about, comments.c_str());
1895     gtk_about_dialog_set_authors(about, authors);
1896     gtk_about_dialog_set_documenters(about, documentors);
1897     gtk_about_dialog_set_artists(about, artists);
1898     gtk_about_dialog_set_translator_credits(about, _("translator-credits"));
1899     gtk_about_dialog_set_logo(about, logo_pixbuf);
1900     gtk_about_dialog_set_license(about, license.c_str());
1901     gtk_about_dialog_set_website(about, "http://www.gnu.org/software/gnash/");
1902 
1903     // Destroy the dialogue box when 'close' is clicked.
1904     g_signal_connect(aboutWidget, "response",
1905             G_CALLBACK(gtk_widget_destroy), aboutWidget);
1906 
1907     gtk_widget_show (aboutWidget);
1908 
1909     if (logo_pixbuf) g_object_unref(logo_pixbuf);
1910 }
1911 
1912 ///////////////////////////////////////////////////////////////////////////////
1913 ///////////////////////////////////////////////////////////////////////////////
1914 ///                                                                         ///
1915 ///                                Menus                                    ///
1916 ///                                                                         ///
1917 ///////////////////////////////////////////////////////////////////////////////
1918 ///////////////////////////////////////////////////////////////////////////////
1919 
1920 
1921 // Create a File menu that can be used from the menu bar or the popup.
1922 void
createFileMenu(GtkWidget * obj)1923 GtkGui::createFileMenu(GtkWidget *obj)
1924 {
1925     GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic(_("_File"));
1926     gtk_widget_show(menuitem);
1927     gtk_container_add(GTK_CONTAINER(obj), menuitem);
1928 
1929     GtkWidget *menu = gtk_menu_new();
1930     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
1931 
1932     // Open
1933     GtkWidget *open = gtk_image_menu_item_new_from_stock("gtk-open", 0);
1934     gtk_widget_show(open);
1935     gtk_container_add(GTK_CONTAINER(menu), open);
1936     g_signal_connect(open, "activate", G_CALLBACK(menuOpenFile), this);
1937 
1938     // Save
1939     GtkWidget *save = gtk_image_menu_item_new_from_stock("gtk-save", 0);
1940     gtk_widget_show(save);
1941     gtk_container_add(GTK_CONTAINER(menu), save);
1942     // Disabled until save functionality is implemented:
1943     gtk_widget_set_sensitive(save, FALSE);
1944 
1945     // Save as
1946     GtkWidget *saveAs = gtk_image_menu_item_new_from_stock("gtk-save-as", 0);
1947     gtk_widget_show(saveAs);
1948     gtk_container_add(GTK_CONTAINER(menu), saveAs);
1949     // Disabled until save-as functionality is implemented:
1950     gtk_widget_set_sensitive(saveAs, FALSE);
1951 
1952     GtkWidget *separatormenuitem1 = gtk_separator_menu_item_new();
1953     gtk_widget_show(separatormenuitem1);
1954     gtk_container_add(GTK_CONTAINER(menu), separatormenuitem1);
1955 
1956     // Properties
1957     GtkWidget *properties =
1958         gtk_image_menu_item_new_from_stock("gtk-properties", 0);
1959     gtk_widget_show(properties);
1960     gtk_container_add(GTK_CONTAINER(menu), properties);
1961     g_signal_connect(properties, "activate", G_CALLBACK(menuMovieInfo), this);
1962 
1963     GtkWidget *separator2 = gtk_separator_menu_item_new();
1964     gtk_widget_show(separator2);
1965     gtk_container_add(GTK_CONTAINER(menu), separator2);
1966 
1967     GtkWidget *quit = gtk_image_menu_item_new_from_stock("gtk-quit", 0);
1968     gtk_widget_show(quit);
1969     gtk_container_add(GTK_CONTAINER(menu), quit);
1970     g_signal_connect(quit, "activate", G_CALLBACK(menuQuit), this);
1971 }
1972 
1973 // Create an Edit menu that can be used from the menu bar or the popup.
1974 void
createEditMenu(GtkWidget * obj)1975 GtkGui::createEditMenu(GtkWidget *obj)
1976 {
1977 
1978     GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit"));
1979     gtk_widget_show(menuitem);
1980     gtk_container_add(GTK_CONTAINER(obj), menuitem);
1981 
1982     GtkWidget *menu = gtk_menu_new();
1983     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
1984 
1985     GtkWidget *preferences =
1986         gtk_image_menu_item_new_from_stock("gtk-preferences", 0);
1987     gtk_widget_show(preferences);
1988     gtk_container_add(GTK_CONTAINER(menu), preferences);
1989 
1990     g_signal_connect(preferences, "activate", G_CALLBACK(menuPreferences),
1991                       this);
1992 }
1993 
1994 // Create a Help menu that can be used from the menu bar or the popup.
1995 void
createHelpMenu(GtkWidget * obj)1996 GtkGui::createHelpMenu(GtkWidget *obj)
1997 {
1998     GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic(_("_Help"));
1999     gtk_widget_show(menuitem);
2000     gtk_container_add(GTK_CONTAINER (obj), menuitem);
2001 
2002     GtkWidget *menu = gtk_menu_new();
2003     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
2004 
2005     GtkWidget *about = gtk_image_menu_item_new_from_stock("gtk-about", 0);
2006     gtk_widget_show(about);
2007     gtk_container_add(GTK_CONTAINER(menu), about);
2008 
2009     g_signal_connect(about, "activate", G_CALLBACK(menuAbout), this);
2010 }
2011 
2012 
2013 // Create a View menu that can be used from the menu bar or the popup.
2014 void
createViewMenu(GtkWidget * obj)2015 GtkGui::createViewMenu(GtkWidget *obj)
2016 {
2017 
2018     GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (_("_View"));
2019     gtk_widget_show (menuitem);
2020     gtk_container_add (GTK_CONTAINER (obj), menuitem);
2021 
2022     GtkWidget *menu = gtk_menu_new ();
2023     gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
2024 
2025     // Refresh
2026     GtkWidget *refresh = gtk_image_menu_item_new_with_label(_("Redraw"));
2027     gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(refresh),
2028         gtk_image_new_from_stock("gtk-refresh", GTK_ICON_SIZE_MENU));
2029     gtk_menu_append(menu, refresh);
2030     gtk_widget_show(refresh);
2031     g_signal_connect(refresh, "activate", G_CALLBACK(menuRefreshView), this);
2032 
2033     // Fullscreen
2034 #if GTK_CHECK_VERSION(2,8,0)
2035     GtkWidget *fullscreen =
2036         gtk_image_menu_item_new_with_label(_("Toggle fullscreen"));
2037     gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(fullscreen),
2038                gtk_image_new_from_stock("gtk-fullscreen", GTK_ICON_SIZE_MENU));
2039 #else
2040     GtkWidget *fullscreen =
2041         gtk_menu_item_new_with_label(_("Toggle fullscreen"));
2042 #endif
2043     gtk_menu_append(menu, fullscreen);
2044     gtk_widget_show(GTK_WIDGET(fullscreen));
2045     g_signal_connect(fullscreen, "activate", G_CALLBACK(menuFullscreen), this);
2046 
2047 // Can be disabled at compile time.
2048 #ifndef DISABLE_REGION_UPDATES_DEBUGGING
2049     GtkWidget *updated_regions =
2050         gtk_check_menu_item_new_with_label(_("Show updated ranges"));
2051 
2052     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(updated_regions),
2053             showUpdatedRegions());
2054 
2055     gtk_menu_append(menu, updated_regions);
2056     gtk_widget_show(updated_regions);
2057     g_signal_connect(updated_regions, "activate",
2058                      G_CALLBACK(menuShowUpdatedRegions), this);
2059 #endif
2060 
2061     createQualityMenu(menu);
2062 
2063 }
2064 
2065 // Create a Quality menu that can be used from the View menu
2066 void
createQualityMenu(GtkWidget * obj)2067 GtkGui::createQualityMenu(GtkWidget *obj)
2068 {
2069     GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (_("_Quality"));
2070     gtk_widget_show (menuitem);
2071     gtk_container_add (GTK_CONTAINER (obj), menuitem);
2072 
2073     GtkWidget *menu = gtk_menu_new ();
2074     gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
2075 
2076     // TODO: use to also show current quality state
2077 
2078     // Low
2079     GtkWidget* item = gtk_menu_item_new_with_label(_("Low"));
2080     gtk_menu_append(menu, item);
2081     gtk_widget_show(item);
2082     g_signal_connect(item, "activate", G_CALLBACK(menuQualityLow), this);
2083 
2084     // Medium
2085     item = gtk_menu_item_new_with_label(_("Medium"));
2086     gtk_menu_append(menu, item);
2087     gtk_widget_show(item);
2088     g_signal_connect(item, "activate", G_CALLBACK(menuQualityMedium), this);
2089 
2090     // High
2091     item = gtk_menu_item_new_with_label(_("High"));
2092     gtk_menu_append(menu, item);
2093     gtk_widget_show(item);
2094     g_signal_connect(item, "activate", G_CALLBACK(menuQualityHigh), this);
2095 
2096     // Best
2097     item = gtk_menu_item_new_with_label(_("Best"));
2098     gtk_menu_append(menu, item);
2099     gtk_widget_show(item);
2100     g_signal_connect(item, "activate", G_CALLBACK(menuQualityBest), this);
2101 
2102 }
2103 
2104 // Create a Control menu that can be used from the menu bar or the popup.
2105 void
createControlMenu(GtkWidget * obj)2106 GtkGui::createControlMenu(GtkWidget *obj)
2107 {
2108 
2109     // Movie Control Menu
2110     GtkWidget *control = gtk_menu_item_new_with_mnemonic(_("Movie _Control"));
2111     gtk_widget_show(control);
2112     gtk_container_add(GTK_CONTAINER(obj), control);
2113 
2114     GtkWidget *menu = gtk_menu_new();
2115     gtk_menu_item_set_submenu(GTK_MENU_ITEM(control), menu);
2116 
2117     // Play
2118 #if GTK_CHECK_VERSION(2,6,0)
2119     GtkWidget *play = gtk_image_menu_item_new_from_stock("gtk-media-play", 0);
2120 #else
2121     GtkWidget *play = gtk_menu_item_new_with_label(_("Play"));
2122 #endif
2123     gtk_menu_append(menu, play);
2124     gtk_widget_show(play);
2125     g_signal_connect (play, "activate", G_CALLBACK(menuPlay), this);
2126 
2127     // Pause
2128 #if GTK_CHECK_VERSION(2,6,0)
2129     GtkWidget *pause =
2130         gtk_image_menu_item_new_from_stock ("gtk-media-pause", 0);
2131 #else
2132     GtkWidget *pause = gtk_menu_item_new_with_label(_("Pause"));
2133 #endif
2134     gtk_menu_append(menu, pause);
2135     gtk_widget_show(pause);
2136     g_signal_connect(pause, "activate", G_CALLBACK(menuPause), this);
2137 
2138     // Stop
2139 #if GTK_CHECK_VERSION(2,6,0)
2140     GtkWidget *stop = gtk_image_menu_item_new_from_stock("gtk-media-stop", 0);
2141 #else
2142     GtkWidget *stop = gtk_menu_item_new_with_label(_("Stop"));
2143 #endif
2144     gtk_menu_append(menu, stop);
2145     gtk_widget_show(stop);
2146     g_signal_connect(stop, "activate", G_CALLBACK(menuStop), this);
2147 
2148     GtkWidget *separator1 = gtk_separator_menu_item_new();
2149     gtk_widget_show(separator1);
2150     gtk_container_add(GTK_CONTAINER(menu), separator1);
2151 
2152     // Restart
2153     //
2154     GtkWidget *restart = gtk_image_menu_item_new_with_label(_("Restart Movie"));
2155 
2156     // Suitable image?
2157     gtk_menu_append(menu, restart);
2158     gtk_widget_show(restart);
2159     g_signal_connect(restart, "activate", G_CALLBACK(menuRestart), this);
2160 
2161 }
2162 
2163 // This assumes that the parent of _drawingArea is _window, which
2164 // isn't the case in the plugin fullscreen (it's _overlay). Currently
2165 // we return from fullscreen when Gui::stop() is called, which
2166 // seems like a good idea, and also avoids this problem.
2167 void
stopHook()2168 GtkGui::stopHook()
2169 {
2170 
2171     // Assert they're either both initialised or both uninitialised
2172     assert ((_resumeButton && _vbox) || !(_resumeButton || _vbox));
2173     if (_resumeButton) {
2174         gtk_box_pack_start(GTK_BOX(_vbox), _resumeButton, FALSE, FALSE, 0);
2175     }
2176 
2177     stopAdvanceTimer();
2178 }
2179 
2180 void
playHook()2181 GtkGui::playHook()
2182 {
2183     assert ((_resumeButton && _vbox) || !(_resumeButton || _vbox));
2184     if (_resumeButton) {
2185         gtk_container_remove(GTK_CONTAINER(_vbox), _resumeButton);
2186     }
2187 
2188     startAdvanceTimer();
2189 }
2190 
2191 // See if the X11 server we're using supports an extension.
2192 bool
checkX11Extension(const std::string & ext)2193 GtkGui::checkX11Extension(const std::string& ext)
2194 {
2195 #ifdef HAVE_X11
2196 #ifndef GDK_DISPLAY
2197 	#define GDK_DISPLAY() (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()))
2198 #endif
2199     int n = 0;
2200     char **extlist = XListExtensions(GDK_DISPLAY(), &n);
2201 
2202     if (extlist) {
2203         for (int i = 0; i < n; i++) {
2204             if (std::strncmp(ext.c_str(), extlist[i], ext.size()) == 0) {
2205                 return true;
2206             }
2207         }
2208     }
2209 #endif /* HAVE_X11 */
2210     // do not free, Xlib can depend on contents being unaltered
2211     return false;
2212 }
2213 
2214 bool
yesno(const std::string & question)2215 GtkGui::yesno(const std::string& question)
2216 {
2217     stopAdvanceTimer();
2218     bool ret = true;
2219 
2220     GtkWidget *dialog = gtk_message_dialog_new(
2221         GTK_WINDOW(_window),
2222         GTK_DIALOG_MODAL,
2223         GTK_MESSAGE_QUESTION,
2224         GTK_BUTTONS_YES_NO,
2225         "%s", question.c_str());
2226 
2227     switch (gtk_dialog_run(GTK_DIALOG(dialog)))
2228     {
2229         case GTK_RESPONSE_YES:
2230             ret = true;
2231             break;
2232         case GTK_RESPONSE_NO:
2233             ret = false;
2234             break;
2235         default:
2236             break;
2237     }
2238 
2239     gtk_widget_destroy(dialog);
2240 
2241     startAdvanceTimer();
2242 
2243     return ret;
2244 }
2245 
2246 
2247 /// Anonymous namespace for callbacks, local functions, event handlers etc.
2248 namespace {
2249 
2250 static GList *pixmaps_directories = nullptr;
2251 
2252 // Adds the Gnash icon to a window.
2253 void
addGnashIcon(GtkWindow * window)2254 addGnashIcon(GtkWindow* window)
2255 {
2256     GdkPixbuf *window_icon_pixbuf = createPixbuf ("GnashG.png");
2257     if (window_icon_pixbuf) {
2258         gtk_window_set_icon (GTK_WINDOW (window), window_icon_pixbuf);
2259         g_object_unref (window_icon_pixbuf);
2260     }
2261 }
2262 
2263 /* This is an internally used function to create pixmaps. */
2264 GdkPixbuf*
createPixbuf(const gchar * filename)2265 createPixbuf (const gchar *filename)
2266 {
2267     gchar *pathname = nullptr;
2268     GdkPixbuf *pixbuf;
2269     GError *error = nullptr;
2270 
2271     if (!filename || !filename[0])
2272        return nullptr;
2273 
2274     pathname = findPixmapFile (filename);
2275 
2276     if (!pathname) {
2277         log_error (_("Couldn't find pixmap file: %s"), filename);
2278         g_warning(_("Couldn't find pixmap file: %s"), filename);
2279         return nullptr;
2280     }
2281 
2282     pixbuf = gdk_pixbuf_new_from_file (pathname, &error);
2283     if (!pixbuf) {
2284         log_error (_("Failed to load pixbuf file: %s: %s"), pathname, error->message);
2285         g_error_free (error);
2286     }
2287     g_free (pathname);
2288     return pixbuf;
2289 }
2290 
2291 key::code
gdk_to_gnash_key(guint key)2292 gdk_to_gnash_key(guint key)
2293 {
2294     key::code c(key::INVALID);
2295 
2296     // ascii 32-126 in one range:
2297     if (key >= GDK_space && key <= GDK_asciitilde) {
2298         c = (key::code) ((key - GDK_space) + key::SPACE);
2299     }
2300 
2301     // Function keys:
2302     else if (key >= GDK_F1 && key <= GDK_F15)    {
2303         c = (key::code) ((key - GDK_F1) + key::F1);
2304     }
2305 
2306     // Keypad:
2307     else if (key >= GDK_KP_0 && key <= GDK_KP_9) {
2308         c = (key::code) ((key - GDK_KP_0) + key::KP_0);
2309     }
2310 
2311     // Extended ascii:
2312     else if (key >= GDK_nobreakspace && key <= GDK_ydiaeresis) {
2313         c = (key::code) ((key - GDK_nobreakspace) +
2314                 key::NOBREAKSPACE);
2315     }
2316 
2317     // non-character keys don't correlate, so use a look-up table.
2318     else {
2319         struct {
2320             guint             gdk;
2321             key::code  gs;
2322         } table[] = {
2323             { GDK_BackSpace, key::BACKSPACE },
2324             { GDK_Tab, key::TAB },
2325             { GDK_Clear, key::CLEAR },
2326             { GDK_Return, key::ENTER },
2327 
2328             { GDK_Shift_L, key::SHIFT },
2329             { GDK_Shift_R, key::SHIFT },
2330             { GDK_Control_L, key::CONTROL },
2331             { GDK_Control_R, key::CONTROL },
2332             { GDK_Alt_L, key::ALT },
2333             { GDK_Alt_R, key::ALT },
2334             { GDK_Caps_Lock, key::CAPSLOCK },
2335 
2336             { GDK_Escape, key::ESCAPE },
2337 
2338             { GDK_Page_Down, key::PGDN },
2339             { GDK_Page_Up, key::PGUP },
2340             { GDK_Home, key::HOME },
2341             { GDK_End, key::END },
2342             { GDK_Left, key::LEFT },
2343             { GDK_Up, key::UP },
2344             { GDK_Right, key::RIGHT },
2345             { GDK_Down, key::DOWN },
2346             { GDK_Insert, key::INSERT },
2347             { GDK_Delete, key::DELETEKEY },
2348 
2349             { GDK_Help, key::HELP },
2350             { GDK_Num_Lock, key::NUM_LOCK },
2351 
2352             { GDK_VoidSymbol, key::INVALID }
2353         };
2354 
2355         for (int i = 0; table[i].gdk != GDK_VoidSymbol; i++) {
2356             if (key == table[i].gdk) {
2357                 c = table[i].gs;
2358                 break;
2359             }
2360         }
2361     }
2362 
2363     return c;
2364 }
2365 
2366 int
gdk_to_gnash_modifier(int state)2367 gdk_to_gnash_modifier(int state)
2368 {
2369     int modifier = key::GNASH_MOD_NONE;
2370 
2371     if (state & GDK_SHIFT_MASK) {
2372       modifier = modifier | key::GNASH_MOD_SHIFT;
2373     }
2374     if (state & GDK_CONTROL_MASK) {
2375       modifier = modifier | key::GNASH_MOD_CONTROL;
2376     }
2377     if (state & GDK_MOD1_MASK) {
2378       modifier = modifier | key::GNASH_MOD_ALT;
2379     }
2380 
2381     return modifier;
2382 }
2383 
2384 /* Use this function to set the directory containing installed pixmaps. */
2385 void
addPixmapDirectory(const gchar * directory)2386 addPixmapDirectory(const gchar* directory)
2387 {
2388     pixmaps_directories =
2389         g_list_prepend(pixmaps_directories, g_strdup(directory));
2390 }
2391 
2392 
2393 /* This is an internally used function to find pixmap files. */
2394 gchar*
findPixmapFile(const gchar * filename)2395 findPixmapFile(const gchar* filename)
2396 {
2397     GList *elem;
2398 
2399     /* We step through each of the pixmaps directory to find it. */
2400     elem = pixmaps_directories;
2401     while (elem) {
2402         gchar *pathname = g_strdup_printf("%s%s%s", (gchar*)elem->data,
2403                 G_DIR_SEPARATOR_S, filename);
2404         if (g_file_test (pathname, G_FILE_TEST_EXISTS))
2405             return pathname;
2406         g_free (pathname);
2407         elem = elem->next;
2408     }
2409     return nullptr;
2410 }
2411 
2412 
2413 ///////////////////////////////////////////////////////////////////////////////
2414 ///////////////////////////////////////////////////////////////////////////////
2415 ///                                                                         ///
2416 ///                             Event Handlers                              ///
2417 ///                                                                         ///
2418 ///////////////////////////////////////////////////////////////////////////////
2419 ///////////////////////////////////////////////////////////////////////////////
2420 
2421 gboolean
configureEvent(GtkWidget * const,GdkEventConfigure * const event,const gpointer data)2422 configureEvent(GtkWidget *const /*widget*/, GdkEventConfigure *const event,
2423         const gpointer data)
2424 {
2425     GtkGui* obj = static_cast<GtkGui*>(data);
2426     obj->resize_view(event->width, event->height);
2427 
2428     return false;
2429 }
2430 
2431 gboolean
realizeEvent(GtkWidget *,GdkEvent *,gpointer)2432 realizeEvent(GtkWidget* /*widget*/, GdkEvent* /*event*/, gpointer /*data*/)
2433 {
2434     return true;
2435 }
2436 
2437 // Shut everything down and exit when we're destroyed as a window
2438 gboolean
deleteEvent(GtkWidget *,GdkEvent *,gpointer data)2439 deleteEvent(GtkWidget* /*widget*/, GdkEvent* /*event*/, gpointer data)
2440 {
2441     Gui* gui = static_cast<Gui*>(data);
2442     gui->quit();
2443     return true;
2444 }
2445 
2446 
2447 gboolean
keyPressEvent(GtkWidget * const,GdkEventKey * const event,const gpointer data)2448 keyPressEvent(GtkWidget *const /*widget*/, GdkEventKey *const event,
2449         const gpointer data)
2450 {
2451 
2452     Gui* gui = static_cast<Gui*>(data);
2453 
2454     /* Forward key event to gnash */
2455     key::code c = gdk_to_gnash_key(event->keyval);
2456     int mod = gdk_to_gnash_modifier(event->state);
2457 
2458     if (c != key::INVALID) {
2459         gui->notify_key_event(c, mod, true);
2460     }
2461 
2462     return true;
2463 }
2464 
2465 gboolean
keyReleaseEvent(GtkWidget * const,GdkEventKey * const event,const gpointer data)2466 keyReleaseEvent(GtkWidget *const /*widget*/, GdkEventKey *const event,
2467         const gpointer data)
2468 {
2469 
2470     Gui* gui = static_cast<Gui*>(data);
2471 
2472     /* Forward key event to gnash */
2473     key::code    c = gdk_to_gnash_key(event->keyval);
2474     int mod = gdk_to_gnash_modifier(event->state);
2475 
2476     if (c != key::INVALID) {
2477         gui->notify_key_event(c, mod, false);
2478     }
2479 
2480     return true;
2481 }
2482 
2483 gboolean
mouseWheelEvent(GtkWidget * const,GdkEventScroll * const event,const gpointer data)2484 mouseWheelEvent(GtkWidget *const /*widget*/, GdkEventScroll* const event,
2485         const gpointer data)
2486 {
2487     assert(event->type == GDK_SCROLL);
2488     GtkGui *obj = static_cast<GtkGui*>(data);
2489 
2490     obj->grabFocus();
2491 
2492     switch (event->direction) {
2493         case GDK_SCROLL_UP:
2494             obj->notifyMouseWheel(1);
2495             break;
2496         case GDK_SCROLL_DOWN:
2497             obj->notifyMouseWheel(-1);
2498             break;
2499         default:
2500             break;
2501     }
2502 
2503 
2504     return true;
2505 }
2506 
2507 gboolean
buttonPressEvent(GtkWidget * const,GdkEventButton * const event,const gpointer data)2508 buttonPressEvent(GtkWidget *const /*widget*/, GdkEventButton *const event,
2509         const gpointer data)
2510 {
2511 
2512     /// Double- and triple-clicks should not send an extra event!
2513     /// Flash has no built-in double click.
2514     if (event->type != GDK_BUTTON_PRESS) return false;
2515 
2516     GtkGui *obj = static_cast<GtkGui*>(data);
2517 
2518     obj->grabFocus();
2519     obj->notifyMouseClick(true);
2520     return true;
2521 }
2522 
2523 gboolean
buttonReleaseEvent(GtkWidget * const,GdkEventButton * const,const gpointer data)2524 buttonReleaseEvent(GtkWidget * const /*widget*/,
2525      GdkEventButton* const /*event*/, const gpointer data)
2526 {
2527     Gui *obj = static_cast<Gui*>(data);
2528     obj->notifyMouseClick(false);
2529     return true;
2530 }
2531 
2532 gboolean
motionNotifyEvent(GtkWidget * const,GdkEventMotion * const event,const gpointer data)2533 motionNotifyEvent(GtkWidget *const /*widget*/, GdkEventMotion *const event,
2534         const gpointer data)
2535 {
2536     Gui *obj = static_cast<Gui *>(data);
2537 
2538     obj->notifyMouseMove(event->x, event->y);
2539     return true;
2540 }
2541 
2542 gboolean
visibilityNotifyEvent(GtkWidget * const,GdkEventVisibility * const event,const gpointer data)2543 visibilityNotifyEvent(GtkWidget *const /*widget*/, GdkEventVisibility  *const event,
2544                   const gpointer data)
2545 {
2546     GtkGui *obj = static_cast<GtkGui *>(data);
2547 
2548     switch (event->state) {
2549         case GDK_VISIBILITY_FULLY_OBSCURED:
2550             obj->setVisible(false);
2551             break;
2552         case GDK_VISIBILITY_PARTIAL:
2553         case GDK_VISIBILITY_UNOBSCURED:
2554             obj->setVisible(true);
2555             break;
2556     }
2557 
2558     return false; // propagate the event to other listeners, if any.
2559 }
2560 
2561 ///////////////////////////////////////////////////////////////////////////////
2562 ///////////////////////////////////////////////////////////////////////////////
2563 ///                                                                         ///
2564 ///                             Callbacks                                   ///
2565 ///                                                                         ///
2566 ///////////////////////////////////////////////////////////////////////////////
2567 ///////////////////////////////////////////////////////////////////////////////
2568 
2569 /// This method is called when the "OK" button is clicked in the open file
2570 /// dialog. For GTK <= 2.4.0, this is a callback called by GTK itself.
2571 void
openFile(GtkWidget * widget,gpointer)2572 openFile(GtkWidget *widget, gpointer /* user_data */)
2573 {
2574 #if 0
2575     // We'll need this when implementing file opening.
2576     GtkGui* gui = static_cast<GtkGui*>(user_data);
2577 #endif
2578 
2579 #if GTK_CHECK_VERSION(2,4,0)
2580     char* filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
2581 #else
2582     GtkWidget* file_selector = gtk_widget_get_ancestor(widget,
2583                                  g_type_from_name("GtkFileSelection"));
2584 
2585     GtkFileSelection* filesel = GTK_FILE_SELECTION (file_selector);
2586     const char* filename = gtk_file_selection_get_filename (filesel);
2587 #endif
2588 
2589     // FIXME: we want to do something like calling gtk_main_quit here, so
2590     // run() will return. If run() is then changed to return a pointer to the
2591     // next file to be played, then the Player class can play the next file,
2592     // unless run() returns NULL.
2593     log_error(_("Attempting to open file %s.\n"
2594                "NOTE: the file open functionality is not yet implemented!"),
2595                filename);
2596 
2597 #if GTK_CHECK_VERSION(2,4,0)
2598     g_free(filename);
2599 #endif
2600 }
2601 
2602 
2603 void
menuOpenFile(GtkMenuItem *,gpointer data)2604 menuOpenFile(GtkMenuItem* /*menuitem*/, gpointer data)
2605 {
2606     GtkWidget* dialog;
2607     GtkGui* gui = static_cast<GtkGui*>(data);
2608 
2609 #if GTK_CHECK_VERSION(2,4,0)
2610     dialog = gtk_file_chooser_dialog_new (_("Open file"),
2611                                           nullptr,
2612                                           GTK_FILE_CHOOSER_ACTION_OPEN,
2613                                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2614                                           GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2615                                           nullptr);
2616 
2617     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
2618         openFile(dialog, gui);
2619     }
2620 
2621     gtk_widget_destroy (dialog);
2622 #else
2623     dialog = gtk_file_selection_new (_("Open file"));
2624 
2625     GtkFileSelection* selector = GTK_FILE_SELECTION(dialog);
2626 
2627     g_signal_connect (selector->ok_button, "clicked", G_CALLBACK (openFile),
2628                       gui);
2629 
2630     g_signal_connect_swapped(selector->ok_button, "clicked",
2631                               G_CALLBACK(gtk_widget_destroy), dialog);
2632 
2633     g_signal_connect_swapped(selector->cancel_button, "clicked",
2634                               G_CALLBACK(gtk_widget_destroy), dialog);
2635 
2636     gtk_widget_show (dialog);
2637 #endif // GTK_CHECK_VERSION(2,4,0)
2638 }
2639 
2640 
2641 /// 'About' callback
2642 void
menuAbout(GtkMenuItem *,gpointer data)2643 menuAbout(GtkMenuItem* /*menuitem*/, gpointer data)
2644 {
2645     GtkGui* gui = static_cast<GtkGui*>(data);
2646     gui->showAboutDialog();
2647 }
2648 
2649 /// Preferences callback
2650 void
menuPreferences(GtkMenuItem *,gpointer data)2651 menuPreferences(GtkMenuItem* /*menuitem*/, gpointer data)
2652 {
2653     GtkGui* gui = static_cast<GtkGui*>(data);
2654     gui->showPreferencesDialog();
2655 }
2656 
2657 // Properties Callback
2658 void
menuMovieInfo(GtkMenuItem *,gpointer data)2659 menuMovieInfo(GtkMenuItem* /*menuitem*/, gpointer data)
2660 {
2661     GtkGui* gui = static_cast<GtkGui*>(data);
2662     gui->showPropertiesDialog();
2663 }
2664 
2665 /// \brief This pops up the menu when the right mouse button is clicked
2666 gint
popupHandler(GtkWidget * widget,GdkEvent * event)2667 popupHandler(GtkWidget *widget, GdkEvent *event)
2668 {
2669     GtkMenu *menu = GTK_MENU(widget);
2670 
2671     if( _showMenuState ) {
2672         if (event->type == GDK_BUTTON_PRESS) {
2673             GdkEventButton* event_button =
2674                             reinterpret_cast<GdkEventButton*>(event);
2675             if (event_button->button == 3) {
2676                 gtk_menu_popup(menu, nullptr, nullptr, nullptr, nullptr,
2677                                event_button->button, event_button->time);
2678                 return TRUE;
2679             }
2680         }
2681     }
2682 
2683     return FALSE;
2684 }
2685 
2686 /// \brief This handles the alternative popup for showMenu
2687 gint
popupHandlerAlt(GtkWidget * widget,GdkEvent * event)2688 popupHandlerAlt(GtkWidget *widget, GdkEvent *event)
2689 {
2690     GtkMenu *menu = GTK_MENU(widget);
2691 
2692 
2693     if( !_showMenuState ) {
2694         if (event->type == GDK_BUTTON_PRESS) {
2695             GdkEventButton* event_button =
2696                             reinterpret_cast<GdkEventButton*>(event);
2697             if (event_button->button == 3) {
2698                 gtk_menu_popup(menu, nullptr, nullptr, nullptr, nullptr,
2699                                event_button->button, event_button->time);
2700                 return TRUE;
2701             }
2702         }
2703     }
2704 
2705     return FALSE;
2706 }
2707 
2708 /// \brief Toggle the sound on or off
2709 void
menuSound(GtkMenuItem *,gpointer data)2710 menuSound(GtkMenuItem* /*menuitem*/, gpointer data)
2711 {
2712     Gui* gui = static_cast<Gui*>(data);
2713     gui->toggleSound();
2714 }
2715 
2716 void
menuFullscreen(GtkMenuItem *,gpointer data)2717 menuFullscreen(GtkMenuItem* /*menuitem*/, gpointer data)
2718 {
2719     Gui* gui = static_cast<Gui*>(data);
2720     gui->toggleFullscreen();
2721 }
2722 
2723 void
timeoutQuit(gpointer data)2724 timeoutQuit(gpointer data)
2725 {
2726     Gui* gui = static_cast<Gui*>(data);
2727     gui->quit();
2728 }
2729 
2730 
2731 /// \brief restart the movie from the beginning
2732 void
menuRestart(GtkMenuItem *,gpointer data)2733 menuRestart(GtkMenuItem* /*menuitem*/, gpointer data)
2734 {
2735     Gui* gui = static_cast<Gui*>(data);
2736     gui->restart();
2737 }
2738 
2739 void
menuQuit(GtkMenuItem *,gpointer data)2740 menuQuit(GtkMenuItem* /*menuitem*/, gpointer data)
2741 {
2742     Gui* gui = static_cast<Gui*>(data);
2743     gui->quit();
2744 }
2745 
2746 /// \brief Start the movie playing from the current frame.
2747 void
menuPlay(GtkMenuItem *,gpointer data)2748 menuPlay(GtkMenuItem* /*menuitem*/, gpointer data)
2749 {
2750     Gui* gui = static_cast<Gui*>(data);
2751     gui->play();
2752 }
2753 
2754 /// \brief toggle between playing or paused.
2755 void
menuPause(GtkMenuItem *,gpointer data)2756 menuPause(GtkMenuItem* /*menuitem*/, gpointer data)
2757 {
2758     Gui* gui = static_cast<Gui*>(data);
2759     gui->pause();
2760 }
2761 
2762 /// \brief stop the movie that's playing.
2763 void
menuStop(GtkMenuItem *,gpointer data)2764 menuStop(GtkMenuItem* /*menuitem*/, gpointer data)
2765 {
2766     Gui* gui = static_cast<Gui*>(data);
2767     gui->stop();
2768 }
2769 
2770 
2771 /// \brief Force redraw
2772 void
menuRefreshView(GtkMenuItem *,gpointer data)2773 menuRefreshView(GtkMenuItem* /*menuitem*/, gpointer data)
2774 {
2775     Gui* gui = static_cast<Gui*>(data);
2776     gui->refreshView();
2777 }
2778 
2779 /// \brief Force redraw
2780 void
menuShowUpdatedRegions(GtkMenuItem *,gpointer data)2781 menuShowUpdatedRegions(GtkMenuItem* /*menuitem*/, gpointer data)
2782 {
2783     Gui* gui = static_cast<Gui*>(data);
2784     gui->showUpdatedRegions(!gui->showUpdatedRegions());
2785 
2786     // refresh to clear the remaining red lines...
2787     if (!gui->showUpdatedRegions()) {
2788         gui->refreshView();
2789     }
2790 }
2791 
2792 /// \brief Set quality to LOW level
2793 void
menuQualityLow(GtkMenuItem *,gpointer data)2794 menuQualityLow(GtkMenuItem* /*menuitem*/, gpointer data)
2795 {
2796     Gui* gui = static_cast<Gui*>(data);
2797     gui->setQuality(QUALITY_LOW);
2798 }
2799 
2800 /// \brief Set quality to MEDIUM level
2801 void
menuQualityMedium(GtkMenuItem *,gpointer data)2802 menuQualityMedium(GtkMenuItem* /*menuitem*/, gpointer data)
2803 {
2804     Gui* gui = static_cast<Gui*>(data);
2805     gui->setQuality(QUALITY_MEDIUM);
2806 }
2807 
2808 /// \brief Set quality to HIGH level
2809 void
menuQualityHigh(GtkMenuItem *,gpointer data)2810 menuQualityHigh(GtkMenuItem* /*menuitem*/, gpointer data)
2811 {
2812     Gui* gui = static_cast<Gui*>(data);
2813     gui->setQuality(QUALITY_HIGH);
2814 }
2815 
2816 /// \brief Set quality to BEST level
2817 void
menuQualityBest(GtkMenuItem *,gpointer data)2818 menuQualityBest(GtkMenuItem* /*menuitem*/, gpointer data)
2819 {
2820     Gui* gui = static_cast<Gui*>(data);
2821     gui->setQuality(QUALITY_BEST);
2822 }
2823 
2824 } // anonymous namespace
2825 
2826 } // end of namespace gnash
2827 
2828 // local Variables:
2829 // mode: C++
2830 // indent-tabs-mode: nil
2831 // End:
2832