1 // Qt4Gui.cpp: KDE4/Qt4 Gui implementation for Gnash window
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 <map>
26 
27 #include <QMainWindow>
28 #include <QX11Info>
29 #include <QMenu>
30 #include <QMenuBar>
31 #include <QWidget>
32 #include <QCursor>
33 #include <QApplication>
34 #include <QPainter>
35 #include <QMouseEvent>
36 #include <QResizeEvent>
37 #include <QTimer>
38 #include <QEvent>
39 #include <QDialog>
40 #include <QDialogButtonBox>
41 #include <QLayout>
42 #include <QPushButton>
43 #include <QTreeWidget>
44 #include <QTreeWidgetItem>
45 #include <QStack>
46 #include <QTabWidget>
47 #include <QFrame>
48 #include <QLabel>
49 #include <QSlider>
50 #include <QLineEdit>
51 #include <QCheckBox>
52 #include <QLCDNumber>
53 #include <QSpinBox>
54 #include <QClipboard>
55 #include <QString>
56 #include <QDesktopWidget>
57 #include <QMessageBox>
58 
59 #include "Range2d.h"
60 
61 #include "movie_definition.h"
62 #include "log.h"
63 
64 #include "gui.h"
65 #include "Qt4Gui.h"
66 #include "Qt4Gui.moc"
67 #include "Renderer.h"
68 #include "RunResources.h"
69 #include "GnashException.h"
70 
71 // Macro for using gettext strings where Qt expects QStrings
72 #define _q(Str) QString::fromUtf8(_(Str))
73 
74 extern "C" {
75 #include <X11/Xlib.h>
76 }
77 
78 namespace gnash
79 {
80 
Qt4Gui(unsigned long xid,float scale,bool loop,RunResources & r)81 Qt4Gui::Qt4Gui(unsigned long xid, float scale, bool loop, RunResources& r)
82     :
83     Gui(xid, scale, loop, r),
84     _numArgs(0),
85     _embedWidget(NULL),
86     _drawingWidget(NULL),
87     _interval(0),
88     _advanceTimer(0)
89     , fileMenu(NULL)
90     , propertiesAction(NULL)
91     , quitAction(NULL)
92     , editMenu(NULL)
93     , preferencesAction(NULL)
94     , movieControlMenu(NULL)
95     , playAction(NULL)
96     , pauseAction(NULL)
97     , stopAction(NULL)
98     , restartAction(NULL)
99     , viewMenu(NULL)
100     , refreshAction(NULL)
101     , fullscreenAction(NULL)
102 {
103 }
104 
105 
~Qt4Gui()106 Qt4Gui::~Qt4Gui()
107 {
108 }
109 
110 void
setClipboard(const std::string & copy)111 Qt4Gui::setClipboard(const std::string& copy)
112 {
113     QClipboard* cb = QApplication::clipboard();
114     assert(cb);
115     cb->setText(QString::fromStdString(copy));
116 }
117 
118 bool
init(int,char ** [])119 Qt4Gui::init(int /*argc*/, char ** /*argv*/[])
120 {
121     char** r = NULL;
122 
123     _application.reset(new QApplication(_numArgs, r));
124     _window.reset(new QMainWindow());
125     _embedWidget = new EmbedWidget(*this);
126     _drawingWidget = _embedWidget->drawingWidget();
127 
128     std::string renderer = _runResources.getRenderBackend();
129 
130     if (renderer == "cairo") {
131 #ifdef RENDERER_CAIRO
132         log_debug("Using Cairo renderer");
133         _glue.reset(new Qt4CairoGlue());
134 #else
135         log_error(_("Cairo renderer not supported!"));
136         return false;
137 #endif
138     } else if (renderer == "opengl") {
139 #ifdef RENDERER_OPENGL
140         log_debug("Using OpenGL renderer");
141         _glue.reset(new Qt4OglGlue());
142 #else
143         log_error(_("OpenGL renderer not supported!"));
144         return false;
145 #endif
146     } else if (renderer == "agg") {
147 #ifdef RENDERER_AGG
148         log_debug("Using AGG renderer");
149         _glue.reset(new Qt4AggGlue());
150 #else
151         log_error(_("AGG renderer not supported!"));
152         return false;
153 #endif
154     }
155     else {
156         boost::format fmt = boost::format("Non-existent renderer %1% "
157             "specified") % renderer;
158         throw gnash::GnashException(fmt.str());
159     }
160 
161     setupActions();
162     setupMenus();
163 
164     if (!_xid) {
165         createMainMenu();
166     }
167 
168     // Make sure key events are ready to be passed
169     // before the widget can receive them.
170     setupKeyMap();
171 
172     return true;
173 }
174 
175 bool
run()176 Qt4Gui::run()
177 {
178     return _application->exec();
179 }
180 
181 bool
createWindow(const char * windowtitle,int width,int height,int xPosition,int yPosition)182 Qt4Gui::createWindow(const char* windowtitle, int width, int height,
183                      int xPosition, int yPosition)
184 {
185     _width = width;
186     _height = height;
187 
188     _drawingWidget->setMinimumSize(_width, _height);
189 
190     // Enable receiving of mouse events.
191     _drawingWidget->setMouseTracking(true);
192     _drawingWidget->setFocusPolicy(Qt::StrongFocus);
193     _window->setWindowTitle(windowtitle);
194     _window->setWindowIcon(QIcon(PKGDATADIR"/GnashG.png"));
195 
196     if(_xid) {
197         _embedWidget->embedInto(_xid);
198         _embedWidget->show();
199         // Adjust width and height to the window we're being embedded into...
200         XWindowAttributes winAttributes;
201         XGetWindowAttributes(QX11Info::display(), _xid, &winAttributes);
202         _width=winAttributes.width;
203         _height=winAttributes.height;
204     } else {
205         // The QMainWindow takes ownership of the widgets.
206         _window->setCentralWidget(_embedWidget);
207         if (xPosition > -1 || yPosition > -1)
208             _window->move(xPosition, yPosition);
209         _window->show();
210     }
211 
212     _glue->prepDrawingArea(_drawingWidget);
213 
214     _renderer.reset(_glue->createRenderHandler());
215 
216     if (!_renderer.get()) {
217         return false;
218     }
219 
220     _validbounds.setTo(0, 0, _width, _height);
221     _glue->initBuffer(_width, _height);
222 
223     // log_debug("Setting renderer");
224 
225     _runResources.setRenderer(_renderer);
226 
227     // log_debug("Set renderer");
228 
229     return true;
230 }
231 
232 
233 void
resizeWindow(int width,int height)234 Qt4Gui::resizeWindow(int width, int height)
235 {
236     _width = width;
237     _height = height;
238 
239     _drawingWidget->setMinimumSize(_width, _height);
240 }
241 
242 void
popupMenu(const QPoint & point)243 Qt4Gui::popupMenu(const QPoint& point)
244 {
245     QMenu popupMenu(_drawingWidget);
246     popupMenu.addMenu(fileMenu);
247     popupMenu.addMenu(editMenu);
248     popupMenu.addMenu(movieControlMenu);
249     popupMenu.addMenu(viewMenu);
250     popupMenu.exec(point);
251 }
252 
253 
254 void
renderBuffer()255 Qt4Gui::renderBuffer()
256 {
257 
258     for (DrawBounds::const_iterator i = _drawbounds.begin(),
259                         e = _drawbounds.end(); i != e; ++i) {
260 
261         // it may happen that a particular range is out of the screen, which
262         // will lead to bounds==null.
263         if (i->isNull()) continue;
264 
265         assert(i->isFinite());
266 
267         _drawingWidget->update(i->getMinX(), i->getMinY(),
268                                i->width(), i->height());
269 
270     }
271 }
272 
273 void
renderWidget(const QRect & updateRect)274 Qt4Gui::renderWidget(const QRect& updateRect)
275 {
276     // This call renders onto the widget using a QPainter,
277     // which *must only happen inside a paint event*.
278     _glue->render(updateRect);
279 }
280 
281 void
setInvalidatedRegions(const InvalidatedRanges & ranges)282 Qt4Gui::setInvalidatedRegions(const InvalidatedRanges& ranges)
283 {
284     _renderer->set_invalidated_regions(ranges);
285 
286     _drawbounds.clear();
287 
288     for (size_t i = 0, e = ranges.size(); i != e; ++i) {
289 
290         geometry::Range2d<int> bounds = Intersection(
291         _renderer->world_to_pixel(ranges.getRange(i)),
292         _validbounds);
293 
294         // It may happen that a particular range is out of the screen, which
295         // will lead to bounds==null.
296         if (bounds.isNull()) continue;
297 
298         assert(bounds.isFinite());
299 
300         _drawbounds.push_back(bounds);
301 
302     }
303 }
304 
305 void
setTimeout(unsigned int timeout)306 Qt4Gui::setTimeout(unsigned int timeout)
307 {
308     // This must go through Gui::quit() to make sure screenshots are
309     // handled if necessary.
310     QTimer::singleShot(timeout, _drawingWidget, SLOT(quit()));
311 }
312 
313 void
setInterval(unsigned int interval)314 Qt4Gui::setInterval(unsigned int interval)
315 {
316     _interval = interval;
317     _advanceTimer = _drawingWidget->startTimer(_interval);
318 }
319 
320 void
setCursor(gnash_cursor_type newcursor)321 Qt4Gui::setCursor(gnash_cursor_type newcursor)
322 {
323     if (! _mouseShown) return;
324 
325     switch (newcursor) {
326         case CURSOR_HAND:
327             _drawingWidget->setCursor(Qt::PointingHandCursor);
328             break;
329         case CURSOR_INPUT:
330             _drawingWidget->setCursor(Qt::IBeamCursor);
331             break;
332         default:
333             _drawingWidget->unsetCursor();
334     }
335 }
336 
337 bool
showMouse(bool show)338 Qt4Gui::showMouse(bool show)
339 {
340     bool prevState = _mouseShown;
341     _mouseShown = show;
342 
343     if (show) {
344         _drawingWidget->unsetCursor();
345     }
346     else {
347         _drawingWidget->setCursor(Qt::BlankCursor);
348     }
349 
350     return prevState;
351 }
352 
353 void
setFullscreen()354 Qt4Gui::setFullscreen()
355 {
356     _fullscreen = true;
357     fullscreenAction->setChecked(_fullscreen);
358 
359     _embedWidget->setWindowFlags(Qt::Window);
360     _embedWidget->showFullScreen();
361 }
362 
363 void
unsetFullscreen()364 Qt4Gui::unsetFullscreen()
365 {
366     _fullscreen = false;
367     fullscreenAction->setChecked(_fullscreen);
368 
369     if (_embedWidget->isFullScreen()) {
370         _embedWidget->setWindowFlags(Qt::Widget);
371         _embedWidget->showNormal();
372         if (_xid) {
373             _embedWidget->embedInto(_xid);
374         }
375     }
376 }
377 
378 double
getScreenDPI() const379 Qt4Gui::getScreenDPI() const
380 {
381     assert(_drawingWidget);
382     // Should this be logical or physical DPI?
383     return _drawingWidget->logicalDpiX();
384 }
385 
386 std::pair<int, int>
screenResolution() const387 Qt4Gui::screenResolution() const
388 {
389     QDesktopWidget* d = QApplication::desktop();
390     assert(d);
391 
392     const QRect c = d->screenGeometry();
393     return std::make_pair(c.width(), c.height());
394 }
395 
396 gnash::key::code
qtToGnashKey(QKeyEvent * event)397 Qt4Gui::qtToGnashKey(QKeyEvent *event)
398 {
399 
400     // This should be initialized by now.
401     assert (!_keyMap.empty());
402 
403     // Gnash uses its own keycodes to map key events
404     // to the three sometimes weird and confusing values that flash movies
405     // can refer to. See GnashKey.h for the keycodes and map.
406     //
407     // Gnash's keycodes are gnash::key::code. They are mainly in ascii order.
408     // Standard ascii characters (32-127) have the same value. Extended ascii
409     // characters (160-254) are in ascii order but correspond to
410     // gnash::key::code
411     // 169-263. Non-character values must normally be mapped separately.
412 
413     const int key = event->key();
414 
415     if (key >= Qt::Key_0 && key <= Qt::Key_9) {
416           return static_cast<gnash::key::code>(
417                 key - Qt::Key_0 + gnash::key::_0);
418     }
419 
420     // All other characters between ascii 32 and 126 are simple.
421     // From space (32) to slash (47):
422     else if (key >= Qt::Key_Space && key <= Qt::Key_AsciiTilde) {
423         return static_cast<gnash::key::code>(
424                 key - Qt::Key_Space + gnash::key::SPACE);
425     }
426 
427     // Function keys:
428     else if (key >= Qt::Key_F1 && key <= Qt::Key_F15) {
429         return static_cast<gnash::key::code>(
430                 key - Qt::Key_F1 + gnash::key::F1);
431     }
432 
433     // Extended ascii from non-breaking (160) space to ÿ (264) is in the same
434     // order.
435     else if (key >= Qt::Key_nobreakspace && key <= Qt::Key_ydiaeresis) {
436         return static_cast<gnash::key::code>(
437                 key - Qt::Key_nobreakspace + gnash::key::NOBREAKSPACE);
438     }
439 
440     const KeyMap::const_iterator it = _keyMap.find(key);
441 
442     if (it == _keyMap.end()) return gnash::key::INVALID;
443 
444     return it->second;
445 
446 }
447 
448 int
qtToGnashModifier(const Qt::KeyboardModifiers modifiers)449 Qt4Gui::qtToGnashModifier(const Qt::KeyboardModifiers modifiers)
450 {
451     int gnashModifier = gnash::key::GNASH_MOD_NONE;
452 
453     if (modifiers & Qt::ShiftModifier)
454         gnashModifier = gnashModifier | gnash::key::GNASH_MOD_SHIFT;
455     if (modifiers & Qt::ControlModifier)
456         gnashModifier = gnashModifier | gnash::key::GNASH_MOD_CONTROL;
457     if (modifiers & Qt::AltModifier)
458         gnashModifier = gnashModifier | gnash::key::GNASH_MOD_ALT;
459 
460     return gnashModifier;
461 }
462 
463 void
handleKeyEvent(QKeyEvent * event,bool down)464 Qt4Gui::handleKeyEvent(QKeyEvent *event, bool down)
465 {
466     gnash::key::code c = qtToGnashKey(event);
467     int mod = qtToGnashModifier(event->modifiers());
468     notify_key_event(c, mod, down);
469 }
470 
471 void
resize(int width,int height)472 Qt4Gui::resize(int width, int height)
473 {
474     _glue->resize(width, height);
475     resize_view(width, height);
476 }
477 
478 void
showProperties()479 Qt4Gui::showProperties()
480 {
481     QDialog* propsDialog = new QDialog(_drawingWidget);
482     propsDialog->setWindowTitle(_q("Movie properties"));
483     propsDialog->setAttribute(Qt::WA_DeleteOnClose);
484     propsDialog->resize(500, 300);
485 
486     QDialogButtonBox *dialogButtons = new QDialogButtonBox(
487                  QDialogButtonBox::Close, Qt::Horizontal, propsDialog);
488     dialogButtons->button(QDialogButtonBox::Close)->setDefault(true);
489 
490     QVBoxLayout* layout = new QVBoxLayout(propsDialog);
491     propsDialog->connect(dialogButtons->button(QDialogButtonBox::Close),
492             SIGNAL(clicked()), SLOT(close()));
493 
494 #ifdef USE_SWFTREE
495     std::unique_ptr<movie_root::InfoTree> infoptr = getMovieInfo();
496     const movie_root::InfoTree& info = *infoptr;
497 
498     QTreeWidget *tree = new QTreeWidget();
499     tree->setColumnCount(2);
500     QStringList treeHeader;
501     treeHeader.append(_q("Variable"));
502     treeHeader.append(_q("Value"));
503     tree->setHeaderLabels(treeHeader);
504 
505     QList<QTreeWidgetItem *> items;
506 
507     int prevDepth = 0;
508     QStack<QTreeWidgetItem*> stack;
509     for (movie_root::InfoTree::iterator i = info.begin(), e = info.end();
510             i != e; ++i) {
511 
512         const movie_root::InfoTree::value_type& p = *i;
513 
514         QStringList cols;
515         cols.append(p.first.c_str());
516         cols.append(p.second.c_str());
517         QTreeWidgetItem* item = new QTreeWidgetItem(cols);
518 
519         int newDepth = info.depth(i);
520 
521         if (newDepth == 0) {
522             // Insert top level entries directly into the tree widget.
523             items.append(item);
524             stack.clear();
525         } else {
526             // The position to insert the new row.
527             QTreeWidgetItem* parent = NULL;
528 
529             if (newDepth == prevDepth ) {
530                 // Pop an extra time if there is a sibling on the stack.
531                 int size = stack.size();
532                 if (size + 1 > newDepth)
533                     stack.pop();
534 
535                 parent = stack.pop();
536             } else if (newDepth > prevDepth) {
537                 parent = stack.pop();
538             } else if (newDepth < prevDepth) {
539                 // Pop until the stack has the right depth.
540                 int size = stack.size();
541                 for (int j = 0; j < (size + 1) - newDepth; ++j) {
542                     parent = stack.pop();
543                 }
544             }
545 
546             parent->addChild(item);
547             stack.push(parent);
548         }
549 
550         stack.push(item);
551         prevDepth = newDepth;
552     }
553     tree->insertTopLevelItems(0, items);
554     layout->addWidget(tree);
555 
556 #endif // USE_SWFTREE
557     layout->addWidget(dialogButtons);
558 
559     propsDialog->show();
560     propsDialog->activateWindow();
561 }
562 
563 void
showPreferences()564 Qt4Gui::showPreferences()
565 {
566     Qt4GuiPrefs::PreferencesDialog* prefsDialog = new Qt4GuiPrefs::PreferencesDialog(_drawingWidget);
567 
568     prefsDialog->setAttribute(Qt::WA_DeleteOnClose);
569     prefsDialog->show();
570     prefsDialog->raise();
571     prefsDialog->activateWindow();
572 }
573 
574 bool
yesno(const std::string & question)575 Qt4Gui::yesno(const std::string& question)
576 {
577     _drawingWidget->killTimer(_advanceTimer);
578 
579     QMessageBox* dialog = new QMessageBox(_drawingWidget);
580     dialog->setText(QString::fromStdString(question));
581     dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
582     dialog->setDefaultButton(QMessageBox::Yes);
583     const int ret = dialog->exec();
584 
585     _advanceTimer = _drawingWidget->startTimer(_interval);
586 
587 
588     if (ret == QMessageBox::Yes) return true;
589     return false;
590 }
591 
592 void
quitUI()593 Qt4Gui::quitUI()
594 {
595     _application->quit();
596 }
597 
598 void
setupActions()599 Qt4Gui::setupActions()
600 {
601 
602     // File Menu actions
603     propertiesAction = new QAction(_q("Properties"), _window.get());
604     _drawingWidget->connect(propertiesAction, SIGNAL(triggered()),
605                      _drawingWidget, SLOT(properties()));
606 
607     quitAction = new QAction(_q("Quit Gnash"), _window.get());
608     // This must go through Gui::quit() to make sure we don't exit
609     // before doing whatever the Gui wants to do on exit.
610     _drawingWidget->connect(quitAction, SIGNAL(triggered()),
611                      _drawingWidget, SLOT(quit()));
612 
613     // Edit Menu actions
614     preferencesAction = new QAction(_q("Preferences"), _window.get());
615     _drawingWidget->connect(preferencesAction, SIGNAL(triggered()),
616                      _drawingWidget, SLOT(preferences()));
617 
618     // Movie Control Menu actions
619     playAction = new QAction(_q("Play"), _window.get());
620     _drawingWidget->connect(playAction, SIGNAL(triggered()),
621                      _drawingWidget, SLOT(play()));
622 
623     pauseAction = new QAction(_q("Pause"), _window.get());
624     _drawingWidget->connect(pauseAction, SIGNAL(triggered()),
625                      _drawingWidget, SLOT(pause()));
626 
627     stopAction = new QAction(_q("Stop"), _window.get());
628     _drawingWidget->connect(stopAction, SIGNAL(triggered()),
629                      _drawingWidget, SLOT(stop()));
630 
631     restartAction = new QAction(_q("Restart"), _window.get());
632     _drawingWidget->connect(restartAction, SIGNAL(triggered()),
633                      _drawingWidget, SLOT(restart()));
634 
635     // View Menu actions
636     refreshAction = new QAction(_q("Refresh"), _window.get());
637     _drawingWidget->connect(refreshAction, SIGNAL(triggered()),
638                      _drawingWidget, SLOT(refresh()));
639 
640     fullscreenAction = new QAction(_q("Fullscreen"), _window.get());
641     fullscreenAction->setCheckable(true);
642     _drawingWidget->connect(fullscreenAction, SIGNAL(toggled(bool)),
643                            _drawingWidget, SLOT(fullscreen(bool)));
644 }
645 
646 
647 void
setupMenus()648 Qt4Gui::setupMenus()
649 {
650     /// The menus are children of the QMainWindow so that
651     /// they are destroyed on exit. The QMainWindow already has
652     /// ownership of the main QMenuBar.
653 
654     // Set up the File menu.
655     fileMenu = new QMenu(_q("File"), _window.get());
656     fileMenu->addAction(propertiesAction);
657     fileMenu->addAction(quitAction);
658 
659     // Set up the Edit menu.
660     editMenu = new QMenu(_q("Edit"), _window.get());
661     editMenu->addAction(preferencesAction);
662 
663     // Set up the Movie Control menu
664     movieControlMenu = new QMenu(_q("Movie Control"), _window.get());
665     movieControlMenu->addAction(playAction);
666     movieControlMenu->addAction(pauseAction);
667     movieControlMenu->addAction(stopAction);
668     movieControlMenu->addAction(restartAction);
669 
670     // Set up the View menu
671     viewMenu = new QMenu(_q("View"), _window.get());
672     viewMenu->addAction(refreshAction);
673     viewMenu->addAction(fullscreenAction);
674 }
675 
676 
677 void
createMainMenu()678 Qt4Gui::createMainMenu()
679 {
680     std::unique_ptr<QMenuBar> mainMenu(new QMenuBar);
681 
682     // Set up the menu bar.
683     mainMenu->addMenu(fileMenu);
684     mainMenu->addMenu(editMenu);
685     mainMenu->addMenu(movieControlMenu);
686     mainMenu->addMenu(viewMenu);
687 
688     // The QMainWindow::setMenuBar transfers ownership
689     // of the QMenuBar.
690     _window->setMenuBar(mainMenu.release());
691 
692 }
693 
694 void
setupKeyMap()695 Qt4Gui::setupKeyMap()
696 {
697     // We only want to do this once, although it would not
698     // be harmful to do it more.
699     assert (_keyMap.empty());
700 
701     _keyMap = {
702     {Qt::Key_Backspace, gnash::key::BACKSPACE},
703     {Qt::Key_Tab, gnash::key::TAB},
704     {Qt::Key_Clear, gnash::key::CLEAR},
705     {Qt::Key_Return, gnash::key::ENTER},
706     {Qt::Key_Enter, gnash::key::ENTER},
707     {Qt::Key_Shift, gnash::key::SHIFT},
708     {Qt::Key_Control, gnash::key::CONTROL},
709     {Qt::Key_Alt, gnash::key::ALT},
710     {Qt::Key_CapsLock, gnash::key::CAPSLOCK},
711     {Qt::Key_Escape, gnash::key::ESCAPE},
712     {Qt::Key_Space, gnash::key::SPACE},
713     {Qt::Key_PageDown, gnash::key::PGDN},
714     {Qt::Key_PageUp, gnash::key::PGUP},
715     {Qt::Key_Home, gnash::key::HOME},
716     {Qt::Key_End, gnash::key::END},
717     {Qt::Key_Left, gnash::key::LEFT},
718     {Qt::Key_Up, gnash::key::UP},
719     {Qt::Key_Right, gnash::key::RIGHT},
720     {Qt::Key_Down, gnash::key::DOWN},
721     {Qt::Key_Insert, gnash::key::INSERT},
722     {Qt::Key_Delete, gnash::key::DELETEKEY},
723     {Qt::Key_Help, gnash::key::HELP},
724     {Qt::Key_NumLock, gnash::key::NUM_LOCK},
725     {Qt::Key_Semicolon, gnash::key::SEMICOLON},
726     {Qt::Key_Equal, gnash::key::EQUALS},
727     {Qt::Key_Minus, gnash::key::MINUS},
728     {Qt::Key_Slash, gnash::key::SLASH},
729     {Qt::Key_BracketLeft, gnash::key::LEFT_BRACKET},
730     {Qt::Key_Backslash, gnash::key::BACKSLASH},
731     {Qt::Key_BracketRight, gnash::key::RIGHT_BRACKET},
732     {Qt::Key_QuoteDbl, gnash::key::DOUBLE_QUOTE} };
733 }
734 
735 void
playHook()736 Qt4Gui::playHook()
737 {
738     _embedWidget->hidePlayButton();
739 }
740 
741 void
stopHook()742 Qt4Gui::stopHook()
743 {
744     _embedWidget->showPlayButton();
745 }
746 
747 /// EmbedWidget implementation
748 
EmbedWidget(Qt4Gui & gui)749 EmbedWidget::EmbedWidget(Qt4Gui& gui)
750   : QX11EmbedWidget()
751 {
752     _drawingWidget = new DrawingWidget(gui);
753     _playButton = new QPushButton(_q("Click to Play"), this);
754 
755     QVBoxLayout* layout = new QVBoxLayout(this);
756     layout->setContentsMargins(0,0,0,0);
757     layout->setSpacing(0);
758     layout->addWidget(_playButton);
759     layout->addWidget(_drawingWidget);
760     _playButton->hide();
761 
762     connect(_playButton, SIGNAL(clicked()), this, SLOT(hidePlayButton()));
763     connect(_playButton, SIGNAL(clicked()), _drawingWidget, SLOT(play()));
764 }
765 
766 void
hidePlayButton()767 EmbedWidget::hidePlayButton()
768 {
769     _playButton->hide();
770 }
771 
772 void
showPlayButton()773 EmbedWidget::showPlayButton()
774 {
775     _playButton->show();
776 }
777 
778 namespace Qt4GuiPrefs {
779 
PreferencesDialog(QWidget * parent)780 PreferencesDialog::PreferencesDialog(QWidget* parent)
781     :
782     QDialog(parent),
783     _rcfile(RcInitFile::getDefaultInstance())
784 {
785     setWindowTitle(_q("Gnash preferences"));
786     setAttribute(Qt::WA_DeleteOnClose);
787 
788     QDialogButtonBox *buttons = new QDialogButtonBox(
789                           QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
790 
791     QVBoxLayout *vLayout = new QVBoxLayout(this);
792 
793     QLabel* tmpLabel;
794 
795     // Make notebook pages.
796     QTabWidget *tabs = new QTabWidget;
797 
798     // Logging tab
799     QFrame* loggingTab = new QFrame();
800     tabs->addTab(loggingTab, _q("Logging"));
801     QVBoxLayout* layout = new QVBoxLayout (loggingTab);
802 
803     tmpLabel = new QLabel(_q("<b>Logging options</b>"), loggingTab);
804     layout->addWidget(tmpLabel);
805 
806     tmpLabel = new QLabel(_q("Verbosity level"), loggingTab);
807     layout->addWidget(tmpLabel);
808 
809     QLCDNumber* lcd = new QLCDNumber(loggingTab);
810     lcd->display(_rcfile.verbosityLevel());
811     lcd->setNumDigits(2);
812     layout->addWidget(lcd);
813 
814     _verbositySlider = new QSlider(loggingTab);
815     _verbositySlider->setOrientation(Qt::Horizontal);
816     _verbositySlider->setMaximum(10);
817     _verbositySlider->setSingleStep(1);
818     _verbositySlider->setPageStep(1);
819     _verbositySlider->setValue(_rcfile.verbosityLevel());
820     connect(_verbositySlider, SIGNAL(valueChanged(int)),
821             lcd, SLOT(display(int)));
822     layout->addWidget(_verbositySlider);
823 
824     _logToFileToggle = new QCheckBox(_q("Log to file"), loggingTab);
825     _logToFileToggle->setChecked(_rcfile.useWriteLog());
826     layout->addWidget(_logToFileToggle);
827 
828     _logFileName = new QLineEdit(_rcfile.getDebugLog().c_str(), loggingTab);
829     layout->addWidget(_logFileName);
830 
831     _parserDumpToggle = new QCheckBox(_q("Log parser output"), loggingTab);
832     _parserDumpToggle->setChecked(_rcfile.useParserDump());
833     layout->addWidget(_parserDumpToggle);
834 
835     _actionDumpToggle = new QCheckBox(_q("Log SWF actions"), loggingTab);
836     _actionDumpToggle->setChecked(_rcfile.useActionDump());
837     layout->addWidget(_actionDumpToggle);
838 
839     _malformedSWFToggle = new QCheckBox(_q("Log malformed SWF errors"),
840                                         loggingTab);
841     _malformedSWFToggle->setChecked(_rcfile.showMalformedSWFErrors());
842     layout->addWidget(_malformedSWFToggle);
843 
844     _ASCodingErrorToggle = new QCheckBox(_q("Log ActionScript coding errors"),
845                                         loggingTab);
846     _ASCodingErrorToggle->setChecked(_rcfile.showASCodingErrors());
847     layout->addWidget(_ASCodingErrorToggle);
848 
849     // Security tab
850     QFrame* securityTab = new QFrame(tabs);
851     tabs->addTab(securityTab, _q("Security"));
852     layout = new QVBoxLayout (securityTab);
853 
854     tmpLabel = new QLabel(_q("<b>Network connections</b>"), securityTab);
855     layout->addWidget(tmpLabel);
856 
857     _localHostToggle = new QCheckBox(_q("Connect only to local host"),
858                                      securityTab);
859     _localHostToggle->setChecked(_rcfile.useLocalHost());
860     layout->addWidget(_localHostToggle);
861 
862     _localDomainToggle = new QCheckBox(_q("Connect only to local domain"),
863                                        securityTab);
864     _localDomainToggle->setChecked(_rcfile.useLocalDomain());
865     layout->addWidget(_localDomainToggle);
866 
867     _insecureSSLToggle = new QCheckBox(_q("Disable SSL verification"),
868                                        securityTab);
869     _insecureSSLToggle->setChecked(_rcfile.insecureSSL());
870     layout->addWidget(_insecureSSLToggle);
871 
872     tmpLabel = new QLabel(_q("<b>Privacy</b>"), securityTab);
873     layout->addWidget(tmpLabel);
874 
875     tmpLabel = new QLabel(_q("Shared objects directory:"), securityTab);
876     layout->addWidget(tmpLabel);
877     _solSandboxDir = new QLineEdit(_rcfile.getSOLSafeDir().c_str(),
878                                    securityTab);
879     layout->addWidget(_solSandboxDir);
880 
881     _solReadOnlyToggle = new QCheckBox(_q("Do not write Shared Object files"),
882                                        securityTab);
883     _solReadOnlyToggle->setChecked(_rcfile.getSOLReadOnly());
884     layout->addWidget(_solReadOnlyToggle);
885 
886     _solLocalDomainToggle = new QCheckBox(
887             _q("Only access local Shared Object files"), securityTab);
888     _solLocalDomainToggle->setChecked(_rcfile.getSOLLocalDomain());
889     layout->addWidget(_solLocalDomainToggle);
890 
891     _localConnectionToggle = new QCheckBox(
892             _q("Disable Local Connection object"), securityTab);
893     _localConnectionToggle->setChecked(_rcfile.getLocalConnection());
894     layout->addWidget(_localConnectionToggle);
895     layout->addStretch();
896 
897     // Network tab
898     QFrame* networkTab = new QFrame(tabs);
899     tabs->addTab(networkTab, _q("Network"));
900     layout = new QVBoxLayout (networkTab);
901 
902     tmpLabel = new QLabel(_q("<b>Network preferences</b>"), networkTab);
903     layout->addWidget(tmpLabel);
904 
905     tmpLabel = new QLabel(_q("Network timeout in seconds"), networkTab);
906     layout->addWidget(tmpLabel);
907 
908     _streamsTimeoutScale = new QSpinBox(networkTab);
909     _streamsTimeoutScale->setMinimum(0);
910     _streamsTimeoutScale->setMaximum(300);
911     _streamsTimeoutScale->setValue(_rcfile.getStreamsTimeout());
912     layout->addWidget(_streamsTimeoutScale);
913     layout->addStretch();
914 
915     // Network tab
916     QFrame* mediaTab = new QFrame(tabs);
917     tabs->addTab(mediaTab, _q("Media"));
918     layout = new QVBoxLayout (mediaTab);
919 
920     tmpLabel = new QLabel(_q("<b>Sound</b>"), mediaTab);
921     layout->addWidget(tmpLabel);
922 
923     _soundToggle = new QCheckBox(_q("Use sound handler"), mediaTab);
924     _soundToggle->setChecked(_rcfile.useSound());
925     layout->addWidget(_soundToggle);
926 
927     _saveStreamingMediaToggle = new QCheckBox(_q("Save media streams to disk"),
928                                               mediaTab);
929     _saveStreamingMediaToggle->setChecked(_rcfile.saveStreamingMedia());
930     layout->addWidget(_saveStreamingMediaToggle);
931 
932     _saveLoadedMediaToggle = new QCheckBox(
933                         _q("Save dynamically loaded media to disk"), mediaTab);
934     _saveLoadedMediaToggle->setChecked(_rcfile.saveLoadedMedia());
935     layout->addWidget(_saveLoadedMediaToggle);
936 
937     tmpLabel = new QLabel(_q("Saved media directory:"), mediaTab);
938     layout->addWidget(tmpLabel);
939 
940     _mediaDir = new QLineEdit(_rcfile.getMediaDir().c_str(), mediaTab);
941     layout->addWidget(_mediaDir);
942     layout->addStretch();
943 
944     // Player tab
945     QFrame* playerTab = new QFrame(tabs);
946     tabs->addTab(playerTab, _q("Player"));
947     layout = new QVBoxLayout (playerTab);
948 
949     tmpLabel = new QLabel(_q("<b>Player description</b>"), playerTab);
950     layout->addWidget(tmpLabel);
951 
952     tmpLabel = new QLabel(_q("Player version:"), playerTab);
953     layout->addWidget(tmpLabel);
954     _versionText = new QLineEdit(_rcfile.getFlashVersionString().c_str(),
955                                  playerTab);
956     layout->addWidget(_versionText);
957 
958     tmpLabel = new QLabel(_q("Operating system:"), playerTab);
959     layout->addWidget(tmpLabel);
960 
961     _osText = new QLineEdit(playerTab);
962     if (_rcfile.getFlashSystemOS().empty()) {
963         _osText->setText(_q("<Autodetect>"));
964     } else {
965         _osText->setText(_rcfile.getFlashSystemOS().c_str());
966     }
967     layout->addWidget(_osText);
968 
969     tmpLabel = new QLabel(_q("URL opener:"), playerTab);
970     layout->addWidget(tmpLabel);
971 
972     _urlOpenerText = new QLineEdit(_rcfile.getURLOpenerFormat().c_str(),
973                                  playerTab);
974     layout->addWidget(_urlOpenerText);
975 
976     tmpLabel = new QLabel(_q("<b>Performance</b>"), playerTab);
977     layout->addWidget(tmpLabel);
978 
979     tmpLabel = new QLabel(_q("Max size of movie library:"), playerTab);
980     layout->addWidget(tmpLabel);
981 
982     _librarySize = new QSpinBox(playerTab);
983     _librarySize->setMinimum(0);
984     _librarySize->setMaximum(100);
985     _librarySize->setValue(_rcfile.getMovieLibraryLimit());
986     layout->addWidget(_librarySize);
987 
988     _startStoppedToggle = new QCheckBox(_q("Start Gnash in pause mode"),
989                                         playerTab);
990     _startStoppedToggle->setChecked(_rcfile.startStopped());
991     layout->addWidget(_startStoppedToggle);
992     layout->addStretch();
993     // End of notebook tabs
994 
995     vLayout->addWidget(tabs);
996     vLayout->addStretch();
997     vLayout->addWidget(buttons);
998 
999     // Connect the dialog buttons.
1000     connect(buttons, SIGNAL(accepted()), this, SLOT(savePreferences()));
1001     connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
1002 }
1003 
1004 void
savePreferences()1005 PreferencesDialog::savePreferences()
1006 {
1007     // Logging tab widgets
1008     _rcfile.verbosityLevel(_verbositySlider->value());
1009     _rcfile.useWriteLog(_logToFileToggle->isChecked());
1010     _rcfile.setDebugLog(_logFileName->text().toStdString());
1011     _rcfile.useParserDump(_parserDumpToggle->isChecked());
1012     _rcfile.useActionDump(_actionDumpToggle->isChecked());
1013     _rcfile.showMalformedSWFErrors(_malformedSWFToggle->isChecked());
1014     _rcfile.showASCodingErrors(_ASCodingErrorToggle->isChecked());
1015 
1016     // Security tab widgets
1017     _rcfile.useLocalHost(_localHostToggle->isChecked());
1018     _rcfile.useLocalDomain(_localDomainToggle->isChecked());
1019     _rcfile.insecureSSL(_insecureSSLToggle->isChecked());
1020     _rcfile.setSOLSafeDir(_solSandboxDir->text().toStdString());
1021     _rcfile.setSOLReadOnly(_solReadOnlyToggle->isChecked());
1022     _rcfile.setSOLLocalDomain(_solLocalDomainToggle->isChecked());
1023     _rcfile.setLocalConnection(_localConnectionToggle->isChecked());
1024 
1025     // Network tab widgets
1026     _rcfile.setStreamsTimeout(_streamsTimeoutScale->value());
1027 
1028     // Media tab widgets
1029     _rcfile.useSound(_soundToggle->isChecked());
1030     _rcfile.saveStreamingMedia(_saveStreamingMediaToggle->isChecked());
1031     _rcfile.saveLoadedMedia(_saveLoadedMediaToggle->isChecked());
1032     _rcfile.setMediaDir(_mediaDir->text().toStdString());
1033 
1034     // Player tab widgets
1035     _rcfile.setFlashVersionString(_versionText->text().toStdString());
1036     if (_osText->text() != _q("<Autodetect>")) {
1037         _rcfile.setFlashSystemOS(_osText->text().toStdString());
1038     }
1039     _rcfile.setURLOpenerFormat(_urlOpenerText->text().toStdString());
1040     _rcfile.setMovieLibraryLimit(_librarySize->value());
1041     _rcfile.startStopped(_startStoppedToggle->isChecked());
1042 
1043     // Save the file.
1044     _rcfile.updateFile();
1045 
1046     // Allow the dialog to close normally.
1047     emit accept();
1048 }
1049 
1050 } // End of Qt4GuiPrefs namespace
1051 
1052 }
1053 
1054