#include "mainwindow.h" #include "./ui_mainwindow.h" #include "src/archive/base_archive.h" #include "src/command_line.h" #include "src/recent_files.h" #include "src/settingsdialog.h" #include "src/aboutdialog.h" #include "src/widgets/tab_activity_list.h" #include "src/widgets/tab_actor_info.h" #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); recent_files_menu = recent_files.create_menu(tr("Recent files…")); ui->menuFile->insertMenu(ui->actionSettings, recent_files_menu.get()); connect(recent_files_menu.get(), &QMenu::triggered, this, [&](QAction* action) { // Needed for interepreting a QAction as a RecentFiles::Action so that we can retrieve the file_path and open it if (auto* ac = qobject_cast(action); ac) this->open_file(ac->file_path); }); create_initial_tabs(); connect(&archive_thread_watcher, &QFutureWatcher::finished, this, &MainWindow::archive_thread_watcher_done); } MainWindow::~MainWindow() { delete ui; } void MainWindow::create_initial_tabs() { welcome_tab = new TabWelcome(recent_files_menu); ui->tabWidget->addTab(welcome_tab, tr("Welcome")); connect(welcome_tab, &TabWelcome::show_file_open_dialog, this, &MainWindow::on_actionOpen_triggered); activity_list_tab = new TabActivityList(&data_archive); ui->tabWidget->addTab(activity_list_tab, tr("Activity List")); activity_list_tab->view_actions = { .actionAll_toots = ui->actionAll_toots, .actionPublic_toots = ui->actionPublic_toots, .actionUnlisted_toots = ui->actionUnlisted_toots, .actionPrivate_toots = ui->actionPrivate_toots, .actionDirect_messages = ui->actionDirect_messages, .actionOnly_with_attachment = ui->actionOnly_with_attachment }; connect(ui->actionAll_toots, &QAction::triggered, activity_list_tab, &TabActivityList::actionAll_toots_triggered); connect(ui->actionPublic_toots, &QAction::triggered, activity_list_tab, &TabActivityList::actionPublic_toots_triggered); connect(ui->actionUnlisted_toots, &QAction::triggered, activity_list_tab, &TabActivityList::actionUnlisted_toots_triggered); connect(ui->actionPrivate_toots, &QAction::triggered, activity_list_tab, &TabActivityList::actionPrivate_toots_triggered); connect(ui->actionDirect_messages, &QAction::triggered, activity_list_tab, &TabActivityList::actionDirect_messages_triggered); connect(ui->actionOnly_with_attachment, &QAction::triggered, activity_list_tab, &TabActivityList::actionOnly_with_attachment_triggered); connect(ui->actionReblogs, &QAction::triggered, activity_list_tab, &TabActivityList::actionReblogs_triggered); connect(ui->actionRandom_status, &QAction::triggered, activity_list_tab, &TabActivityList::on_buttonRandom_clicked); connect(ui->actionCopy_status, &QAction::triggered, activity_list_tab, &TabActivityList::on_buttonCopy_clicked); connect(ui->actionFind, &QAction::triggered, activity_list_tab, &TabActivityList::on_buttonSearch_clicked); #ifdef MASTODONPP_BUILD // Only display if mastodonpp build // TODO: This should be more precise if we ever add other Fedi APIs. connect(ui->actionOpen_URL, &QAction::triggered, activity_list_tab, &TabActivityList::actionOpen_URL_triggered); #else ui->actionOpen_URL->deleteLater(); #endif connect(ui->menuView, &QMenu::aboutToHide, activity_list_tab, &TabActivityList::menuView_aboutToHide); connect(this, &MainWindow::new_archive_opened, activity_list_tab, [=](bool is_valid) { if (is_valid) activity_list_tab->relist_statuses(true); else activity_list_tab->clear_list(); }); // TODO: maybe have one Actor info tab that is constantly updated with the new opened archive? // actor_info_tab = new TabActorInfo(data_archive); // ui->tabWidget->addTab(actor_info_tab, tr("Actor Info")); // TODO: Add the "+" tab for opening new tabs } void MainWindow::act_command_line(CommandLineParsedOptions &options, QCommandLineParser &parser) { switch (options.result) { case CommandLineError: QMessageBox::critical(this, tr("Command line"), tr("The passed command line is incorrect and will be ignored.\nMore information: %1").arg(options.error_message)); exit(EXIT_FAILURE); break; case CommandLineHelpRequested: QMessageBox::information(this, QGuiApplication::applicationDisplayName(), QStringLiteral("
%1
").arg(parser.helpText())); exit(EXIT_SUCCESS); break; case CommandLineVersionRequested: on_actionAbout_triggered(true); exit(EXIT_SUCCESS); break; case CommandLineOk: if (!options.outbox_filename.isEmpty()) open_file(options.outbox_filename); break; } } void MainWindow::on_actionOpen_triggered() { QFileDialog fileDialog; fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setNameFilter(tr("Mastodon data export directory (outbox.json)")); if (fileDialog.exec()) { QStringList files = fileDialog.selectedFiles(); open_file(files[0]); } } void MainWindow::on_actionSettings_triggered(bool checked) { if (!settings_dialog) { settings_dialog = new SettingsDialog(this); connect(settings_dialog, &QDialog::finished, this, &MainWindow::settingsDialog_done); if (data_archive) settings_dialog->set_instance_address(data_archive->get_instance_address()); } settings_dialog->open(); } void MainWindow::settingsDialog_done(int result) { if (result == QDialog::Accepted) { // TODO: reload settings } delete settings_dialog; settings_dialog = nullptr; } void MainWindow::on_actionQuit_triggered(bool checked) { QCoreApplication::quit(); } void MainWindow::on_actionAbout_triggered(bool checked) { AboutDialog about_dialog(this); about_dialog.exec(); } void MainWindow::open_file(const QString &filename) { // Close the welcome tab as it's not needed anymore // Done here as this method is directly called when opening a recent file if (welcome_tab) { welcome_tab->deleteLater(); welcome_tab = nullptr; } if (data_archive) { delete data_archive; data_archive = nullptr; } open_file_filename = filename; recent_files.add_file(filename); // BUG: the overwritten cursor seems to get "lost" and revert in moc_mainwindow.cpp (after the end of the function we're in) when opening a file through a file dialog. // The cursor behaves properly when opening a file throught the command line. QApplication::setOverrideCursor(Qt::WaitCursor); data_archive = Archive::create_archive(ArchiveType::MASTODON, filename); if (not data_archive) return; #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) QFuture parse_error = QtConcurrent::run(&Archive::init, data_archive); #else QFuture parse_error = QtConcurrent::run(data_archive, &Archive::init); #endif archive_thread_watcher.setFuture(parse_error); } void MainWindow::finish_open_file(const Archive::InitError& parse_error) { switch (parse_error) { case Archive::FailedOpeningFile: QMessageBox::warning(this, tr("Archive Parser"), tr("Failed opening file %1").arg(open_file_filename)); break; case Archive::JsonParseError: QMessageBox::warning(this, tr("Archive Parser"), tr("Failed parsing outbox JSON.")); break; case Archive::JsonEmpty: case Archive::JsonNull: case Archive::JsonNotObject: case Archive::JsonNotActivityStream: QMessageBox::warning(this, tr("Archive Parser"), tr("The outbox JSON is empty or invalid in another way. Not an outbox.json?")); break; case Archive::NoError: break; } // Avoids errors elsewhere down the line if (parse_error != Archive::NoError) { delete data_archive; data_archive = nullptr; } emit new_archive_opened(parse_error == Archive::NoError); actor_info_tab = new TabActorInfo(data_archive); ui->tabWidget->addTab(actor_info_tab, tr("Actor Info")); connect(this, &MainWindow::new_archive_opened, actor_info_tab, &TabActorInfo::deleteLater); // The cursor is restored in TabActivityList::relist_statuses() } void MainWindow::archive_thread_watcher_done() { Archive::InitError result = archive_thread_watcher.result(); finish_open_file(result); }