1 /*  QWinFF - a qt4 gui frontend for ffmpeg
2  *  Copyright (C) 2011-2013 Timothy Lin <lzh9102@gmail.com>
3  *
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.
8  *
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.
13  *
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/>.
16  */
17 
18 #include "mainwindow.h"
19 #include "ui_mainwindow.h"
20 #include "convertlist.h"
21 #include "addtaskwizard.h"
22 #include "aboutffmpegdialog.h"
23 #include "optionsdialog.h"
24 #include "aboutdialog.h"
25 #include "poweroffdialog.h"
26 #include "updatedialog.h"
27 #include "services/paths.h"
28 #include "services/notification.h"
29 #include "services/powermanagement.h"
30 #include "converter/mediaconverter.h"
31 #include "converter/presets.h"
32 #include "services/updatechecker.h"
33 #include "services/constants.h"
34 #include "services/settingtimer.h"
35 #include "interactivecuttingdialog.h"
36 #include <QHBoxLayout>
37 #include <QToolButton>
38 #include <QMessageBox>
39 #include <QLabel>
40 #include <QFileDialog>
41 #include <QDesktopServices>
42 #include <QApplication>
43 #include <QSettings>
44 #include <QCloseEvent>
45 #include <QTimer>
46 #include <QSignalMapper>
47 #include <QPushButton>
48 #include <QDebug>
49 #include <QUrl>
50 
51 #define DATA_PATH "/usr/local/share/qwinff"
52 
MainWindow(QWidget * parent,const QStringList & fileList)53 MainWindow::MainWindow(QWidget *parent, const QStringList& fileList) :
54     QMainWindow(parent),
55     ui(new Ui::MainWindow),
56     m_presets(new Presets(this)),
57     m_list(new ConvertList(m_presets, this)),
58     m_argv_input_files(fileList),
59     m_elapsedTimeLabel(new QLabel(this)),
60     m_timer(new QTimer(this)),
61     m_poweroff_button(0),
62     m_poweroff_actiongroup(0),
63     m_update_checker(new UpdateChecker(this))
64 {
65     ui->setupUi(this);
66 
67     connect(m_list, SIGNAL(task_finished(int)),
68             this, SLOT(task_finished(int)));
69     connect(m_list, SIGNAL(all_tasks_finished()),
70             this, SLOT(all_tasks_finished()));
71     connect(m_list, SIGNAL(customContextMenuRequested(QPoint)),
72             this, SLOT(slotListContextMenu(QPoint)));
73     connect(m_list, SIGNAL(itemSelectionChanged()),
74             this, SLOT(refresh_action_states()));
75     connect(m_timer, SIGNAL(timeout()),
76             this, SLOT(timerEvent()));
77     connect(m_list, SIGNAL(started()),
78             this, SLOT(conversion_started()));
79     connect(m_list, SIGNAL(stopped()),
80             this, SLOT(conversion_stopped()));
81     connect(m_update_checker, SIGNAL(receivedResult(int)),
82             this, SLOT(received_update_result(int)));
83     connect(ui->btnStartConversion, SIGNAL(clicked()),
84             this, SLOT(slotStartConversion()));
85 
86     setup_widgets();
87     setup_menus();
88     setup_poweroff_button();
89     setup_toolbar(Constants::getSpaceSeparatedList("ToolbarEntries"));
90     setup_statusbar();
91     setup_appicon();
92 
93     load_settings();
94 
95     refresh_action_states();
96 
97     if (!check_execute_conditions()) {
98         // Close the window immediately after it has started.
99         QTimer::singleShot(0, this, SLOT(close()));
100     } else {
101         QTimer::singleShot(0, this, SLOT(window_ready()));
102     }
103 
104 }
105 
~MainWindow()106 MainWindow::~MainWindow()
107 {
108     delete ui;
109 }
110 
window_ready()111 void MainWindow::window_ready()
112 {
113     if (!m_argv_input_files.isEmpty()) {
114         add_files(m_argv_input_files);
115     }
116     QSettings settings;
117     if (settings.value("options/check_update_on_startup",
118                        Constants::getBool("CheckUpdateOnStartup")).toBool()) {
119         if (ask_for_update_permission())
120             m_update_checker->checkUpdate();
121     }
122     refresh_status();
123 }
124 
task_finished(int)125 void MainWindow::task_finished(int /*exitcode*/)
126 {
127 //    if (exitcode == 0) { // succeed
128 //        QMessageBox::information(this, this->windowTitle()
129 //                                 , tr("Conversion finished successfully.")
130 //                                 , QMessageBox::Ok);
131 //    } else { // failed
132 //        QMessageBox::critical(this, this->windowTitle()
133 //                              , tr("Conversion failed.")
134 //                              , QMessageBox::Ok);
135 //    }
136 }
137 
all_tasks_finished()138 void MainWindow::all_tasks_finished()
139 {
140     Notification::send(this, "QWinFF",
141                        tr("All tasks have finished."), NotifyLevel::INFO);
142     activateWindow(); // notify the user (make taskbar entry blink)
143     refresh_action_states();
144 
145     if (PowerManagement::implemented() && m_poweroff_button->isChecked()) {
146         // show poweroff dialog
147         if (PoweroffDialog(this).exec(get_poweroff_behavior()) == QDialog::Accepted) {
148             save_settings(); // save settings in case of power failure
149         }
150     }
151 }
152 
153 // Menu Events
154 
slotAddFiles()155 void MainWindow::slotAddFiles()
156 {
157     add_files();
158 }
159 
slotOptions()160 void MainWindow::slotOptions()
161 {
162     OptionsDialog dialog(this);
163     dialog.exec();
164 }
165 
slotSetTools()166 void MainWindow::slotSetTools()
167 {
168     OptionsDialog dialog(this);
169     dialog.exec_tools();
170 }
171 
slotExit()172 void MainWindow::slotExit()
173 {
174     this->close();
175 }
176 
slotStartConversion()177 void MainWindow::slotStartConversion()
178 {
179     if (m_list->isEmpty()) {
180         QMessageBox::information(this, this->windowTitle(),
181                                  tr("Nothing to convert."), QMessageBox::Ok);
182     } else {
183         m_list->start();
184     }
185 }
186 
slotStopConversion()187 void MainWindow::slotStopConversion()
188 {
189     m_list->stop();
190 }
191 
slotSetConversionParameters()192 void MainWindow::slotSetConversionParameters()
193 {
194     if (m_list->selectedCount() > 0) {
195         m_list->editSelectedParameters();
196     }
197 }
198 
199 // Open the output folder of the file.
slotOpenOutputFolder()200 void MainWindow::slotOpenOutputFolder()
201 {
202     const ConversionParameters *param = m_list->getCurrentIndexParameter();
203     if (param) {
204         QString folder_path = QFileInfo(param->destination).path();
205         if (QFileInfo(folder_path).exists()) {
206             QDesktopServices::openUrl(QUrl::fromLocalFile(folder_path));
207         }
208     }
209 }
210 
slotAboutQt()211 void MainWindow::slotAboutQt()
212 {
213     QMessageBox::aboutQt(this);
214 }
215 
slotAboutFFmpeg()216 void MainWindow::slotAboutFFmpeg()
217 {
218     AboutFFmpegDialog(this).exec();
219 }
220 
slotAbout()221 void MainWindow::slotAbout()
222 {
223     AboutDialog(this).exec();
224 }
225 
slotShowUpdateDialog()226 void MainWindow::slotShowUpdateDialog()
227 {
228     if (ask_for_update_permission()) {
229         UpdateChecker update_checker;
230         UpdateDialog(this).exec(update_checker);
231     }
232 }
233 
slotCut()234 void MainWindow::slotCut()
235 {
236     m_list->cutSelectedTask();
237 }
238 
slotListContextMenu(QPoint)239 void MainWindow::slotListContextMenu(QPoint /*pos*/)
240 {
241     refresh_action_states();
242 
243     QMenu menu;
244     menu.addAction(ui->actionOpenOutputFolder);
245     menu.addSeparator();
246     menu.addAction(ui->actionRemoveSelectedItems);
247     menu.addSeparator();
248     menu.addAction(ui->actionRetry);
249     menu.addAction(ui->actionRetryAll);
250     menu.addSeparator();
251     menu.addAction(ui->actionShowErrorMessage);
252     menu.addAction(ui->actionChangeOutputFilename);
253     menu.addAction(ui->actionChangeOutputDirectory);
254     menu.addAction(ui->actionSetParameters);
255     menu.addAction(ui->actionCut);
256 
257     menu.exec(QCursor::pos());
258 }
259 
260 // Events
261 
closeEvent(QCloseEvent * event)262 void MainWindow::closeEvent(QCloseEvent *event)
263 {
264     if (m_list->isBusy()) {
265         int reply = QMessageBox::warning(this, this->windowTitle(),
266                              tr("Conversion is still in progress. Abort?"),
267                              QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
268         if (reply == QMessageBox::No) {
269             event->ignore();
270             return;
271         }
272     }
273 
274     m_list->stop();
275 
276     save_settings();
277 }
278 
timerEvent()279 void MainWindow::timerEvent()
280 {
281     refresh_status();
282 }
283 
conversion_started()284 void MainWindow::conversion_started()
285 {
286     m_elapsedTimeLabel->clear();
287     m_timer->start(1000);
288     refresh_status();
289     refresh_action_states();
290 }
291 
conversion_stopped()292 void MainWindow::conversion_stopped()
293 {
294     m_timer->stop();
295     refresh_status();
296     refresh_action_states();
297 }
298 
update_poweroff_button(int id)299 void MainWindow::update_poweroff_button(int id)
300 {
301     const char *icon_id = "";
302     QString title = "";
303     QString status_tip = "";
304     switch (id) {
305     case PowerManagement::SHUTDOWN:
306         icon_id = ":/actions/icons/system_shutdown.png";
307         title = tr("Shutdown");
308         status_tip = tr("Shutdown when all tasks are done.");
309         break;
310     case PowerManagement::SUSPEND:
311         icon_id = ":/actions/icons/system_suspend.png";
312         title = tr("Suspend");
313         status_tip = tr("Suspend when all tasks are done.");
314         break;
315     case PowerManagement::HIBERNATE:
316         icon_id = ":/actions/icons/system_hibernate.png";
317         title = tr("Hibernate");
318         status_tip = tr("Hibernate when all tasks are done.");
319         break;
320     default:
321         Q_ASSERT(!"Incorrect id! Be sure to handle every power action in switch().");
322     }
323     m_poweroff_button->setIcon(QIcon(icon_id));
324     m_poweroff_button->setToolTip(status_tip);
325     m_poweroff_button->setStatusTip(status_tip);
326     ui->actionPoweroff->setIcon(QIcon(icon_id));
327     ui->actionPoweroff->setText(title);
328     ui->actionPoweroff->setStatusTip(status_tip);
329 }
330 
received_update_result(int status)331 void MainWindow::received_update_result(int status)
332 {
333     if (status == UpdateChecker::UpdateFound) {
334         QSettings settings;
335         SettingTimer timer("mainwindow/last_remind_update_time");
336         const int seconds_per_day = 86400;
337         const unsigned int prev_update_version =
338                 settings.value("mainwindow/last_remind_update_version").toUInt();
339         const unsigned int new_update_version = m_update_checker->versionId();
340         const bool timeout = !timer.isValid()
341                 || timer.elapsedSeconds() > seconds_per_day;
342         const bool is_different_version =
343                 new_update_version != prev_update_version;
344 
345         // Show update dialog only if the update dialog has not been shown
346         // for a certain period or the version is different.
347         if (timeout || is_different_version) {
348             UpdateDialog(this).exec(*m_update_checker);
349             timer.restart();
350             settings.setValue("mainwindow/last_remind_update_version",
351                               new_update_version);
352         }
353     }
354 }
355 
356 // Private Methods
357 
358 /* Check if all execute conditions are met.
359    This function should return true if all the conditions are met
360    and return false if any of the conditions fails.
361 */
check_execute_conditions()362 bool MainWindow::check_execute_conditions()
363 {
364     QString errmsg;
365 
366     // check external programs
367     if (!MediaConverter::checkExternalPrograms(errmsg)) {
368         QMessageBox::critical(this, this->windowTitle(), errmsg);
369 #ifdef TOOLS_IN_DATA_PATH
370         return false; // fatal: ffmpeg should be in the data path but doesn't exist
371 #else
372         QTimer::singleShot(0, this, SLOT(slotSetTools()));
373 #endif
374     }
375     // load presets
376     if (!load_presets())
377         return false;
378 
379     return true;
380 }
381 
382 // We should respect the user and ask before connecting to the Internet to
383 // check for updates.
384 // If the user says yes, remember the decision and don't ask next time.
385 // If the user says no, disable checking for updates on startup.
ask_for_update_permission()386 bool MainWindow::ask_for_update_permission()
387 {
388     const char *setting_key = "update_permission";
389     QSettings settings;
390     bool permitted = settings.value(setting_key, false).toBool();
391     if (permitted) return true;
392 
393     QString msg = tr("This program is going to check for updates online. "
394                      "Do you allow this program to use the Internet "
395                      "to check for updates?");
396 
397     int reply = QMessageBox::information(this,
398                                           windowTitle(),
399                                           msg,
400                                           QMessageBox::Yes,
401                                           QMessageBox::No);
402 
403     if (reply == QMessageBox::Yes) { // permitted
404         settings.setValue(setting_key, true); // don't ask next time
405         return true;
406     } else { // rejected
407         // disable auto update because the user probably doesn't like it
408         settings.setValue("options/check_update_on_startup", false);
409         return false;
410     }
411 }
412 
413 // Popup wizard to add tasks.
add_files()414 void MainWindow::add_files()
415 {
416     AddTaskWizard wizard(m_presets, this);
417 
418     if (wizard.exec_openfile() == QDialog::Accepted) {
419         // Add all input files to the list.
420         const QList<ConversionParameters> &paramList = wizard.getConversionParameters();
421         m_list->addTasks(paramList);
422     }
423 }
424 
add_files(const QStringList & fileList)425 void MainWindow::add_files(const QStringList &fileList)
426 {
427     AddTaskWizard wizard(m_presets, this);
428 
429     if (wizard.exec(fileList) == QDialog::Accepted) {
430         // Add all input files to the list.
431         const QList<ConversionParameters> &paramList = wizard.getConversionParameters();
432         m_list->addTasks(paramList);
433     }
434 }
435 
setup_widgets()436 void MainWindow::setup_widgets()
437 {
438     // list
439     ui->layoutListPlaceholder->addWidget(m_list);
440     m_list->adjustSize();
441     m_list->setContextMenuPolicy(Qt::CustomContextMenu);
442 
443     ui->lblTime->clear();
444 }
445 
setup_menus()446 void MainWindow::setup_menus()
447 {
448     /* === Menu Events === */
449 
450     // File
451     connect(ui->actionAddFiles, SIGNAL(triggered()),
452             this, SLOT(slotAddFiles()));
453     connect(ui->actionOptions, SIGNAL(triggered()),
454             this, SLOT(slotOptions()));
455     connect(ui->actionExit, SIGNAL(triggered()),
456             this, SLOT(slotExit()));
457 
458     // Edit
459     connect(ui->menuEdit, SIGNAL(aboutToShow()),
460             this, SLOT(refresh_action_states()));
461     connect(ui->actionRemoveSelectedItems, SIGNAL(triggered()),
462             m_list, SLOT(removeSelectedItems()));
463     connect(ui->actionRemoveCompletedItems, SIGNAL(triggered()),
464             m_list, SLOT(removeCompletedItems()));
465     connect(ui->actionClearList, SIGNAL(triggered()),
466             m_list, SLOT(clear()));
467     connect(ui->actionSetParameters, SIGNAL(triggered()),
468             this, SLOT(slotSetConversionParameters()));
469     connect(ui->actionOpenOutputFolder, SIGNAL(triggered()),
470             this, SLOT(slotOpenOutputFolder()));
471     connect(ui->actionChangeOutputFilename, SIGNAL(triggered()),
472             m_list, SLOT(changeSelectedOutputFile()));
473     connect(ui->actionChangeOutputDirectory, SIGNAL(triggered()),
474             m_list, SLOT(changeSelectedOutputDirectory()));
475     connect(ui->actionShowErrorMessage, SIGNAL(triggered()),
476             m_list, SLOT(showErrorMessage()));
477     connect(ui->actionCut, SIGNAL(triggered()), SLOT(slotCut()));
478     ui->actionCut->setVisible(InteractiveCuttingDialog::available());
479 
480     // Convert
481     connect(ui->menuConvert, SIGNAL(aboutToShow()),
482             this, SLOT(refresh_action_states()));
483     connect(ui->actionStartConversion, SIGNAL(triggered()),
484             this, SLOT(slotStartConversion()));
485     connect(ui->actionStopConversion, SIGNAL(triggered()),
486             this, SLOT(slotStopConversion()));
487     connect(ui->actionRetry, SIGNAL(triggered()),
488             m_list, SLOT(retrySelectedItems()));
489     connect(ui->actionRetry, SIGNAL(triggered()),
490             this, SLOT(refresh_action_states()));
491     connect(ui->actionRetryAll, SIGNAL(triggered()),
492             m_list, SLOT(retryAll()));
493     connect(ui->actionRetryAll, SIGNAL(triggered()),
494             this, SLOT(refresh_action_states()));
495 
496     // About
497     connect(ui->actionAboutQt, SIGNAL(triggered()),
498             this, SLOT(slotAboutQt()));
499     connect(ui->actionAboutFFmpeg, SIGNAL(triggered()),
500             this, SLOT(slotAboutFFmpeg()));
501     connect(ui->actionAbout, SIGNAL(triggered()),
502             this, SLOT(slotAbout()));
503     connect(ui->actionCheckUpdate, SIGNAL(triggered()),
504             this, SLOT(slotShowUpdateDialog()));
505 }
506 
setup_toolbar(const QStringList & entries)507 void MainWindow::setup_toolbar(const QStringList &entries)
508 {
509     Q_ASSERT(m_poweroff_button && "setup_poweroff_button() must be called first");
510 
511     // construct a table of available actions
512     // map action name to action pointer
513     QMap<QString, QAction*> toolbar_table;
514 #define ADD_ACTION(name) toolbar_table[QString(#name).toUpper()] = ui->action ## name
515     ADD_ACTION(AddFiles);
516     ADD_ACTION(Options);
517     ADD_ACTION(Exit);
518     ADD_ACTION(RemoveSelectedItems);
519     ADD_ACTION(RemoveCompletedItems);
520     ADD_ACTION(ClearList);
521     ADD_ACTION(OpenOutputFolder);
522     ADD_ACTION(SetParameters);
523     ADD_ACTION(ChangeOutputFilename);
524     ADD_ACTION(ChangeOutputDirectory); // TODO: rename to "folder"
525     ADD_ACTION(ShowErrorMessage);
526     ADD_ACTION(StartConversion);
527     ADD_ACTION(StopConversion);
528     ADD_ACTION(Retry);
529     ADD_ACTION(RetryAll);
530     // "Shutdown" button is special, so we don't add it here
531 #define POWEROFF_BUTTON_NAME "POWEROFF"
532     ADD_ACTION(AboutQt);
533     ADD_ACTION(AboutFFmpeg);
534     ADD_ACTION(About);
535     ADD_ACTION(CheckUpdate);
536 
537     for (int i=0; i<entries.size(); i++) {
538         QString entry = entries[i].toUpper(); // case-insensitive compare
539         if (entry == POWEROFF_BUTTON_NAME && PowerManagement::implemented())
540             ui->toolBar->addWidget(m_poweroff_button);
541         else if (entry == "|") // separator
542             ui->toolBar->addSeparator();
543         else if (toolbar_table.contains(entry))
544             ui->toolBar->addAction(toolbar_table[entry]);
545     }
546 }
547 
setup_statusbar()548 void MainWindow::setup_statusbar()
549 {
550     ui->statusBar->addPermanentWidget(m_elapsedTimeLabel);
551 }
552 
553 /*
554  * Setup the poweroff button and menu.
555  * The poweroff button is handled differently from other menu and buttons.
556  * Its icon and title changes as the action changes.
557  * When this function finishes, m_poweroff_button will point to the constructed
558  * button widget.
559  */
setup_poweroff_button()560 void MainWindow::setup_poweroff_button()
561 {
562     QToolButton *button = new QToolButton(this);
563     QMenu *menu = new QMenu(this);
564     QList<QAction*> actionList;
565     QSignalMapper *signalMapper = new QSignalMapper(this);
566     QActionGroup *checkGroup = new QActionGroup(this);
567 
568     m_poweroff_button = button;
569     m_poweroff_actiongroup = checkGroup;
570 
571     // Insert all actions into the list.
572     for (int i=0; i<PowerManagement::ACTION_COUNT; i++) {
573         const char *icon_id = "";
574         QString text = "";
575         switch (i) {
576         case PowerManagement::SHUTDOWN:
577             //: Shutdown the computer (completely poweroff)
578             text = tr("Shutdown");
579             icon_id = ":/actions/icons/system_shutdown.png";
580             break;
581         case PowerManagement::SUSPEND:
582             //: Suspend the computer (sleep to ram, standby)
583             text = tr("Suspend");
584             icon_id = ":/actions/icons/system_suspend.png";
585             break;
586         case PowerManagement::HIBERNATE:
587             //: Hibernate the computer (sleep to disk, completely poweroff)
588             text = tr("Hibernate");
589             icon_id = ":/actions/icons/system_hibernate.png";
590             break;
591         default:
592             Q_ASSERT(!"Incorrect id! Be sure to implement every power action in switch().");
593         }
594         actionList.append(new QAction(QIcon(icon_id)
595                                       , text, this));
596     }
597 
598     // Add all actions into the menu (from list)
599     foreach (QAction *action, actionList) {
600         menu->addAction(action);
601         action->setCheckable(true);
602         action->setActionGroup(checkGroup);
603     }
604 
605     button->setMenu(menu);
606     button->setPopupMode(QToolButton::MenuButtonPopup);
607 
608     // ensure that the toolbutton and actionPoweroff are both checkable
609     ui->actionPoweroff->setCheckable(true);
610     button->setCheckable(true);
611     button->setChecked(false);
612 
613     /* Synchronize the checked state of the toolbutton and actionPoweroff.
614        This cyclic connection doesn't cause an infinite loop because
615        toggled(bool) is only triggered when the checked() state changes.
616      */
617     connect(button, SIGNAL(toggled(bool))
618             , ui->actionPoweroff, SLOT(setChecked(bool)));
619     connect(ui->actionPoweroff, SIGNAL(toggled(bool))
620             , button, SLOT(setChecked(bool)));
621 
622     // update the poweroff button when the action changes
623     for (int i=0; i<actionList.size(); i++) {
624         QAction *action = actionList.at(i);
625         signalMapper->setMapping(action, i);
626         connect(action, SIGNAL(triggered())
627                 , signalMapper, SLOT(map()));
628         connect(signalMapper, SIGNAL(mapped(int))
629                 , this, SLOT(update_poweroff_button(int)));
630     }
631 
632     actionList.at(0)->trigger();
633 
634     /* Check if the power management functions are available.
635        If not, hide poweroff button and menus.
636      */
637     if (!PowerManagement::implemented()) {
638         m_poweroff_button->setVisible(false);
639         ui->actionPoweroff->setVisible(false);
640     }
641 }
642 
643 // Fill window icon with multiple sizes of images.
setup_appicon()644 void MainWindow::setup_appicon()
645 {
646     QIcon icon;
647     QDir iconDir = QDir(":/app/icons/");
648     QStringList fileList = iconDir.entryList();
649     QRegExp pattern("^qwinff_[0-9]+x[0-9]+\\.png$");
650     foreach (QString file, fileList) {
651         if (pattern.indexIn(file) >= 0) {
652             icon.addPixmap(QPixmap(iconDir.absoluteFilePath(file)));
653         }
654     }
655     setWindowIcon(icon);
656     ui->actionAbout->setIcon(icon);
657 }
658 
set_poweroff_behavior(int action)659 void MainWindow::set_poweroff_behavior(int action)
660 {
661     if (action >= PowerManagement::ACTION_COUNT)
662         action = PowerManagement::SHUTDOWN;
663     m_poweroff_actiongroup->actions().at(action)->trigger();
664 }
665 
get_poweroff_behavior()666 int MainWindow::get_poweroff_behavior()
667 {
668     for (int i=0; i<m_poweroff_actiongroup->actions().size(); i++) {
669         if (m_poweroff_actiongroup->actions().at(i)->isChecked())
670             return i;
671     }
672     return PowerManagement::SHUTDOWN;
673 }
674 
load_presets()675 bool MainWindow::load_presets()
676 {
677     // The default preset file is located in <datapath>/presets.xml
678     //QString default_preset_file = QDir(Paths::dataPath()).absoluteFilePath("presets.xml");
679     QString app_path = QString(DATA_PATH);
680     QString default_preset_file = QDir(app_path).absoluteFilePath("presets.xml");
681 
682     QString local_preset_file;
683     if (!Constants::getBool("Portable")) { // non-portable app
684         // rename local preset file created by older versions of qwinff
685         // operation: mv ~/.qwinff/presets.xml ~/.qwinff/presets.xml.old
686         QString local_preset_file_old = QDir(QDir::homePath()).absoluteFilePath(".qwinff/presets.xml");
687         if (QFile(local_preset_file_old).exists()) {
688             QFile::remove(local_preset_file_old + ".old");
689             if (QFile::rename(local_preset_file_old, local_preset_file_old + ".old")) {
690                 qDebug() << local_preset_file_old + " is no longer used, "
691                             "rename to " + local_preset_file_old + ".old";
692             }
693         }
694 
695         // use global preset temporarily
696         local_preset_file = default_preset_file;
697     } else {
698         // portable app
699         local_preset_file = default_preset_file;
700     }
701 
702     QSettings settings;
703     bool removeUnavailableCodecs = settings.value("options/hideformats", true).toBool();
704     // Load the preset file from the user's home directory
705     // The presets are loaded once and shared between objects
706     // that need the information.
707     if (!m_presets->readFromFile(local_preset_file, removeUnavailableCodecs)) {
708         QMessageBox::critical(this, this->windowTitle(),
709                               tr("Failed to load preset file. "
710                                  "The application will quit now."));
711         return false;
712     }
713     return true;
714 }
715 
716 // Hide unused actions
refresh_action_states()717 void MainWindow::refresh_action_states()
718 {
719     int selected_file_count = m_list->selectedCount();
720 
721     // Hide actionSetParameters if no item in m_list is selected.
722     bool hide_SetParameters = (selected_file_count == 0);
723 
724     // Hide actionStartConversion if the conversion is in progress.
725     bool hide_StartConversion = m_list->isBusy();
726 
727     // Hide actionStopConversion if nothing is being converted.
728     bool hide_StopConversion = !m_list->isBusy();
729 
730     // Show actionOpenOutputFolder only if 1 file is selected.
731     bool hide_OpenFolder = (selected_file_count <= 0);
732 
733     // Hide actionRemoveSelectedItems if no file is selected.
734     bool hide_RemoveSelectedItems = (selected_file_count == 0);
735 
736     bool hide_Retry = (selected_file_count == 0);
737     bool hide_RetryAll = (m_list->isEmpty());
738 
739     bool hide_ClearList = (m_list->isEmpty());
740 
741     bool hide_ChangeOutputFilename = m_list->selectedCount() != 1;
742     bool hide_ChangeOutputDirectory = m_list->selectedCount() <= 0;
743 
744 
745     /* Show actionShowErrorMessage if and only if one task is selected
746        and the state of the selected task is FAILED
747      */
748     bool hide_ShowErrorMessage = (selected_file_count != 1
749                                   || !m_list->selectedTaskFailed());
750 
751     ui->actionSetParameters->setDisabled(hide_SetParameters);
752     ui->actionStartConversion->setDisabled(hide_StartConversion);
753     ui->actionStopConversion->setDisabled(hide_StopConversion);
754     ui->actionOpenOutputFolder->setDisabled(hide_OpenFolder);
755     ui->actionRemoveSelectedItems->setDisabled(hide_RemoveSelectedItems);
756     ui->actionRetry->setDisabled(hide_Retry);
757     ui->actionRetryAll->setDisabled(hide_RetryAll);
758     ui->actionClearList->setDisabled(hide_ClearList);
759     ui->actionChangeOutputFilename->setDisabled(hide_ChangeOutputFilename);
760     ui->actionChangeOutputDirectory->setDisabled(hide_ChangeOutputDirectory);
761     ui->actionShowErrorMessage->setDisabled(hide_ShowErrorMessage);
762     ui->actionCut->setEnabled(selected_file_count == 1); // cut only 1 file at a time
763 }
764 
load_settings()765 void MainWindow::load_settings()
766 {
767     QSettings settings;
768     restoreGeometry(settings.value("mainwindow/geometry").toByteArray());
769     restoreState(settings.value("mainwindow/state").toByteArray());
770     int poweroff_behavior = settings.value("options/poweroff_behavior"
771                                          , PowerManagement::SHUTDOWN).toInt();
772     set_poweroff_behavior(poweroff_behavior);
773 
774 }
775 
save_settings()776 void MainWindow::save_settings()
777 {
778     QSettings settings;
779     settings.setValue("mainwindow/geometry", saveGeometry());
780     settings.setValue("mainwindow/state", saveState());
781     settings.setValue("options/poweroff_behavior", get_poweroff_behavior());
782 }
783 
refresh_status()784 void MainWindow::refresh_status()
785 {
786     refresh_statusbar();
787     refresh_titlebar();
788 }
789 
refresh_statusbar()790 void MainWindow::refresh_statusbar()
791 {
792     if (m_list->isBusy()) {
793         int total_seconds = m_list->elapsedTime() / 1000;
794         int hours = total_seconds / 3600;
795         int minutes = (total_seconds / 60) % 60;
796         int seconds = total_seconds % 60;
797 
798         QString timeinfo = tr("Elapsed Time: %1 h %2 m %3 s")
799                 .arg(hours).arg(minutes).arg(seconds);
800         //m_elapsedTimeLabel->setText(timeinfo);
801         ui->lblTime->setText(timeinfo);
802     } else {
803         //m_elapsedTimeLabel->clear();
804         //ui->lblTime->clear();
805     }
806 }
807 
refresh_titlebar()808 void MainWindow::refresh_titlebar()
809 {
810     const int task_count = m_list->count();
811     const int finished_task_count = m_list->finishedCount();
812     if (finished_task_count < task_count && m_list->isBusy()) {
813         //: Converting the %1-th file in %2 files. %2 is the number of files.
814         setWindowTitle(tr("Converting %1/%2")
815                        .arg(finished_task_count+1).arg(task_count));
816     } else {
817         setWindowTitle(tr("QWinFF"));
818     }
819 }
820