]> git.sur5r.net Git - minitube/blob - src/MainWindow.cpp
Imported Upstream version 1.6
[minitube] / src / MainWindow.cpp
1 #include "MainWindow.h"
2 #include "spacer.h"
3 #include "constants.h"
4 #include "iconloader/qticonloader.h"
5 #include "global.h"
6 #include "videodefinition.h"
7 #include "fontutils.h"
8 #include "globalshortcuts.h"
9 #ifdef Q_WS_X11
10 #include "gnomeglobalshortcutbackend.h"
11 #endif
12 #ifdef Q_WS_MAC
13 #include "mac_startup.h"
14 #include "macfullscreen.h"
15 #include "macsupport.h"
16 #endif
17 #ifndef Q_WS_X11
18 #include "extra.h"
19 #endif
20 #include "downloadmanager.h"
21 #include "youtubesuggest.h"
22 #include "updatechecker.h"
23 #ifdef APP_DEMO
24 #include "demostartupview.h"
25 #endif
26 #include "temporary.h"
27
28 MainWindow::MainWindow() :
29         aboutView(0),
30         downloadView(0),
31         mediaObject(0),
32         audioOutput(0),
33         m_fullscreen(false),
34         updateChecker(0) {
35
36     // views mechanism
37     history = new QStack<QWidget*>();
38     views = new QStackedWidget(this);
39     setCentralWidget(views);
40
41     // views
42     searchView = new SearchView(this);
43     connect(searchView, SIGNAL(search(SearchParams*)), this, SLOT(showMedia(SearchParams*)));
44     views->addWidget(searchView);
45
46     mediaView = new MediaView(this);
47     views->addWidget(mediaView);
48
49     toolbarSearch = new SearchLineEdit(this);
50     toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize()*15);
51     toolbarSearch->setSuggester(new YouTubeSuggest(this));
52     connect(toolbarSearch, SIGNAL(search(const QString&)), this, SLOT(startToolbarSearch(const QString&)));
53
54     // build ui
55     createActions();
56     createMenus();
57     createToolBars();
58     createStatusBar();
59
60     initPhonon();
61     // mediaView->setSlider(slider);
62     mediaView->setMediaObject(mediaObject);
63
64     // remove that useless menu/toolbar context menu
65     this->setContextMenuPolicy(Qt::NoContextMenu);
66
67     // mediaView init stuff thats needs actions
68     mediaView->initialize();
69
70     // event filter to block ugly toolbar tooltips
71     qApp->installEventFilter(this);
72
73     // restore window position
74     readSettings();
75
76     // show the initial view
77 #ifdef APP_DEMO
78         QWidget *demoStartupView = new DemoStartupView(this);
79         views->addWidget(demoStartupView);
80         showWidget(demoStartupView);
81 #else
82     showSearch();
83 #endif
84
85     // Global shortcuts
86     GlobalShortcuts &shortcuts = GlobalShortcuts::instance();
87 #ifdef Q_WS_X11
88     if (GnomeGlobalShortcutBackend::IsGsdAvailable())
89         shortcuts.setBackend(new GnomeGlobalShortcutBackend(&shortcuts));
90 #endif
91 #ifdef Q_WS_MAC
92     mac::MacSetup();
93 #endif
94     connect(&shortcuts, SIGNAL(PlayPause()), pauseAct, SLOT(trigger()));
95     connect(&shortcuts, SIGNAL(Stop()), this, SLOT(stop()));
96     connect(&shortcuts, SIGNAL(Next()), skipAct, SLOT(trigger()));
97
98     connect(DownloadManager::instance(), SIGNAL(statusMessageChanged(QString)),
99             SLOT(updateDownloadMessage(QString)));
100     connect(DownloadManager::instance(), SIGNAL(finished()),
101             SLOT(downloadsFinished()));
102
103     setAcceptDrops(true);
104
105     QTimer::singleShot(0, this, SLOT(checkForUpdate()));
106
107 }
108
109 MainWindow::~MainWindow() {
110     delete history;
111 }
112
113 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
114 #ifdef Q_WS_X11
115     if (event->type() == QEvent::MouseMove && this->m_fullscreen) {
116         QMouseEvent *mouseEvent = static_cast<QMouseEvent*> (event);
117         int x = mouseEvent->pos().x();
118         int y = mouseEvent->pos().y();
119
120         if (y < 0 && (obj == this->mainToolBar || !(y <= 10-this->mainToolBar->height() && y >= 0-this->mainToolBar->height() )))
121            this->mainToolBar->setVisible(false);
122         if (x < 0)
123             this->mediaView->setPlaylistVisible(false);
124     }
125 #endif
126
127     if (event->type() == QEvent::ToolTip) {
128         // kill tooltips
129         return true;
130     }
131     // standard event processing
132     return QObject::eventFilter(obj, event);
133 }
134
135 void MainWindow::createActions() {
136
137     QMap<QString, QAction*> *actions = The::globalActions();
138
139     stopAct = new QAction(QtIconLoader::icon("media-playback-stop"), tr("&Stop"), this);
140     stopAct->setStatusTip(tr("Stop playback and go back to the search view"));
141     stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
142     stopAct->setEnabled(false);
143     actions->insert("stop", stopAct);
144     connect(stopAct, SIGNAL(triggered()), this, SLOT(stop()));
145
146     skipAct = new QAction(QtIconLoader::icon("media-skip-forward"), tr("S&kip"), this);
147     skipAct->setStatusTip(tr("Skip to the next video"));
148     skipAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Right) << QKeySequence(Qt::Key_MediaNext));
149     skipAct->setEnabled(false);
150     actions->insert("skip", skipAct);
151     connect(skipAct, SIGNAL(triggered()), mediaView, SLOT(skip()));
152
153     pauseAct = new QAction(QtIconLoader::icon("media-playback-pause"), tr("&Pause"), this);
154     pauseAct->setStatusTip(tr("Pause playback"));
155     pauseAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Space) << QKeySequence(Qt::Key_MediaPlay));
156     pauseAct->setEnabled(false);
157     actions->insert("pause", pauseAct);
158     connect(pauseAct, SIGNAL(triggered()), mediaView, SLOT(pause()));
159
160     fullscreenAct = new QAction(QtIconLoader::icon("view-fullscreen"), tr("&Full Screen"), this);
161     fullscreenAct->setStatusTip(tr("Go full screen"));
162     QList<QKeySequence> fsShortcuts;
163 #ifdef APP_MAC
164     fsShortcuts << QKeySequence(Qt::CTRL + Qt::META + Qt::Key_F);
165 #else
166     fsShortcuts << QKeySequence(Qt::Key_F11);
167 #endif
168     fullscreenAct->setShortcuts(fsShortcuts);
169     fullscreenAct->setShortcutContext(Qt::ApplicationShortcut);
170 #if QT_VERSION >= 0x040600
171     fullscreenAct->setPriority(QAction::LowPriority);
172 #endif
173     actions->insert("fullscreen", fullscreenAct);
174     connect(fullscreenAct, SIGNAL(triggered()), this, SLOT(fullscreen()));
175
176     compactViewAct = new QAction(tr("&Compact mode"), this);
177     compactViewAct->setStatusTip(tr("Hide the playlist and the toolbar"));
178 #ifdef APP_MAC
179     compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::META + Qt::Key_C));
180 #else
181     compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C));
182 #endif
183     compactViewAct->setCheckable(true);
184     compactViewAct->setChecked(false);
185     compactViewAct->setEnabled(false);
186     actions->insert("compactView", compactViewAct);
187     connect(compactViewAct, SIGNAL(toggled(bool)), this, SLOT(compactView(bool)));
188
189     webPageAct = new QAction(tr("Open the &YouTube page"), this);
190     webPageAct->setStatusTip(tr("Go to the YouTube video page and pause playback"));
191     webPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y));
192     webPageAct->setEnabled(false);
193     actions->insert("webpage", webPageAct);
194     connect(webPageAct, SIGNAL(triggered()), mediaView, SLOT(openWebPage()));
195
196     copyPageAct = new QAction(tr("Copy the YouTube &link"), this);
197     copyPageAct->setStatusTip(tr("Copy the current video YouTube link to the clipboard"));
198     copyPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L));
199     copyPageAct->setEnabled(false);
200     actions->insert("pagelink", copyPageAct);
201     connect(copyPageAct, SIGNAL(triggered()), mediaView, SLOT(copyWebPage()));
202
203     copyLinkAct = new QAction(tr("Copy the video stream &URL"), this);
204     copyLinkAct->setStatusTip(tr("Copy the current video stream URL to the clipboard"));
205     copyLinkAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U));
206     copyLinkAct->setEnabled(false);
207     actions->insert("videolink", copyLinkAct);
208     connect(copyLinkAct, SIGNAL(triggered()), mediaView, SLOT(copyVideoLink()));
209
210     findVideoPartsAct = new QAction(tr("Find video &parts"), this);
211     findVideoPartsAct->setStatusTip(tr("Find other video parts hopefully in the right order"));
212     findVideoPartsAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P));
213     findVideoPartsAct->setEnabled(false);
214     connect(findVideoPartsAct, SIGNAL(triggered()), mediaView, SLOT(findVideoParts()));
215     actions->insert("findVideoParts", findVideoPartsAct);
216
217     removeAct = new QAction(tr("&Remove"), this);
218     removeAct->setStatusTip(tr("Remove the selected videos from the playlist"));
219     removeAct->setShortcuts(QList<QKeySequence>() << QKeySequence("Del") << QKeySequence("Backspace"));
220     removeAct->setEnabled(false);
221     actions->insert("remove", removeAct);
222     connect(removeAct, SIGNAL(triggered()), mediaView, SLOT(removeSelected()));
223
224     moveUpAct = new QAction(tr("Move &Up"), this);
225     moveUpAct->setStatusTip(tr("Move up the selected videos in the playlist"));
226     moveUpAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up));
227     moveUpAct->setEnabled(false);
228     actions->insert("moveUp", moveUpAct);
229     connect(moveUpAct, SIGNAL(triggered()), mediaView, SLOT(moveUpSelected()));
230
231     moveDownAct = new QAction(tr("Move &Down"), this);
232     moveDownAct->setStatusTip(tr("Move down the selected videos in the playlist"));
233     moveDownAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down));
234     moveDownAct->setEnabled(false);
235     actions->insert("moveDown", moveDownAct);
236     connect(moveDownAct, SIGNAL(triggered()), mediaView, SLOT(moveDownSelected()));
237
238     clearAct = new QAction(tr("&Clear recent searches"), this);
239     clearAct->setMenuRole(QAction::ApplicationSpecificRole);
240     clearAct->setShortcuts(QList<QKeySequence>()
241                            << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Delete)
242                            << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Backspace));
243     clearAct->setStatusTip(tr("Clear the search history. Cannot be undone."));
244     clearAct->setEnabled(true);
245     actions->insert("clearRecentKeywords", clearAct);
246     connect(clearAct, SIGNAL(triggered()), SLOT(clearRecentKeywords()));
247
248     quitAct = new QAction(tr("&Quit"), this);
249     quitAct->setMenuRole(QAction::QuitRole);
250     quitAct->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+Q")) << QKeySequence(Qt::CTRL + Qt::Key_W));
251     quitAct->setStatusTip(tr("Bye"));
252     actions->insert("quit", quitAct);
253     connect(quitAct, SIGNAL(triggered()), this, SLOT(close()));
254
255     siteAct = new QAction(tr("&Website"), this);
256     siteAct->setShortcut(QKeySequence::HelpContents);
257     siteAct->setStatusTip(tr("%1 on the Web").arg(Constants::NAME));
258     actions->insert("site", siteAct);
259     connect(siteAct, SIGNAL(triggered()), this, SLOT(visitSite()));
260
261 #if !defined(APP_MAC) && !defined(APP_WIN)
262     donateAct = new QAction(tr("Make a &donation"), this);
263     donateAct->setStatusTip(tr("Please support the continued development of %1").arg(Constants::NAME));
264     actions->insert("donate", donateAct);
265     connect(donateAct, SIGNAL(triggered()), this, SLOT(donate()));
266 #endif
267
268     aboutAct = new QAction(tr("&About"), this);
269     aboutAct->setMenuRole(QAction::AboutRole);
270     aboutAct->setStatusTip(tr("Info about %1").arg(Constants::NAME));
271     actions->insert("about", aboutAct);
272     connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
273
274     // Invisible actions
275
276     searchFocusAct = new QAction(this);
277     searchFocusAct->setShortcut(QKeySequence::Find);
278     searchFocusAct->setStatusTip(tr("Search"));
279     actions->insert("search", searchFocusAct);
280     connect(searchFocusAct, SIGNAL(triggered()), this, SLOT(searchFocus()));
281     addAction(searchFocusAct);
282
283     volumeUpAct = new QAction(this);
284     volumeUpAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Plus) << QKeySequence(Qt::Key_VolumeUp));
285     actions->insert("volume-up", volumeUpAct);
286     connect(volumeUpAct, SIGNAL(triggered()), this, SLOT(volumeUp()));
287     addAction(volumeUpAct);
288
289     volumeDownAct = new QAction(this);
290     volumeDownAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Minus) << QKeySequence(Qt::Key_VolumeDown));
291     actions->insert("volume-down", volumeDownAct);
292     connect(volumeDownAct, SIGNAL(triggered()), this, SLOT(volumeDown()));
293     addAction(volumeDownAct);
294
295     volumeMuteAct = new QAction(this);
296     volumeMuteAct->setIcon(QtIconLoader::icon("audio-volume-high"));
297     volumeMuteAct->setStatusTip(tr("Mute volume"));
298     volumeMuteAct->setShortcuts(QList<QKeySequence>()
299                                 << QKeySequence(tr("Ctrl+M"))
300                                 << QKeySequence(Qt::Key_VolumeMute));
301     actions->insert("volume-mute", volumeMuteAct);
302     connect(volumeMuteAct, SIGNAL(triggered()), SLOT(volumeMute()));
303     addAction(volumeMuteAct);
304
305     QAction *definitionAct = new QAction(this);
306     definitionAct->setIcon(QtIconLoader::icon("video-display"));
307     definitionAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_D));
308     /*
309     QMenu *definitionMenu = new QMenu(this);
310     foreach (QString definition, VideoDefinition::getDefinitionNames()) {
311         definitionMenu->addAction(definition);
312     }
313     definitionAct->setMenu(definitionMenu);
314     */
315     actions->insert("definition", definitionAct);
316     connect(definitionAct, SIGNAL(triggered()), SLOT(toggleDefinitionMode()));
317     addAction(definitionAct);
318
319     QAction *action;
320
321     /*
322     action = new QAction(tr("&Autoplay"), this);
323     action->setStatusTip(tr("Automatically start playing videos"));
324     action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P));
325     action->setCheckable(true);
326     connect(action, SIGNAL(toggled(bool)), SLOT(setAutoplay(bool)));
327     actions->insert("autoplay", action);
328     */
329
330     action = new QAction(tr("&Downloads"), this);
331     action->setStatusTip(tr("Show details about video downloads"));
332     action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_J));
333     action->setCheckable(true);
334     action->setIcon(QtIconLoader::icon("go-down"));
335     action->setVisible(false);
336     connect(action, SIGNAL(toggled(bool)), SLOT(toggleDownloads(bool)));
337     actions->insert("downloads", action);
338
339     action = new QAction(tr("&Download"), this);
340     action->setStatusTip(tr("Download the current video"));
341 #ifndef APP_NO_DOWNLOADS
342     action->setShortcut(QKeySequence::Save);
343 #endif
344     action->setIcon(QtIconLoader::icon("go-down"));
345     action->setEnabled(false);
346 #if QT_VERSION >= 0x040600
347     action->setPriority(QAction::LowPriority);
348 #endif
349     connect(action, SIGNAL(triggered()), mediaView, SLOT(downloadVideo()));
350     actions->insert("download", action);
351
352     QString shareTip = tr("Share the current video using %1");
353
354     action = new QAction("&Twitter", this);
355     action->setStatusTip(shareTip.arg("Twitter"));
356     actions->insert("twitter", action);
357     connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaTwitter()));
358
359     action = new QAction("&Facebook", this);
360     action->setStatusTip(shareTip.arg("Facebook"));
361     actions->insert("facebook", action);
362     connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaFacebook()));
363
364     action = new QAction(tr("&Email"), this);
365     action->setStatusTip(shareTip.arg(tr("Email")));
366     actions->insert("email", action);
367     connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaEmail()));
368
369     // common action properties
370     foreach (QAction *action, actions->values()) {
371
372         // add actions to the MainWindow so that they work
373         // when the menu is hidden
374         addAction(action);
375
376         // never autorepeat.
377         // unexperienced users tend to keep keys pressed for a "long" time
378         action->setAutoRepeat(false);
379
380         // set to something more meaningful then the toolbar text
381         // HELP! how to remove tooltips altogether?!
382         if (!action->statusTip().isEmpty())
383             action->setToolTip(action->statusTip());
384
385         // show keyboard shortcuts in the status bar
386         if (!action->shortcut().isEmpty())
387             action->setStatusTip(action->statusTip() + " (" + action->shortcut().toString(QKeySequence::NativeText) + ")");
388
389         // no icons in menus
390         action->setIconVisibleInMenu(false);
391
392     }
393
394 }
395
396 void MainWindow::createMenus() {
397
398     QMap<QString, QMenu*> *menus = The::globalMenus();
399
400     fileMenu = menuBar()->addMenu(tr("&Application"));
401     // menus->insert("file", fileMenu);
402     fileMenu->addAction(clearAct);
403 #ifndef APP_MAC
404     fileMenu->addSeparator();
405 #endif
406     fileMenu->addAction(quitAct);
407
408     QMenu* playbackMenu = menuBar()->addMenu(tr("&Playback"));
409     menus->insert("playback", playbackMenu);
410     playbackMenu->addAction(stopAct);
411     playbackMenu->addAction(pauseAct);
412     playbackMenu->addAction(skipAct);
413 #ifdef APP_MAC
414     MacSupport::dockMenu(playbackMenu);
415 #endif
416
417     playlistMenu = menuBar()->addMenu(tr("&Playlist"));
418     menus->insert("playlist", playlistMenu);
419     playlistMenu->addAction(removeAct);
420     playlistMenu->addSeparator();
421     playlistMenu->addAction(moveUpAct);
422     playlistMenu->addAction(moveDownAct);
423
424     QMenu* videoMenu = menuBar()->addMenu(tr("&Video"));
425     menus->insert("video", videoMenu);
426     videoMenu->addAction(findVideoPartsAct);
427     videoMenu->addSeparator();
428     videoMenu->addAction(webPageAct);
429 #ifndef APP_NO_DOWNLOADS
430     videoMenu->addSeparator();
431     videoMenu->addAction(The::globalActions()->value("download"));
432     videoMenu->addAction(copyLinkAct);
433 #endif
434
435     QMenu* viewMenu = menuBar()->addMenu(tr("&View"));
436     menus->insert("view", viewMenu);
437     viewMenu->addAction(fullscreenAct);
438     viewMenu->addAction(compactViewAct);
439
440     QMenu* shareMenu = menuBar()->addMenu(tr("&Share"));
441     menus->insert("share", shareMenu);
442     shareMenu->addAction(copyPageAct);
443     shareMenu->addSeparator();
444     shareMenu->addAction(The::globalActions()->value("twitter"));
445     shareMenu->addAction(The::globalActions()->value("facebook"));
446     shareMenu->addAction(The::globalActions()->value("email"));
447
448     helpMenu = menuBar()->addMenu(tr("&Help"));
449     helpMenu->addAction(siteAct);
450 #if !defined(APP_MAC) && !defined(APP_WIN)
451     helpMenu->addAction(donateAct);
452 #endif
453     helpMenu->addAction(aboutAct);
454 }
455
456 void MainWindow::createToolBars() {
457
458     setUnifiedTitleAndToolBarOnMac(true);
459
460     mainToolBar = new QToolBar(this);
461 #if QT_VERSION < 0x040600 | defined(APP_MAC)
462     mainToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
463 #else
464     mainToolBar->setToolButtonStyle(Qt::ToolButtonFollowStyle);
465 #endif
466     mainToolBar->setFloatable(false);
467     mainToolBar->setMovable(false);
468
469 #if defined(APP_MAC) | defined(APP_WIN)
470     mainToolBar->setIconSize(QSize(32, 32));
471 #endif
472
473     mainToolBar->addAction(stopAct);
474     mainToolBar->addAction(pauseAct);
475     mainToolBar->addAction(skipAct);
476
477     bool addFullScreenAct = true;
478 #ifdef Q_WS_MAC
479     addFullScreenAct = !mac::CanGoFullScreen(winId());
480 #endif
481     if (addFullScreenAct) mainToolBar->addAction(fullscreenAct);
482
483 #ifndef APP_NO_DOWNLOADS
484     mainToolBar->addAction(The::globalActions()->value("download"));
485 #endif
486
487     mainToolBar->addWidget(new Spacer());
488
489     QFont smallerFont = FontUtils::small();
490     currentTime = new QLabel(mainToolBar);
491     currentTime->setFont(smallerFont);
492     mainToolBar->addWidget(currentTime);
493
494     mainToolBar->addWidget(new Spacer());
495
496     seekSlider = new Phonon::SeekSlider(this);
497     seekSlider->setIconVisible(false);
498     seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
499     mainToolBar->addWidget(seekSlider);
500
501 /*
502     mainToolBar->addWidget(new Spacer());
503     slider = new QSlider(this);
504     slider->setOrientation(Qt::Horizontal);
505     slider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
506     mainToolBar->addWidget(slider);
507 */
508
509     mainToolBar->addWidget(new Spacer());
510
511     totalTime = new QLabel(mainToolBar);
512     totalTime->setFont(smallerFont);
513     mainToolBar->addWidget(totalTime);
514
515     mainToolBar->addWidget(new Spacer());
516
517     mainToolBar->addAction(volumeMuteAct);
518
519     volumeSlider = new Phonon::VolumeSlider(this);
520     volumeSlider->setMuteVisible(false);
521     // qDebug() << volumeSlider->children();
522     // status tip for the volume slider
523     QSlider* volumeQSlider = volumeSlider->findChild<QSlider*>();
524     if (volumeQSlider)
525         volumeQSlider->setStatusTip(tr("Press %1 to raise the volume, %2 to lower it").arg(
526                 volumeUpAct->shortcut().toString(QKeySequence::NativeText), volumeDownAct->shortcut().toString(QKeySequence::NativeText)));
527     // this makes the volume slider smaller
528     volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
529     mainToolBar->addWidget(volumeSlider);
530
531     mainToolBar->addWidget(new Spacer());
532
533     toolbarSearch->setStatusTip(searchFocusAct->statusTip());
534     mainToolBar->addWidget(toolbarSearch);
535
536     mainToolBar->addWidget(new Spacer());
537
538     addToolBar(mainToolBar);
539 }
540
541 void MainWindow::createStatusBar() {
542     QToolBar *toolBar = new QToolBar(this);
543     toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
544     toolBar->setIconSize(QSize(16, 16));
545     toolBar->addAction(The::globalActions()->value("downloads"));
546     // toolBar->addAction(The::globalActions()->value("autoplay"));
547     toolBar->addAction(The::globalActions()->value("definition"));
548     statusBar()->addPermanentWidget(toolBar);
549     statusBar()->show();
550 }
551
552 void MainWindow::readSettings() {
553     QSettings settings;
554     restoreGeometry(settings.value("geometry").toByteArray());
555 #ifdef APP_MAC
556     MacSupport::fixGeometry(this);
557 #endif
558     setDefinitionMode(settings.value("definition", VideoDefinition::getDefinitionNames().first()).toString());
559     audioOutput->setVolume(settings.value("volume", 1).toDouble());
560     audioOutput->setMuted(settings.value("volumeMute").toBool());
561 }
562
563 void MainWindow::writeSettings() {
564
565     QSettings settings;
566
567     // do not save geometry when in full screen
568     if (!m_fullscreen) {
569         settings.setValue("geometry", saveGeometry());
570     }
571
572     settings.setValue("volume", audioOutput->volume());
573     settings.setValue("volumeMute", audioOutput->isMuted());
574     mediaView->saveSplitterState();
575 }
576
577 void MainWindow::goBack() {
578     if ( history->size() > 1 ) {
579         history->pop();
580         QWidget *widget = history->pop();
581         showWidget(widget);
582     }
583 }
584
585 void MainWindow::showWidget ( QWidget* widget ) {
586
587     setUpdatesEnabled(false);
588
589     // call hide method on the current view
590     View* oldView = dynamic_cast<View *> (views->currentWidget());
591     if (oldView) {
592         oldView->disappear();
593     }
594
595     // call show method on the new view
596     View* newView = dynamic_cast<View *> (widget);
597     if (newView) {
598         newView->appear();
599         QMap<QString,QVariant> metadata = newView->metadata();
600         QString windowTitle = metadata.value("title").toString();
601         if (windowTitle.length())
602             windowTitle += " - ";
603         setWindowTitle(windowTitle + Constants::NAME);
604         statusBar()->showMessage((metadata.value("description").toString()));
605     }
606
607     stopAct->setEnabled(widget == mediaView);
608     compactViewAct->setEnabled(widget == mediaView);
609     webPageAct->setEnabled(widget == mediaView);
610     copyPageAct->setEnabled(widget == mediaView);
611     copyLinkAct->setEnabled(widget == mediaView);
612     findVideoPartsAct->setEnabled(widget == mediaView);
613     toolbarSearch->setEnabled(widget == searchView || widget == mediaView || widget == downloadView);
614
615     The::globalActions()->value("twitter")->setEnabled(widget == mediaView);
616     The::globalActions()->value("facebook")->setEnabled(widget == mediaView);
617     The::globalActions()->value("email")->setEnabled(widget == mediaView);
618
619     aboutAct->setEnabled(widget != aboutView);
620     The::globalActions()->value("download")->setEnabled(widget == mediaView);
621     The::globalActions()->value("downloads")->setChecked(widget == downloadView);
622
623     // toolbar only for the mediaView
624     /* mainToolBar->setVisible(
625             (widget == mediaView && !compactViewAct->isChecked())
626             || widget == downloadView
627             ); */
628
629     setUpdatesEnabled(true);
630
631     QWidget *oldWidget = views->currentWidget();
632     views->setCurrentWidget(widget);
633
634 #ifndef Q_WS_X11
635     Extra::fadeInWidget(oldWidget, widget);
636 #endif
637
638     history->push(widget);
639 }
640
641 void MainWindow::about() {
642     if (!aboutView) {
643         aboutView = new AboutView(this);
644         views->addWidget(aboutView);
645     }
646     showWidget(aboutView);
647 }
648
649 void MainWindow::visitSite() {
650     QUrl url(Constants::WEBSITE);
651     statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
652     QDesktopServices::openUrl(url);
653 }
654
655 void MainWindow::donate() {
656     QUrl url(QString(Constants::WEBSITE) + "#donate");
657     statusBar()->showMessage(QString(tr("Opening %1").arg(url.toString())));
658     QDesktopServices::openUrl(url);
659 }
660
661 void MainWindow::quit() {
662     writeSettings();
663     Temporary::deleteAll();
664     qApp->quit();
665 }
666
667 void MainWindow::closeEvent(QCloseEvent *event) {
668     if (DownloadManager::instance()->activeItems() > 0) {
669         QMessageBox msgBox;
670         msgBox.setIconPixmap(QPixmap(":/images/app.png").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
671         msgBox.setText(tr("Do you want to exit %1 with a download in progress?").arg(Constants::NAME));
672         msgBox.setInformativeText(tr("If you close %1 now, this download will be cancelled.").arg(Constants::NAME));
673         msgBox.setModal(true);
674
675         msgBox.addButton(tr("Close and cancel download"), QMessageBox::RejectRole);
676         QPushButton *waitButton = msgBox.addButton(tr("Wait for download to finish"), QMessageBox::ActionRole);
677
678         msgBox.exec();
679
680         if (msgBox.clickedButton() == waitButton) {
681             event->ignore();
682             return;
683         }
684
685     }
686     quit();
687     QWidget::closeEvent(event);
688 }
689
690 void MainWindow::showSearch() {
691     showWidget(searchView);
692     currentTime->clear();
693     totalTime->clear();
694 }
695
696 void MainWindow::showMedia(SearchParams *searchParams) {
697     if (toolbarSearch->text().isEmpty() && !searchParams->keywords().isEmpty()) {
698         toolbarSearch->lineEdit()->setText(searchParams->keywords());
699     }
700     mediaView->search(searchParams);
701     showWidget(mediaView);
702 }
703
704 void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */) {
705
706     // qDebug() << "Phonon state: " << newState;
707
708     switch (newState) {
709
710     case Phonon::ErrorState:
711         if (mediaObject->errorType() == Phonon::FatalError) {
712             // Do not display because we try to play incomplete video files and sometimes trigger this
713             // We retry automatically (in MediaView) so no need to show it
714             // statusBar()->showMessage(tr("Fatal error: %1").arg(mediaObject->errorString()));
715         } else {
716             statusBar()->showMessage(tr("Error: %1").arg(mediaObject->errorString()));
717         }
718         break;
719
720          case Phonon::PlayingState:
721         pauseAct->setEnabled(true);
722         pauseAct->setIcon(QtIconLoader::icon("media-playback-pause"));
723         pauseAct->setText(tr("&Pause"));
724         pauseAct->setStatusTip(tr("Pause playback") + " (" +  pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
725         skipAct->setEnabled(true);
726         // stopAct->setEnabled(true);
727         break;
728
729          case Phonon::StoppedState:
730         pauseAct->setEnabled(false);
731         skipAct->setEnabled(false);
732         // stopAct->setEnabled(false);
733         break;
734
735          case Phonon::PausedState:
736         skipAct->setEnabled(true);
737         pauseAct->setEnabled(true);
738         pauseAct->setIcon(QtIconLoader::icon("media-playback-start"));
739         pauseAct->setText(tr("&Play"));
740         pauseAct->setStatusTip(tr("Resume playback") + " (" +  pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
741         // stopAct->setEnabled(true);
742         break;
743
744          case Phonon::BufferingState:
745          case Phonon::LoadingState:
746         skipAct->setEnabled(true);
747         pauseAct->setEnabled(false);
748         currentTime->clear();
749         totalTime->clear();
750         // stopAct->setEnabled(true);
751         break;
752
753          default:
754         ;
755     }
756 }
757
758 void MainWindow::stop() {
759     mediaView->stop();
760     showSearch();
761 }
762
763 void MainWindow::resizeEvent(QResizeEvent*) {
764 #ifdef Q_WS_MAC
765     if (mac::CanGoFullScreen(winId())) {
766         bool isFullscreen = mac::IsFullScreen(winId());
767         if (isFullscreen != m_fullscreen) {
768             m_fullscreen = isFullscreen;
769             updateUIForFullscreen();
770         }
771     }
772 #endif
773 }
774
775 void MainWindow::fullscreen() {
776
777 #ifdef Q_WS_MAC
778     WId handle = winId();
779     if (mac::CanGoFullScreen(handle)) {
780         mac::ToggleFullScreen(handle);
781         return;
782     }
783 #endif
784
785     m_fullscreen = !m_fullscreen;
786
787     if (m_fullscreen) {
788         // Enter full screen
789
790         m_maximized = isMaximized();
791
792         // save geometry now, if the user quits when in full screen
793         // geometry won't be saved
794         writeSettings();
795
796 #ifdef Q_WS_MAC
797         MacSupport::enterFullScreen(this, views);
798 #else
799         mainToolBar->hide();
800         showFullScreen();
801 #endif
802
803     } else {
804         // Exit full screen
805
806 #ifdef Q_WS_MAC
807         MacSupport::exitFullScreen(this, views);
808 #else
809         mainToolBar->show();
810         if (m_maximized) showMaximized();
811         else showNormal();
812 #endif
813
814         // Make sure the window has focus
815         activateWindow();
816
817     }
818
819     updateUIForFullscreen();
820
821 }
822
823 void MainWindow::updateUIForFullscreen() {
824     static QList<QKeySequence> fsShortcuts;
825     static QString fsText;
826
827     if (m_fullscreen) {
828         fsShortcuts = fullscreenAct->shortcuts();
829         fsText = fullscreenAct->text();
830         fullscreenAct->setShortcuts(QList<QKeySequence>(fsShortcuts)
831                                     << QKeySequence(Qt::Key_Escape));
832         fullscreenAct->setText(tr("Leave &Full Screen"));
833     } else {
834         fullscreenAct->setShortcuts(fsShortcuts);
835         fullscreenAct->setText(fsText);
836     }
837
838     // No compact view action when in full screen
839     compactViewAct->setVisible(!m_fullscreen);
840     compactViewAct->setChecked(false);
841
842     // Hide anything but the video
843     mediaView->setPlaylistVisible(!m_fullscreen);
844     statusBar()->setVisible(!m_fullscreen);
845
846 #ifndef APP_MAC
847     menuBar()->setVisible(!m_fullscreen);
848 #endif
849
850     if (m_fullscreen) {
851         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
852     } else {
853         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
854     }
855
856 #ifdef Q_WS_MAC
857     MacSupport::fullScreenActions(The::globalActions()->values(), m_fullscreen);
858 #endif
859
860     if (views->currentWidget() == mediaView)
861         mediaView->setFocus();
862 }
863
864 void MainWindow::compactView(bool enable) {
865
866     static QList<QKeySequence> compactShortcuts;
867     static QList<QKeySequence> stopShortcuts;
868
869     /*
870     const static QString key = "compactGeometry";
871     QSettings settings;
872     */
873
874 #ifndef APP_MAC
875     menuBar()->setVisible(!enable);
876 #endif
877
878     if (enable) {
879         /*
880         writeSettings();
881         restoreGeometry(settings.value(key).toByteArray());
882         */
883
884         compactShortcuts = compactViewAct->shortcuts();
885         stopShortcuts = stopAct->shortcuts();
886
887         QList<QKeySequence> newStopShortcuts(stopShortcuts);
888         newStopShortcuts.removeAll(QKeySequence(Qt::Key_Escape));
889         stopAct->setShortcuts(newStopShortcuts);
890         compactViewAct->setShortcuts(QList<QKeySequence>(compactShortcuts) << QKeySequence(Qt::Key_Escape));
891
892         // ensure focus does not end up to the search box
893         // as it would steal the Space shortcut
894         mediaView->setFocus();
895     } else {
896         /*
897         settings.setValue(key, saveGeometry());
898         readSettings();
899         */
900
901         compactViewAct->setShortcuts(compactShortcuts);
902         stopAct->setShortcuts(stopShortcuts);
903     }
904
905     mainToolBar->setVisible(!enable);
906     mediaView->setPlaylistVisible(!enable);
907     statusBar()->setVisible(!enable);
908
909 }
910
911 void MainWindow::searchFocus() {
912     QWidget *view = views->currentWidget();
913     toolbarSearch->selectAll();
914     toolbarSearch->setFocus();
915 }
916
917 void MainWindow::initPhonon() {
918     // Phonon initialization
919     if (mediaObject) delete mediaObject;
920     if (audioOutput) delete audioOutput;
921     mediaObject = new Phonon::MediaObject(this);
922     mediaObject->setTickInterval(100);
923     connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
924             this, SLOT(stateChanged(Phonon::State, Phonon::State)));
925     connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64)));
926     connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64)));
927     seekSlider->setMediaObject(mediaObject);
928     audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this);
929     connect(audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(volumeChanged(qreal)));
930     connect(audioOutput, SIGNAL(mutedChanged(bool)), this, SLOT(volumeMutedChanged(bool)));
931     volumeSlider->setAudioOutput(audioOutput);
932     Phonon::createPath(mediaObject, audioOutput);
933 }
934
935 void MainWindow::tick(qint64 time) {
936     if (time <= 0) {
937         // the "if" is important because tick is continually called
938         // and we don't want to paint the toolbar every 100ms
939         if (!currentTime->text().isEmpty()) currentTime->clear();
940         return;
941     }
942
943     currentTime->setText(formatTime(time));
944
945     // remaining time
946     const qint64 remainingTime = mediaObject->remainingTime();
947     currentTime->setStatusTip(tr("Remaining time: %1").arg(formatTime(remainingTime)));
948
949     /*
950     slider->blockSignals(true);
951     slider->setValue(time/1000);
952     slider->blockSignals(false);
953     */
954 }
955
956 void MainWindow::totalTimeChanged(qint64 time) {
957     if (time <= 0) {
958         totalTime->clear();
959         return;
960     }
961     totalTime->setText(formatTime(time));
962
963     /*
964     slider->blockSignals(true);
965     slider->setMaximum(time/1000);
966     slider->blockSignals(false);
967     */
968
969 }
970
971 QString MainWindow::formatTime(qint64 time) {
972     QTime displayTime;
973     displayTime = displayTime.addMSecs(time);
974     QString timeString;
975     // 60 * 60 * 1000 = 3600000
976     if (time > 3600000)
977         timeString = displayTime.toString("h:mm:ss");
978     else
979         timeString = displayTime.toString("m:ss");
980     return timeString;
981 }
982
983 void MainWindow::volumeUp() {
984     qreal newVolume = volumeSlider->audioOutput()->volume() + .1;
985     if (newVolume > volumeSlider->maximumVolume())
986         newVolume = volumeSlider->maximumVolume();
987     volumeSlider->audioOutput()->setVolume(newVolume);
988 }
989
990 void MainWindow::volumeDown() {
991     qreal newVolume = volumeSlider->audioOutput()->volume() - .1;
992     if (newVolume < 0)
993         newVolume = 0;
994     volumeSlider->audioOutput()->setVolume(newVolume);
995 }
996
997 void MainWindow::volumeMute() {
998     volumeSlider->audioOutput()->setMuted(!volumeSlider->audioOutput()->isMuted());
999 }
1000
1001 void MainWindow::volumeChanged(qreal newVolume) {
1002     // automatically unmute when volume changes
1003     if (volumeSlider->audioOutput()->isMuted())
1004         volumeSlider->audioOutput()->setMuted(false);
1005     statusBar()->showMessage(tr("Volume at %1%").arg((int)(newVolume*100)));
1006 }
1007
1008 void MainWindow::volumeMutedChanged(bool muted) {
1009     if (muted) {
1010         volumeMuteAct->setIcon(QtIconLoader::icon("audio-volume-muted"));
1011         statusBar()->showMessage(tr("Volume is muted"));
1012     } else {
1013         volumeMuteAct->setIcon(QtIconLoader::icon("audio-volume-high"));
1014         statusBar()->showMessage(tr("Volume is unmuted"));
1015     }
1016 }
1017
1018 void MainWindow::setDefinitionMode(QString definitionName) {
1019     QAction *definitionAct = The::globalActions()->value("definition");
1020     definitionAct->setText(definitionName);
1021     definitionAct->setStatusTip(tr("Maximum video definition set to %1").arg(definitionAct->text())
1022                                 + " (" +  definitionAct->shortcut().toString(QKeySequence::NativeText) + ")");
1023     statusBar()->showMessage(definitionAct->statusTip());
1024     QSettings settings;
1025     settings.setValue("definition", definitionName);
1026 }
1027
1028 void MainWindow::toggleDefinitionMode() {
1029     QSettings settings;
1030     QString currentDefinition = settings.value("definition").toString();
1031     QStringList definitionNames = VideoDefinition::getDefinitionNames();
1032     int currentIndex = definitionNames.indexOf(currentDefinition);
1033     int nextIndex = 0;
1034     if (currentIndex != definitionNames.size() - 1) {
1035         nextIndex = currentIndex + 1;
1036     }
1037     QString nextDefinition = definitionNames.at(nextIndex);
1038     setDefinitionMode(nextDefinition);
1039 }
1040
1041 void MainWindow::showFullscreenToolbar(bool show) {
1042     if (!m_fullscreen) return;
1043     mainToolBar->setVisible(show);
1044 }
1045
1046 void MainWindow::showFullscreenPlaylist(bool show) {
1047     if (!m_fullscreen) return;
1048     mediaView->setPlaylistVisible(show);
1049 }
1050
1051 void MainWindow::clearRecentKeywords() {
1052     QSettings settings;
1053     settings.remove("recentKeywords");
1054     settings.remove("recentChannels");
1055     searchView->updateRecentKeywords();
1056     searchView->updateRecentChannels();
1057     statusBar()->showMessage(tr("Your privacy is now safe"));
1058 }
1059
1060 /*
1061  void MainWindow::setAutoplay(bool enabled) {
1062      QSettings settings;
1063      settings.setValue("autoplay", QVariant::fromValue(enabled));
1064  }
1065  */
1066
1067 void MainWindow::updateDownloadMessage(QString message) {
1068     The::globalActions()->value("downloads")->setText(message);
1069 }
1070
1071 void MainWindow::downloadsFinished() {
1072     The::globalActions()->value("downloads")->setText(tr("&Downloads"));
1073     statusBar()->showMessage(tr("Downloads complete"));
1074 }
1075
1076 void MainWindow::toggleDownloads(bool show) {
1077
1078     if (show) {
1079         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
1080         The::globalActions()->value("downloads")->setShortcuts(
1081                 QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J)
1082                 << QKeySequence(Qt::Key_Escape));
1083     } else {
1084         The::globalActions()->value("downloads")->setShortcuts(
1085                 QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J));
1086         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
1087     }
1088
1089     if (!downloadView) {
1090         downloadView = new DownloadView(this);
1091         views->addWidget(downloadView);
1092     }
1093     if (show) showWidget(downloadView);
1094     else goBack();
1095 }
1096
1097 void MainWindow::startToolbarSearch(QString query) {
1098
1099     query = query.trimmed();
1100
1101     // check for empty query
1102     if (query.length() == 0) {
1103         return;
1104     }
1105
1106     SearchParams *searchParams = new SearchParams();
1107     searchParams->setKeywords(query);
1108
1109     // go!
1110     showMedia(searchParams);
1111 }
1112
1113 void MainWindow::dragEnterEvent(QDragEnterEvent *event) {
1114     if (event->mimeData()->hasFormat("text/uri-list")) {
1115         QList<QUrl> urls = event->mimeData()->urls();
1116         if (urls.isEmpty())
1117             return;
1118         QUrl url = urls.first();
1119         QString videoId = YouTubeSearch::videoIdFromUrl(url.toString());
1120         if (!videoId.isNull())
1121             event->acceptProposedAction();
1122     }
1123 }
1124
1125 void MainWindow::dropEvent(QDropEvent *event) {
1126     QList<QUrl> urls = event->mimeData()->urls();
1127     if (urls.isEmpty())
1128         return;
1129     QUrl url = urls.first();
1130     QString videoId = YouTubeSearch::videoIdFromUrl(url.toString());
1131     if (!videoId.isNull()) {
1132         setWindowTitle(url.toString());
1133         SearchParams *searchParams = new SearchParams();
1134         searchParams->setKeywords(videoId);
1135         showMedia(searchParams);
1136     }
1137 }
1138
1139 void MainWindow::checkForUpdate() {
1140     static const QString updateCheckKey = "updateCheck";
1141
1142     // check every 24h
1143     QSettings settings;
1144     uint unixTime = QDateTime::currentDateTime().toTime_t();
1145     int lastCheck = settings.value(updateCheckKey).toInt();
1146     int secondsSinceLastCheck = unixTime - lastCheck;
1147     // qDebug() << "secondsSinceLastCheck" << unixTime << lastCheck << secondsSinceLastCheck;
1148     if (secondsSinceLastCheck < 86400) return;
1149
1150     // check it out
1151     if (updateChecker) delete updateChecker;
1152     updateChecker = new UpdateChecker();
1153     connect(updateChecker, SIGNAL(newVersion(QString)),
1154             this, SLOT(gotNewVersion(QString)));
1155     updateChecker->checkForUpdate();
1156     settings.setValue(updateCheckKey, unixTime);
1157
1158 }
1159
1160 void MainWindow::gotNewVersion(QString version) {
1161     if (updateChecker) {
1162         delete updateChecker;
1163         updateChecker = 0;
1164     }
1165
1166 #if defined(APP_DEMO) || defined(APP_MAC_STORE)
1167     return;
1168 #endif
1169
1170     QSettings settings;
1171     QString checkedVersion = settings.value("checkedVersion").toString();
1172     if (checkedVersion == version) return;
1173
1174     QMessageBox msgBox(this);
1175     msgBox.setIconPixmap(QPixmap(":/images/app.png").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
1176     msgBox.setText(tr("%1 version %2 is now available.").arg(Constants::NAME, version));
1177
1178     msgBox.setModal(true);
1179     // make it a "sheet" on the Mac
1180     msgBox.setWindowModality(Qt::WindowModal);
1181
1182     QPushButton* laterButton = 0;
1183     QPushButton* updateButton = 0;
1184
1185 #if defined(APP_MAC) || defined(APP_WIN)
1186     msgBox.setInformativeText(
1187                 tr("To get the updated version, download %1 again from the link you received via email and reinstall.")
1188                 .arg(Constants::NAME)
1189                 );
1190     laterButton = msgBox.addButton(tr("Remind me later"), QMessageBox::RejectRole);
1191     msgBox.addButton(QMessageBox::Ok);
1192 #else
1193     msgBox.addButton(QMessageBox::Close);
1194     updateButton = msgBox.addButton(tr("Update"), QMessageBox::AcceptRole);
1195 #endif
1196
1197     msgBox.exec();
1198
1199     if (msgBox.clickedButton() != laterButton) {
1200         settings.setValue("checkedVersion", version);
1201     }
1202
1203     if (updateButton && msgBox.clickedButton() == updateButton) {
1204         QDesktopServices::openUrl(QUrl(QLatin1String(Constants::WEBSITE) + "#download"));
1205     }
1206
1207 }