2 * Copyright (C) 2010 Toni Gundogdu.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include <QMainWindow>
19 #include <QCloseEvent>
20 #include <QFileDialog>
29 #include "Preferences.h"
32 #include "YoutubeFeed.h"
33 #include "MainWindow.h"
35 #define QSETTINGS_GROUP "MainWindow"
36 #define QSETTINGS_REMINDER_SHOWTIPS "showTips"
40 extern QMap
<QString
,QStringList
> hosts
;
41 extern SharedPreferences shPrefs
;
47 enum { StreamVideo
=0, DownloadVideo
};
51 MainWindow::MainWindow () {
59 if (shPrefs
.get (SharedPreferences::StayOnTop
).toBool ())
60 setWindowFlags (windowFlags () | Qt::WindowStaysOnTopHint
);
62 // Create Video instance.
66 // Create context menu.
70 // Create system tray icon.
74 // Create Download dialog.
76 download
= new DownloadDialog (this);
78 // Create Process Progress dialog for quvi.
80 proc
= new ProcessProgressDialog (this);
82 connect (proc
, SIGNAL (finished (QString
)),
83 this, SLOT (onProcFinished (QString
)));
85 QHash
<QString
,QRegExp
> h
;
88 do { h[s] = QRegExp(r); } while (0)
90 _wrap(tr("Checking..."), "^:: Check");
91 _wrap(tr("Fetching..."), "^:: Fetch");
92 _wrap(tr("Verifying..."), "^:: Verify");
96 proc
->setLabelRegExp (h
);
98 log
<< tr ("Program started.") + "\n";
100 // Custom program icon.
102 if (shPrefs
.get (SharedPreferences::CustomProgramIcon
).toBool ())
103 changeProgramIcon ();
108 MainWindow::createContextMenu () {
110 #define creat_a(t,f,c) \
112 QAction *a = new QAction (t, textBrowser); \
114 a->setCheckable (c); \
115 /*connect (a, SIGNAL(toggled (bool)), SLOT(f (bool)));*/ \
118 connect (a, SIGNAL(triggered ()), SLOT(f ())); \
119 textBrowser->addAction (a); \
125 QAction *a = new QAction (textBrowser); \
126 a->setSeparator (true); \
127 textBrowser->addAction (a); \
130 creat_a (tr("Address..."), onAddress
, false);
131 creat_a (tr("Feed..."), onFeed
, false);
132 creat_a (tr("Recent..."), onRecent
, false);
134 creat_a (tr("Overwrite"), _
, true);
136 creat_a (tr("Log..."), onLog
, false);
137 creat_a (tr("Preferences..."), onPreferences
, false);
139 creat_a (tr("About..."), onAbout
, false);
140 creat_a (tr("Quit"), close
, false);
145 // Add key shortcuts.
148 do { actions[s]->setShortcut (QKeySequence(k)); } while (0)
150 _wrap (tr("Address..."), "Ctrl+A");
151 _wrap (tr("Feed..."), "Ctrl+F");
152 _wrap (tr("Recent..."), "Ctrl+R");
154 _wrap (tr("Overwrite"), "Ctrl+W");
156 _wrap (tr("Log..."), "Ctrl+L");
157 _wrap (tr("Preferences..."),"Ctrl+E");
159 _wrap (tr("Quit"), "Ctrl+Q");
163 // Add the context menu.
165 textBrowser
->setContextMenuPolicy (Qt::ActionsContextMenu
);
171 MainWindow::createTray () {
173 trayMenu
= new QMenu(this);
175 #define creat_a(t,f) \
177 QAction *a = new QAction(t, this); \
178 connect(a, SIGNAL(triggered()), this, SLOT(f)); \
180 trayMenu->addAction(a); \
183 creat_a ( tr("Open"), showNormal() );
184 trayMenu
->addSeparator();
185 creat_a ( tr("Quit"), close() );
189 trayIcon
= new QSystemTrayIcon (this);
191 trayIcon
->setContextMenu (trayMenu
);
192 trayIcon
->setIcon (QIcon (":img/nomnom.png"));
194 connect(trayIcon
, SIGNAL( activated(QSystemTrayIcon::ActivationReason
) ),
195 this, SLOT( onTrayActivated(QSystemTrayIcon::ActivationReason
) ) );
201 MainWindow::showEvent (QShowEvent
*e
) {
209 MainWindow::hideEvent (QHideEvent
*e
) {
211 if (QSystemTrayIcon::isSystemTrayAvailable()
212 && shPrefs
.get(SharedPreferences::MinToTray
).toBool())
224 MainWindow::closeEvent (QCloseEvent
*e
) {
227 NomNom::save_size (s
, this, QSETTINGS_GROUP
);
228 NomNom::save_pos (s
, this, QSETTINGS_GROUP
);
230 s
.beginGroup (QSETTINGS_GROUP
);
231 s
.setValue ("modeCBox", modeCBox
->currentIndex ());
242 MainWindow::readSettings () {
246 NomNom::restore_size(s
, this, QSETTINGS_GROUP
, QSize(130,150));
247 NomNom::restore_pos(s
, this, QSETTINGS_GROUP
);
249 s
.beginGroup(QSETTINGS_GROUP
);
250 modeCBox
->setCurrentIndex(s
.value("modeCBox").toInt());
253 Reminder (this, "SharedPreferences").conditionalExec ();
255 // Re-read (now updated) "showReminder" value. This was previously done
256 // in main.cpp, but has to be done here if we expect the Preferences
257 // "show tips" box value to match the one set in the Reminder dialog.
262 // Handle (dropped) URL.
265 MainWindow::handleURL (const QString
& url
) {
269 const QString quviPath
=
270 shPrefs
.get (SharedPreferences::QuviPath
).toString ().simplified ();
272 if (quviPath
.isEmpty ()) {
273 NomNom::crit (this, tr ("You must specify path to the quvi command."));
278 if (hosts
.isEmpty ()) {
280 if (!NomNom::parse_quvi_support (quviPath
, errmsg
)) {
281 NomNom::crit (this, errmsg
);
286 const QString playerPath
=
287 shPrefs
.get (SharedPreferences::PlayerPath
).toString ().simplified ();
289 if (playerPath
.isEmpty ()) {
292 tr ("You must specify path to a stream-capable media "
307 quviPath
.split (" ").replaceInStrings ("%u", url
);
313 foreach (QString key
, hosts
.keys ()) {
315 if (re
.indexIn (url
) != -1) {
316 formats
= hosts
.value (key
);
321 if (formats
.size () > 1) { // Assume "default" is always present.
323 QString s
= QInputDialog::getItem (
325 tr ("Choose format"),
338 log
<< args
.join (" ") + "\n";
342 proc
->setLabelText (tr ("Checking ..."));
343 proc
->setMaximum (0);
347 if (modeCBox
->currentIndex() == StreamVideo
)
354 // View video (stream).
357 MainWindow::streamVideo () {
360 shPrefs
.get (SharedPreferences::PlayerPath
)
361 .toString ().simplified ().split (" ");
363 args
= args
.replaceInStrings ("%u", video
->get (Video::Link
).toString ());
365 log
<< args
.join(" ") + "\n";
367 const bool ok
= QProcess::startDetached (args
.takeFirst (), args
);
371 tr ("Unable to start player command, check the Preferences."));
375 // Download video (to a file).
378 MainWindow::downloadVideo () {
381 shPrefs
.get (SharedPreferences::FilenameFormat
).toString ();
383 const QString suffix
=
384 video
->get (Video::Suffix
).toString ().simplified ();
386 bool ok
= NomNom::format_filename (
388 shPrefs
.get (SharedPreferences::Regexp
).toString (),
389 video
->get (Video::Title
).toString ().simplified (),
391 video
->get (Video::Host
).toString ().simplified (),
392 video
->get (Video::ID
).toString ().simplified (),
400 shPrefs
.get (SharedPreferences::SaveDir
).toString ();
402 if (fpath
.isEmpty ())
403 fpath
= QDir::currentPath ();
405 fpath
= QDir::toNativeSeparators (fpath
+"/"+ fname
);
407 const bool dont_prompt
=
408 shPrefs
.get (SharedPreferences::DontPromptFilename
).toBool ();
412 const QFileDialog::Options opts
=
413 QFileDialog::DontConfirmOverwrite
;
415 fpath
= QFileDialog::getSaveFileName (
417 tr ("Save video as"),
420 0, // Selected filter.
424 if (fpath
.isEmpty ())
428 if (actions
[tr("Overwrite")]->isChecked ())
429 QDir ().remove (fpath
);
431 const qint64 expected_bytes
=
432 video
->get (Video::Length
).toLongLong ();
434 if (QFileInfo (fpath
).size () < expected_bytes
) {
437 shPrefs
.get (SharedPreferences::CurlPath
).toString ().simplified ();
439 download
->start (cmd
, fpath
, video
);
442 const bool completeFile
=
443 QFileInfo (fpath
).size () >= expected_bytes
;
445 const bool playWhenDone
=
446 shPrefs
.get (SharedPreferences::PlayWhenDone
).toBool ();
450 << QFileInfo (fpath
).size ()
456 if (completeFile
&& playWhenDone
) {
459 shPrefs
.get (SharedPreferences::PlayerPath
)
460 .toString ().simplified ().split (" ");
462 args
= args
.replaceInStrings ("%u", fpath
);
464 log
<< args
.join(" ") + "\n";
466 const bool ok
= QProcess::startDetached (args
.takeFirst (), args
);
470 tr ("Unable to start player command, check the Preferences."));
477 // Change program icon.
480 MainWindow::changeProgramIcon () {
482 const bool customProgramIcon
=
483 shPrefs
.get (SharedPreferences::CustomProgramIcon
).toBool ();
485 QString html
= textBrowser
->toHtml ();
487 const QString iconPath
=
489 ? shPrefs
.get (SharedPreferences::ProgramIconPath
).toString ()
492 html
.replace (QRegExp ("img src=\".*\""), "img src=\"" +iconPath
+ "\"");
494 textBrowser
->setHtml (html
);
496 QIcon icon
= QIcon (iconPath
);
498 setWindowIcon (icon
);
499 trayIcon
->setIcon (icon
);
502 // Parse JSON data returned by quvi.
505 MainWindow::parseOK () {
507 const int n
= json
.indexOf ("{");
514 if (!video
->fromJSON (json
.mid (n
), error
)) {
515 NomNom::crit (this, error
);
522 // Slot: Icon activated.
525 MainWindow::onTrayActivated (QSystemTrayIcon::ActivationReason r
) {
527 case QSystemTrayIcon::Trigger
: showNormal(); break;
532 // Slot: Preferences.
535 MainWindow::onPreferences () {
537 const bool icon_state
=
538 shPrefs
.get (SharedPreferences::CustomProgramIcon
).toBool ();
540 const QString icon_path
=
541 shPrefs
.get (SharedPreferences::ProgramIconPath
).toString ();
543 Preferences
dlg (this);
545 if (dlg
.exec () == QDialog::Accepted
) {
547 // User requested app restart.
549 if (dlg
.restartAfter ()) {
551 QProcess::startDetached (QCoreApplication::applicationFilePath ());
555 Qt::WindowFlags flags
= windowFlags ();
557 // Update window flags: Stay on top.
559 if (shPrefs
.get (SharedPreferences::StayOnTop
).toBool () ) {
561 if (! (flags
& Qt::WindowStaysOnTopHint
) )
562 flags
|= Qt::WindowStaysOnTopHint
;
566 if (flags
& Qt::WindowStaysOnTopHint
)
567 flags
&= ~Qt::WindowStaysOnTopHint
;
570 if (flags
!= windowFlags ()) {
571 setWindowFlags (flags
);
575 // Update program icon?
578 shPrefs
.get (SharedPreferences::CustomProgramIcon
).toBool ()
580 shPrefs
.get (SharedPreferences::ProgramIconPath
).toString ())
582 changeProgramIcon ();
592 MainWindow::onAbout ()
593 { About (this).exec (); }
598 MainWindow::onRecent () {
600 const QStringList lst
= recent
.toStringList ();
602 if (lst
.size () == 0) {
603 NomNom::info (this, tr ("No record of recently visited URLs found."));
609 const QString s
= QInputDialog::getItem (
629 { LogView (this).exec (); }
634 MainWindow::onAddress() {
637 QInputDialog::getText (this, tr ("Address"), tr ("Video URL:"));
646 extern QHash
<QString
,QString
> feed
;
651 MainWindow::onFeed () {
654 shPrefs
.get (SharedPreferences::UmphPath
).toString ();
656 if (path
.isEmpty ()) {
658 tr ("Specify path to the umph(1) command in the Preferences."));
664 if (!feed
.empty ()) {
666 const int rc
= NomNom::ask (this, tr ("Choose from old results?"));
668 if (rc
== QMessageBox::Yes
) {
669 if (!NomNom::choose_from_feed (this, url
))
675 if (url
.isEmpty ()) {
677 YoutubeFeed
d (this);
679 if (d
.exec () != QDialog::Accepted
|| !d
.gotItems ())
682 if (!NomNom::choose_from_feed (this, url
))
690 // Slot: quvi finished.
693 MainWindow::onProcFinished (QString output
)
699 MainWindow::dragEnterEvent (QDragEnterEvent
*e
) {
701 QUrl
url(e
->mimeData()->text());
703 if (url
.isValid() && url
.scheme().toLower() == "http")
704 e
->acceptProposedAction();
710 MainWindow::dropEvent (QDropEvent
*e
) {
711 handleURL (e
->mimeData ()->text ().simplified ());
712 e
->acceptProposedAction ();
716 // vim: set ts=4 sw=4 tw=72 expandtab: