diff options
Diffstat (limited to 'qt-ui/mainwindow.cpp')
-rw-r--r-- | qt-ui/mainwindow.cpp | 548 |
1 files changed, 370 insertions, 178 deletions
diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index 82eaaac57..a06a2c227 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -11,7 +11,8 @@ #include <QSettings> #include <QShortcut> #include <QToolBar> -#include "ssrf-version.h" +#include "version.h" +#include "divelistview.h" #include "downloadfromdivecomputer.h" #include "preferences.h" #include "subsurfacewebservices.h" @@ -20,6 +21,10 @@ #include "updatemanager.h" #include "planner.h" #include "filtermodels.h" +#include "profile/profilewidget2.h" +#include "globe.h" +#include "maintab.h" +#include "diveplanner.h" #ifndef NO_PRINTING #include <QPrintDialog> #include "printdialog.h" @@ -27,10 +32,15 @@ #include "divelogimportdialog.h" #include "divelogexportdialog.h" #include "usersurvey.h" +#include "divesitehelpers.h" +#include "locationinformation.h" #ifndef NO_USERMANUAL #include "usermanual.h" #endif #include <QNetworkProxy> +#include <QUndoStack> +#include <qthelper.h> +#include <QtConcurrentRun> MainWindow *MainWindow::m_Instance = NULL; @@ -44,7 +54,26 @@ MainWindow::MainWindow() : QMainWindow(), Q_ASSERT_X(m_Instance == NULL, "MainWindow", "MainWindow recreated!"); m_Instance = this; ui.setupUi(this); - ui.multiFilter->hide(); + read_hashes(); + // Define the States of the Application Here, Currently the states are situations where the different + // widgets will change on the mainwindow. + + // for the "default" mode + MainTab *mainTab = new MainTab(); + DiveListView *diveListView = new DiveListView(); + ProfileWidget2 *profileWidget = new ProfileWidget2(); + +#ifndef NO_MARBLE + GlobeGPS *globeGps = new GlobeGPS(); +#else + QWidget *globeGps = NULL; +#endif + + PlannerSettingsWidget *plannerSettings = new PlannerSettingsWidget(); + DivePlannerWidget *plannerWidget = new DivePlannerWidget(); + PlannerDetails *plannerDetails = new PlannerDetails(); + LocationInformationWidget *locationInformation = new LocationInformationWidget(); + // what is a sane order for those icons? we should have the ones the user is // most likely to want towards the top so they are always visible // and the ones that someone likely sets and then never touches again towards the bottom @@ -57,69 +86,82 @@ MainWindow::MainWindow() : QMainWindow(), ui.profEad << ui.profSAC << ui.profHR << // very few dive computers support this ui.profTissues; // maybe less frequently used + + QToolBar *toolBar = new QToolBar(); + Q_FOREACH (QAction *a, profileToolbarActions) + toolBar->addAction(a); + toolBar->setOrientation(Qt::Vertical); + toolBar->setIconSize(QSize(24,24)); + + QWidget *profileContainer = new QWidget(); + QHBoxLayout *profLayout = new QHBoxLayout(); + profLayout->addWidget(toolBar); + profLayout->addWidget(profileWidget); + profileContainer->setLayout(profLayout); + + registerApplicationState("Default", mainTab, profileContainer, diveListView, globeGps ); + registerApplicationState("AddDive", mainTab, profileContainer, diveListView, globeGps ); + registerApplicationState("EditDive", mainTab, profileContainer, diveListView, globeGps ); + registerApplicationState("PlanDive", plannerWidget, profileContainer, plannerSettings, plannerDetails ); + registerApplicationState("EditPlannedDive", plannerWidget, profileContainer, diveListView, globeGps ); + registerApplicationState("EditDiveSite",locationInformation, profileContainer, diveListView, globeGps ); + + setApplicationState("Default"); + + ui.multiFilter->hide(); + setWindowIcon(QIcon(":subsurface-icon")); if (!QIcon::hasThemeIcon("window-close")) { QIcon::setThemeName("subsurface"); } - connect(ui.ListWidget, SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int))); + connect(dive_list(), SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int))); connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), this, SLOT(readSettings())); - connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui.ListWidget, SLOT(update())); - connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui.ListWidget, SLOT(reloadHeaderActions())); - connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui.InfoWidget, SLOT(updateDiveInfo())); - connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui.divePlannerWidget, SLOT(settingsChanged())); - connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui.plannerSettingsWidget, SLOT(settingsChanged())); + connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), diveListView, SLOT(update())); + connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), diveListView, SLOT(reloadHeaderActions())); + connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), information(), SLOT(updateDiveInfo())); + connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), divePlannerWidget(), SLOT(settingsChanged())); + connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), divePlannerSettingsWidget(), SLOT(settingsChanged())); connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), TankInfoModel::instance(), SLOT(update())); connect(ui.actionRecent1, SIGNAL(triggered(bool)), this, SLOT(recentFileTriggered(bool))); connect(ui.actionRecent2, SIGNAL(triggered(bool)), this, SLOT(recentFileTriggered(bool))); connect(ui.actionRecent3, SIGNAL(triggered(bool)), this, SLOT(recentFileTriggered(bool))); connect(ui.actionRecent4, SIGNAL(triggered(bool)), this, SLOT(recentFileTriggered(bool))); - connect(information(), SIGNAL(addDiveFinished()), ui.newProfile, SLOT(setProfileState())); + connect(information(), SIGNAL(addDiveFinished()), graphics(), SLOT(setProfileState())); connect(DivePlannerPointsModel::instance(), SIGNAL(planCreated()), this, SLOT(planCreated())); connect(DivePlannerPointsModel::instance(), SIGNAL(planCanceled()), this, SLOT(planCanceled())); - connect(ui.printPlan, SIGNAL(pressed()), ui.divePlannerWidget, SLOT(printDecoPlan())); + connect(plannerDetails->printPlan(), SIGNAL(pressed()), divePlannerWidget(), SLOT(printDecoPlan())); + connect(mainTab, SIGNAL(requestDiveSiteEdit(uint32_t)), this, SLOT(enableDiveSiteEdit(uint32_t))); + connect(locationInformation, SIGNAL(informationManagementEnded()), this, SLOT(setDefaultState())); + connect(locationInformation, SIGNAL(informationManagementEnded()), information(), SLOT(showLocation())); + #ifdef NO_PRINTING - ui.printPlan->hide(); + plannerDetails->printPlan()->hide(); + ui.menuFile->removeAction(ui.actionPrint); #endif ui.mainErrorMessage->hide(); - ui.newProfile->setEmptyState(); + graphics()->setEmptyState(); initialUiSetup(); readSettings(); - ui.ListWidget->reload(DiveTripModel::TREE); - ui.ListWidget->reloadHeaderActions(); - ui.ListWidget->setFocus(); - ui.globe->reload(); - ui.ListWidget->expand(ui.ListWidget->model()->index(0, 0)); - ui.ListWidget->scrollTo(ui.ListWidget->model()->index(0, 0), QAbstractItemView::PositionAtCenter); - ui.divePlannerWidget->settingsChanged(); - ui.plannerSettingsWidget->settingsChanged(); + diveListView->reload(DiveTripModel::TREE); + diveListView->reloadHeaderActions(); + diveListView->setFocus(); + globe()->reload(); + diveListView->expand(dive_list()->model()->index(0, 0)); + diveListView->scrollTo(dive_list()->model()->index(0, 0), QAbstractItemView::PositionAtCenter); + divePlannerWidget()->settingsChanged(); + divePlannerSettingsWidget()->settingsChanged(); #ifdef NO_MARBLE - ui.globePane->hide(); ui.menuView->removeAction(ui.actionViewGlobe); #else - connect(ui.globe, SIGNAL(coordinatesChanged()), ui.InfoWidget, SLOT(updateGpsCoordinates())); + connect(globe(), SIGNAL(coordinatesChanged()), locationInformation, SLOT(updateGpsCoordinates())); #endif #ifdef NO_USERMANUAL ui.menuHelp->removeAction(ui.actionUserManual); #endif -#ifdef NO_PRINTING - ui.menuFile->removeAction(ui.actionPrint); -#endif memset(©PasteDive, 0, sizeof(copyPasteDive)); memset(&what, 0, sizeof(what)); - QToolBar *toolBar = new QToolBar(); - Q_FOREACH (QAction *a, profileToolbarActions) - toolBar->addAction(a); - toolBar->setOrientation(Qt::Vertical); - toolBar->setIconSize(QSize(24,24)); - // since I'm adding the toolBar by hand, because designer - // has no concept of "toolbar" for a non-mainwindow widget (...) - // I need to take the current item that's in the toolbar Position - // and reposition it alongside the grid layout. - QLayoutItem *p = ui.profileInnerLayout->takeAt(0); - ui.profileInnerLayout->addWidget(toolBar, 0, 0); - ui.profileInnerLayout->addItem(p, 0, 1); // and now for some layout hackery // this gets us consistent margins everywhere and a much more balanced look @@ -146,19 +188,52 @@ MainWindow::MainWindow() : QMainWindow(), else layout->setContentsMargins(margins); } - margins = QMargins(0, 5, 5, 5); - ui.profileInnerLayout->setContentsMargins(margins); - ui.profileInnerLayout->setSpacing(0); toolBar->setContentsMargins(zeroMargins); updateManager = new UpdateManager(this); + + undoStack = new QUndoStack(this); + QAction *undoAction = undoStack->createUndoAction(this, tr("&Undo")); + QAction *redoAction = undoStack->createRedoAction(this, tr("&Redo")); + undoAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z)); + redoAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z)); + QList<QAction*>undoRedoActions; + undoRedoActions.append(undoAction); + undoRedoActions.append(redoAction); + ui.menu_Edit->addActions(undoRedoActions); + + ReverseGeoLoockupThread *geoLoockup = ReverseGeoLoockupThread::instance(); + connect(geoLoockup, SIGNAL(started()),information(), SLOT(disableGeoLoockupEdition())); + connect(geoLoockup, SIGNAL(finished()), information(), SLOT(enableGeoLoockupEdition())); } MainWindow::~MainWindow() { + write_hashes(); m_Instance = NULL; } +PlannerDetails *MainWindow::plannerDetails() const { + return qobject_cast<PlannerDetails*>(applicationState["PlanDive"].bottomRight); +} + +PlannerSettingsWidget *MainWindow::divePlannerSettingsWidget() { + return qobject_cast<PlannerSettingsWidget*>(applicationState["PlanDive"].bottomLeft); +} + +LocationInformationWidget *MainWindow::locationInformationWidget() { + return qobject_cast<LocationInformationWidget*>(applicationState["EditDiveSite"].topLeft); +} + +void MainWindow::enableDiveSiteEdit(uint32_t id) { + locationInformationWidget()->setLocationId(displayed_dive.dive_site_uuid); + setApplicationState("EditDiveSite"); +} + +void MainWindow::setDefaultState() { + setApplicationState("Default"); +} + void MainWindow::setLoadedWithFiles(bool f) { filesAsArguments = f; @@ -177,19 +252,16 @@ MainWindow *MainWindow::instance() // this gets called after we download dives from a divecomputer void MainWindow::refreshDisplay(bool doRecreateDiveList) { - showError(get_error_string()); - ui.InfoWidget->reload(); + getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error); + information()->reload(); TankInfoModel::instance()->update(); - ui.globe->reload(); + globe()->reload(); if (doRecreateDiveList) recreateDiveList(); - ui.diveListPane->setCurrentIndex(0); // switch to the dive list -#ifdef NO_MARBLE - ui.globePane->hide(); -#endif - ui.globePane->setCurrentIndex(0); - ui.ListWidget->setEnabled(true); - ui.ListWidget->setFocus(); + + setApplicationState("Default"); + dive_list()->setEnabled(true); + dive_list()->setFocus(); WSInfoModel::instance()->updateInfo(); if (amount_selected == 0) cleanUpEmpty(); @@ -197,7 +269,7 @@ void MainWindow::refreshDisplay(bool doRecreateDiveList) void MainWindow::recreateDiveList() { - ui.ListWidget->reload(DiveTripModel::CURRENT); + dive_list()->reload(DiveTripModel::CURRENT); TagFilterModel::instance()->repopulate(); BuddyFilterModel::instance()->repopulate(); LocationFilterModel::instance()->repopulate(); @@ -208,10 +280,11 @@ void MainWindow::current_dive_changed(int divenr) { if (divenr >= 0) { select_dive(divenr); - ui.globe->centerOnCurrentDive(); + globe()->centerOnCurrentDive(); } - ui.newProfile->plotDive(); - ui.InfoWidget->updateDiveInfo(); + graphics()->plotDive(); + information()->updateDiveInfo(); + locationInformationWidget()->setLocationId(displayed_dive.dive_site_uuid); } void MainWindow::on_actionNew_triggered() @@ -252,20 +325,49 @@ void MainWindow::on_actionSaveAs_triggered() file_save_as(); } +void learnImageDirs(QStringList dirnames) +{ + QList<QFuture<void> > futures; + foreach (QString dir, dirnames) { + futures << QtConcurrent::run(learnImages, QDir(dir), 10, false); + } + DivePictureModel::instance()->updateDivePicturesWhenDone(futures); +} + +void MainWindow::on_actionHash_images_triggered() +{ + QFuture<void> future; + QFileDialog dialog(this, tr("Traverse image directories"), lastUsedDir(), filter()); + dialog.setFileMode(QFileDialog::Directory); + dialog.setViewMode(QFileDialog::Detail); + dialog.setLabelText(QFileDialog::Accept, tr("Scan")); + dialog.setLabelText(QFileDialog::Reject, tr("Cancel")); + QStringList dirnames; + if (dialog.exec()) + dirnames = dialog.selectedFiles(); + if (dirnames.isEmpty()) + return; + future = QtConcurrent::run(learnImageDirs,dirnames); + MainWindow::instance()->getNotificationWidget()->showNotification(tr("Scanning images...(this can take a while)"), KMessageWidget::Information); + MainWindow::instance()->getNotificationWidget()->setFuture(future); + +} + ProfileWidget2 *MainWindow::graphics() const { - return ui.newProfile; + return qobject_cast<ProfileWidget2*>(applicationState["Default"].topRight->layout()->itemAt(1)->widget()); } void MainWindow::cleanUpEmpty() { - ui.InfoWidget->clearStats(); - ui.InfoWidget->clearInfo(); - ui.InfoWidget->clearEquipment(); - ui.InfoWidget->updateDiveInfo(true); - ui.newProfile->setEmptyState(); - ui.ListWidget->reload(DiveTripModel::TREE); - ui.globe->reload(); + information()->clearStats(); + information()->clearInfo(); + information()->clearEquipment(); + information()->updateDiveInfo(true); + graphics()->setEmptyState(); + dive_list()->reload(DiveTripModel::TREE); + locationInformationWidget()->setLocationId(0); + globe()->reload(); if (!existing_filename) setTitle(MWTF_DEFAULT); disableShortcuts(); @@ -274,7 +376,8 @@ void MainWindow::cleanUpEmpty() bool MainWindow::okToClose(QString message) { if (DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING || - ui.InfoWidget->isEditing()) { + information()->isEditing() || + currentApplicationState == "EditDiveSite") { QMessageBox::warning(this, tr("Warning"), message); return false; } @@ -286,11 +389,13 @@ bool MainWindow::okToClose(QString message) void MainWindow::closeCurrentFile() { - ui.newProfile->setEmptyState(); + graphics()->setEmptyState(); /* free the dives and trips */ clear_git_id(); while (dive_table.nr) delete_single_dive(0); + while (dive_site_table.nr) + delete_dive_site(get_dive_site(0)->uuid); free((void *)existing_filename); existing_filename = NULL; @@ -362,8 +467,8 @@ void MainWindow::enableShortcuts() void MainWindow::showProfile() { enableShortcuts(); - ui.newProfile->setProfileState(); - ui.infoPane->setCurrentIndex(MAINTAB); + graphics()->setProfileState(); + setApplicationState("Default"); } void MainWindow::on_actionPreferences_triggered() @@ -373,9 +478,9 @@ void MainWindow::on_actionPreferences_triggered() void MainWindow::on_actionQuit_triggered() { - if (ui.InfoWidget->isEditing()) { - ui.InfoWidget->rejectChanges(); - if (ui.InfoWidget->isEditing()) + if (information()->isEditing()) { + information()->rejectChanges(); + if (information()->isEditing()) // didn't discard the edits return; } @@ -421,7 +526,7 @@ void MainWindow::on_actionEditDeviceNames_triggered() bool MainWindow::plannerStateClean() { if (DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING || - ui.InfoWidget->isEditing()) { + information()->isEditing()) { QMessageBox::warning(this, tr("Warning"), tr("Please save or cancel the current dive edit before trying to add a dive.")); return false; } @@ -433,16 +538,16 @@ void MainWindow::planCanceled() // while planning we might have modified the displayed_dive // let's refresh what's shown on the profile showProfile(); - ui.newProfile->replot(); + graphics()->replot(); refreshDisplay(false); - ui.newProfile->plotDive(get_dive(selected_dive)); + graphics()->plotDive(get_dive(selected_dive)); DivePictureModel::instance()->updateDivePictures(); } void MainWindow::planCreated() { // get the new dive selected and assign a number if reasonable - ui.newProfile->setProfileState(); + graphics()->setProfileState(); if (displayed_dive.id == 0) { // we might have added a new dive (so displayed_dive was cleared out by clone_dive() dive_list()->unselectDives(); @@ -451,20 +556,20 @@ void MainWindow::planCreated() set_dive_nr_for_current_dive(); } // make sure our UI is in a consistent state - ui.InfoWidget->updateDiveInfo(); + information()->updateDiveInfo(); showProfile(); refreshDisplay(); } void MainWindow::setPlanNotes(const char *notes) { - ui.divePlanOutput->setHtml(notes); + plannerDetails()->divePlanOutput()->setHtml(notes); } void MainWindow::printPlan() { #ifndef NO_PRINTING - QString diveplan = ui.divePlanOutput->toHtml(); + QString diveplan = plannerDetails()->divePlanOutput()->toHtml(); QString withDisclaimer = QString("<img height=50 src=\":subsurface-icon\"> ") + diveplan + QString(disclaimer); QPrinter printer; @@ -473,9 +578,9 @@ void MainWindow::printPlan() if (dialog->exec() != QDialog::Accepted) return; - ui.divePlanOutput->setHtml(withDisclaimer); - ui.divePlanOutput->print(&printer); - ui.divePlanOutput->setHtml(diveplan); + plannerDetails()->divePlanOutput()->setHtml(withDisclaimer); + plannerDetails()->divePlanOutput()->print(&printer); + plannerDetails()->divePlanOutput()->setHtml(diveplan); #endif } @@ -489,31 +594,28 @@ void MainWindow::setupForAddAndPlan(const char *model) // setup the dive cylinders DivePlannerPointsModel::instance()->clear(); DivePlannerPointsModel::instance()->setupCylinders(); + locationInformationWidget()->setLocationId(0); } void MainWindow::on_actionReplanDive_triggered() { - if (!plannerStateClean()) - return; - if (!current_dive || !current_dive->dc.model || strcmp(current_dive->dc.model, "planned dive")) { - qDebug() << "trying to replan a dive that's not a planned dive:" << current_dive->dc.model; + if (!plannerStateClean() || !current_dive || !current_dive->dc.model) return; + else if (strcmp(current_dive->dc.model, "planned dive")) { + if (QMessageBox::warning(this, tr("Warning"), tr("trying to replan a dive that's not a planned dive."), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) + return; } // put us in PLAN mode DivePlannerPointsModel::instance()->clear(); DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN); - ui.newProfile->setPlanState(); - ui.newProfile->clearHandlers(); - ui.infoPane->setCurrentIndex(PLANNERWIDGET); - ui.divePlannerWidget->setReplanButton(true); + graphics()->setPlanState(); + graphics()->clearHandlers(); + setApplicationState("PlanDive"); + divePlannerWidget()->setReplanButton(true); DivePlannerPointsModel::instance()->loadFromDive(current_dive); reset_cylinders(&displayed_dive, true); - ui.diveListPane->setCurrentIndex(1); // switch to the plan output - ui.globePane->setCurrentIndex(1); -#ifdef NO_MARBLE - ui.globePane->show(); -#endif } void MainWindow::on_actionDivePlanner_triggered() @@ -523,22 +625,20 @@ void MainWindow::on_actionDivePlanner_triggered() // put us in PLAN mode DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN); + setApplicationState("PlanDive"); - ui.newProfile->setPlanState(); - ui.infoPane->setCurrentIndex(PLANNERWIDGET); + graphics()->setPlanState(); - // create a simple starting dive, using the first gas from the just copied cylidners + // create a simple starting dive, using the first gas from the just copied cylinders setupForAddAndPlan("planned dive"); // don't translate, stored in XML file DivePlannerPointsModel::instance()->setupStartTime(); DivePlannerPointsModel::instance()->createSimpleDive(); DivePictureModel::instance()->updateDivePictures(); - ui.divePlannerWidget->setReplanButton(false); + divePlannerWidget()->setReplanButton(false); +} - ui.diveListPane->setCurrentIndex(1); // switch to the plan output - ui.globePane->setCurrentIndex(1); -#ifdef NO_MARBLE - ui.globePane->show(); -#endif +DivePlannerWidget* MainWindow::divePlannerWidget() { + return qobject_cast<DivePlannerWidget*>(applicationState["PlanDive"].topLeft); } void MainWindow::on_actionAddDive_triggered() @@ -551,23 +651,45 @@ void MainWindow::on_actionAddDive_triggered() dive_list()->clearSelection(); } + setApplicationState("AddDive"); DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD); // setup things so we can later create our starting dive setupForAddAndPlan("manually added dive"); // don't translate, stored in the XML file // now show the mostly empty main tab - ui.InfoWidget->updateDiveInfo(); + information()->updateDiveInfo(); // show main tab - ui.InfoWidget->setCurrentIndex(0); + information()->setCurrentIndex(0); - ui.InfoWidget->addDiveStarted(); - ui.infoPane->setCurrentIndex(MAINTAB); + information()->addDiveStarted(); - ui.newProfile->setAddState(); + graphics()->setAddState(); DivePlannerPointsModel::instance()->createSimpleDive(); - ui.newProfile->plotDive(); + graphics()->plotDive(); +} + +void MainWindow::on_actionEditDive_triggered() +{ + if (information()->isEditing() || DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING) { + QMessageBox::warning(this, tr("Warning"), tr("Please, first finish the current edition before trying to do another.")); + return; + } + + const bool isTripEdit = dive_list()->selectedTrips().count() >= 1; + if (!current_dive || isTripEdit || strcmp(current_dive->dc.model, "manually added dive")) { + QMessageBox::warning(this, tr("Warning"), tr("Trying to edit a dive that's not a manually added dive.")); + return; + } + + DivePlannerPointsModel::instance()->clear(); + disableShortcuts(); + DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD); + graphics()->setAddState(); + setApplicationState("EditDive"); + DivePlannerPointsModel::instance()->loadFromDive(current_dive); + information()->enableEdition(MainTab::MANUALLY_ADDED_DIVE); } void MainWindow::on_actionRenumber_triggered() @@ -612,16 +734,16 @@ void MainWindow::on_actionYearlyStatistics_triggered() #define TOGGLE_COLLAPSABLE( X ) \ ui.mainSplitter->setCollapsible(0, X); \ ui.mainSplitter->setCollapsible(1, X); \ - ui.infoProfileSplitter->setCollapsible(0, X); \ - ui.infoProfileSplitter->setCollapsible(1, X); \ - ui.listGlobeSplitter->setCollapsible(0, X); \ - ui.listGlobeSplitter->setCollapsible(1, X); + ui.topSplitter->setCollapsible(0, X); \ + ui.topSplitter->setCollapsible(1, X); \ + ui.bottomSplitter->setCollapsible(0, X); \ + ui.bottomSplitter->setCollapsible(1, X); void MainWindow::on_actionViewList_triggered() { TOGGLE_COLLAPSABLE( true ); beginChangeState(LIST_MAXIMIZED); - ui.listGlobeSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED); + ui.topSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED); ui.mainSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED); } @@ -629,7 +751,7 @@ void MainWindow::on_actionViewProfile_triggered() { TOGGLE_COLLAPSABLE( true ); beginChangeState(PROFILE_MAXIMIZED); - ui.infoProfileSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED); + ui.topSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED); ui.mainSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED); } @@ -637,7 +759,7 @@ void MainWindow::on_actionViewInfo_triggered() { TOGGLE_COLLAPSABLE( true ); beginChangeState(INFO_MAXIMIZED); - ui.infoProfileSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED); + ui.topSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED); ui.mainSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED); } @@ -646,7 +768,7 @@ void MainWindow::on_actionViewGlobe_triggered() TOGGLE_COLLAPSABLE( true ); beginChangeState(GLOBE_MAXIMIZED); ui.mainSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED); - ui.listGlobeSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED); + ui.bottomSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED); } #undef BEHAVIOR @@ -677,26 +799,26 @@ void MainWindow::on_actionViewAll_triggered() settings.beginGroup("MainWindow"); if (settings.value("mainSplitter").isValid()) { ui.mainSplitter->restoreState(settings.value("mainSplitter").toByteArray()); - ui.infoProfileSplitter->restoreState(settings.value("infoProfileSplitter").toByteArray()); - ui.listGlobeSplitter->restoreState(settings.value("listGlobeSplitter").toByteArray()); + ui.topSplitter->restoreState(settings.value("topSplitter").toByteArray()); + ui.bottomSplitter->restoreState(settings.value("bottomSplitter").toByteArray()); if (ui.mainSplitter->sizes().first() == 0 || ui.mainSplitter->sizes().last() == 0) ui.mainSplitter->setSizes(mainSizes); - if (ui.infoProfileSplitter->sizes().first() == 0 || ui.infoProfileSplitter->sizes().last() == 0) - ui.infoProfileSplitter->setSizes(infoProfileSizes); - if (ui.listGlobeSplitter->sizes().first() == 0 || ui.listGlobeSplitter->sizes().last() == 0) - ui.listGlobeSplitter->setSizes(listGlobeSizes); + if (ui.topSplitter->sizes().first() == 0 || ui.topSplitter->sizes().last() == 0) + ui.topSplitter->setSizes(infoProfileSizes); + if (ui.bottomSplitter->sizes().first() == 0 || ui.bottomSplitter->sizes().last() == 0) + ui.bottomSplitter->setSizes(listGlobeSizes); } else { ui.mainSplitter->setSizes(mainSizes); - ui.infoProfileSplitter->setSizes(infoProfileSizes); - ui.listGlobeSplitter->setSizes(listGlobeSizes); + ui.topSplitter->setSizes(infoProfileSizes); + ui.bottomSplitter->setSizes(listGlobeSizes); } ui.mainSplitter->setCollapsible(0, false); ui.mainSplitter->setCollapsible(1, false); - ui.infoProfileSplitter->setCollapsible(0, false); - ui.infoProfileSplitter->setCollapsible(1, false); - ui.listGlobeSplitter->setCollapsible(0,false); - ui.listGlobeSplitter->setCollapsible(1,false); + ui.topSplitter->setCollapsible(0, false); + ui.topSplitter->setCollapsible(1, false); + ui.bottomSplitter->setCollapsible(0,false); + ui.bottomSplitter->setCollapsible(1,false); } #undef TOGGLE_COLLAPSABLE @@ -714,24 +836,24 @@ void MainWindow::saveSplitterSizes() QSettings settings; settings.beginGroup("MainWindow"); settings.setValue("mainSplitter", ui.mainSplitter->saveState()); - settings.setValue("infoProfileSplitter", ui.infoProfileSplitter->saveState()); - settings.setValue("listGlobeSplitter", ui.listGlobeSplitter->saveState()); + settings.setValue("topSplitter", ui.topSplitter->saveState()); + settings.setValue("bottomSplitter", ui.bottomSplitter->saveState()); } void MainWindow::on_actionPreviousDC_triggered() { unsigned nrdc = number_of_computers(current_dive); dc_number = (dc_number + nrdc - 1) % nrdc; - ui.newProfile->plotDive(); - ui.InfoWidget->updateDiveInfo(); + graphics()->plotDive(); + information()->updateDiveInfo(); } void MainWindow::on_actionNextDC_triggered() { unsigned nrdc = number_of_computers(current_dive); dc_number = (dc_number + 1) % nrdc; - ui.newProfile->plotDive(); - ui.InfoWidget->updateDiveInfo(); + graphics()->plotDive(); + information()->updateDiveInfo(); } void MainWindow::on_actionFullScreen_triggered(bool checked) @@ -802,6 +924,7 @@ QString MainWindow::filter() f += "UDDF (*.uddf *.UDDF);;"; f += "XML (*.xml *.XML)"; f += "Divesoft (*.dlf *.DLF)"; + f += "Datatrak/WLog Files (*.log *.LOG)"; return f; } @@ -937,7 +1060,7 @@ void MainWindow::checkSurvey(QSettings *s) s->setValue("FirstUse42", value); } // wait a week for production versions, but not at all for non-tagged builds - QString ver(VERSION_STRING); + QString ver(subsurface_version()); int waitTime = 7; QDate firstUse42 = s->value("FirstUse42").toDate(); if (run_survey || (firstUse42.daysTo(QDate().currentDate()) > waitTime && !s->contains("SurveyDone"))) { @@ -965,7 +1088,7 @@ void MainWindow::writeSettings() void MainWindow::closeEvent(QCloseEvent *event) { if (DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING || - ui.InfoWidget->isEditing()) { + information()->isEditing()) { on_actionQuit_triggered(); event->ignore(); return; @@ -994,17 +1117,17 @@ void MainWindow::closeEvent(QCloseEvent *event) DiveListView *MainWindow::dive_list() { - return ui.ListWidget; + return qobject_cast<DiveListView*>(applicationState["Default"].bottomLeft); } GlobeGPS *MainWindow::globe() { - return ui.globe; + return qobject_cast<GlobeGPS*>(applicationState["Default"].bottomRight); } MainTab *MainWindow::information() { - return ui.InfoWidget; + return qobject_cast<MainTab*>(applicationState["Default"].topLeft); } void MainWindow::loadRecentFiles(QSettings *s) @@ -1174,20 +1297,31 @@ int MainWindow::file_save_as(void) { QString filename; const char *default_filename = existing_filename; - filename = QFileDialog::getSaveFileName(this, tr("Save file as"), default_filename, - tr("Subsurface XML files (*.ssrf *.xml *.XML)")); + + // create a file dialog that allows us to save to a new file + QFileDialog selection_dialog(this, tr("Save file as"), default_filename, + tr("Subsurface XML files (*.ssrf *.xml *.XML)")); + selection_dialog.setAcceptMode(QFileDialog::AcceptSave); + selection_dialog.setFileMode(QFileDialog::AnyFile); + + /* if the exit/cancel button is pressed return */ + if (!selection_dialog.exec()) + return 0; + + /* get the first selected file */ + filename = selection_dialog.selectedFiles().at(0); if (filename.isNull() || filename.isEmpty()) return report_error("No filename to save into"); - if (ui.InfoWidget->isEditing()) - ui.InfoWidget->acceptChanges(); + if (information()->isEditing()) + information()->acceptChanges(); if (save_dives(filename.toUtf8().data())) { - showError(get_error_string()); + getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error); return -1; } - showError(get_error_string()); + getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error); set_filename(filename.toUtf8().data(), true); setTitle(MWTF_FILENAME); mark_divelist_changed(false); @@ -1202,8 +1336,8 @@ int MainWindow::file_save(void) if (!existing_filename) return file_save_as(); - if (ui.InfoWidget->isEditing()) - ui.InfoWidget->acceptChanges(); + if (information()->isEditing()) + information()->acceptChanges(); current_default = prefs.default_filename; if (strcmp(existing_filename, current_default) == 0) { @@ -1214,23 +1348,18 @@ int MainWindow::file_save(void) current_def_dir.mkpath(current_def_dir.absolutePath()); } if (save_dives(existing_filename)) { - showError(get_error_string()); + getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error); return -1; } - showError(get_error_string()); + getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error); mark_divelist_changed(false); addRecentFile(QStringList() << QString(existing_filename)); return 0; } -void MainWindow::showError(QString message) +NotificationWidget *MainWindow::getNotificationWidget() { - if (message.isEmpty()) - return; - ui.mainErrorMessage->setText(message); - ui.mainErrorMessage->setCloseButtonVisible(true); - ui.mainErrorMessage->setMessageType(KMessageWidget::Error); - ui.mainErrorMessage->animatedShow(); + return ui.mainErrorMessage; } void MainWindow::setTitle(enum MainWindowTitleFormat format) @@ -1284,6 +1413,18 @@ void MainWindow::importTxtFiles(const QStringList fileNames) refreshDisplay(); } +void MainWindow::showV2Dialog() +{ + // here we need to ask the user if / how they want to do the reverse geo coding + // for now this is just a warning that things could take a long time + QMessageBox d(QMessageBox::Information, + tr("Welcom to Subsurface %1").arg(subsurface_version()), + tr("Importing data files from earlier versions of Subsurface can take a significant amount of time"), + QMessageBox::Ok, + this); + d.exec(); +} + void MainWindow::loadFiles(const QStringList fileNames) { if (fileNames.isEmpty()) @@ -1301,14 +1442,26 @@ void MainWindow::loadFiles(const QStringList fileNames) set_filename(fileNamePtr.data(), true); setTitle(MWTF_FILENAME); } else { + if (!v2_question_shown && abort_read_of_old_file) { + v2_question_shown = true; + abort_read_of_old_file = false; + showV2Dialog(); + getNotificationWidget()->showNotification(tr("Please Wait, Importing your files..."), KMessageWidget::Information); + i--; // so we re-try this file + continue; + } failedParses.append(fileNames.at(i)); } } - + getNotificationWidget()->hideNotification(); process_dives(false, false); addRecentFile(fileNames); removeRecentFile(failedParses); + // searches for geo lookup information in a thread so it doesn`t + // freezes the ui. + ReverseGeoLoockupThread::instance()->start(); + refreshDisplay(); ui.actionAutoGroup->setChecked(autogroup); } @@ -1316,18 +1469,21 @@ void MainWindow::loadFiles(const QStringList fileNames) void MainWindow::on_actionImportDiveLog_triggered() { QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open dive log file"), lastUsedDir(), - tr("Dive log files (*.ssrf *.can *.csv *.db *.dld *.jlb *.lvd *.sde *.udcf *.uddf *.xml *.txt *.dlf *.apd);;" - "Cochran files (*.can);;" - "CSV files (*.csv);;" - "DiveLog.de files (*.dld);;" - "JDiveLog files (*.jlb);;" - "Liquivision files (*.lvd);;" - "MkVI files (*.txt);;" - "Suunto files (*.sde *.db);;" - "Divesoft files (*.dlf);;" - "UDDF/UDCF files (*.uddf *.udcf);;" - "XML files (*.xml);;" - "APD log viewer (*.apd);;" + tr("Dive log files (*.ssrf *.can *.csv *.db *.dld *.jlb *.lvd *.sde *.udcf *.uddf *.xml *.txt *.dlf *.apd" + "*.SSRF *.CAN *.CSV *.DB *.DLD *.JLB *.LVD *.SDE *.UDCF *.UDDF *.xml *.TXT *.DLF *.APD);;" + "Cochran files (*.can *.CAN);;" + "CSV files (*.csv *.CSV);;" + "DiveLog.de files (*.dld *.DLD);;" + "JDiveLog files (*.jlb *.JLB);;" + "Liquivision files (*.lvd *.LVD);;" + "MkVI files (*.txt *.TXT);;" + "Suunto files (*.sde *.db *.SDE *.DB);;" + "Divesoft files (*.dlf *.DLF);;" + "UDDF/UDCF files (*.uddf *.udcf *.UDDF *.UDCF);;" + "XML files (*.xml *.XML);;" + "APD log viewer (*.apd *.APD);;" + "Datatrak/WLog Files (*.log *.LOG);;" + "OSTCtools Files (*.dive *.DIVE);;" "All files (*)")); if (fileNames.isEmpty()) @@ -1368,20 +1524,16 @@ void MainWindow::editCurrentDive() if (defaultDC == "manually added dive") { disableShortcuts(); DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD); - ui.newProfile->setAddState(); - ui.infoPane->setCurrentIndex(MAINTAB); + graphics()->setAddState(); + setApplicationState("EditDive"); DivePlannerPointsModel::instance()->loadFromDive(d); - ui.InfoWidget->enableEdition(MainTab::MANUALLY_ADDED_DIVE); + information()->enableEdition(MainTab::MANUALLY_ADDED_DIVE); } else if (defaultDC == "planned dive") { disableShortcuts(); DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN); - //TODO: I BROKE THIS BY COMMENTING THE LINE BELOW - // and I'm sleepy now, so I think I should not try to fix right away. - // we don't setCurrentIndex anymore, we ->setPlanState() or ->setAddState() on the ProfileView. - //ui.stackedWidget->setCurrentIndex(PLANNERPROFILE); // Planner. - ui.infoPane->setCurrentIndex(PLANNERWIDGET); + setApplicationState("EditPlannedDive"); DivePlannerPointsModel::instance()->loadFromDive(d); - ui.InfoWidget->enableEdition(MainTab::MANUALLY_ADDED_DIVE); + information()->enableEdition(MainTab::MANUALLY_ADDED_DIVE); } } @@ -1456,7 +1608,7 @@ void MainWindow::on_paste_triggered() { // take the data in our copyPasteDive and apply it to selected dives selective_copy_dive(©PasteDive, &displayed_dive, what, false); - ui.InfoWidget->showAndTriggerEditSelective(what); + information()->showAndTriggerEditSelective(what); } void MainWindow::on_actionFilterTags_triggered() @@ -1466,3 +1618,43 @@ void MainWindow::on_actionFilterTags_triggered() else ui.multiFilter->setVisible(true); } + +void MainWindow::registerApplicationState(const QByteArray& state, QWidget *topLeft, QWidget *topRight, QWidget *bottomLeft, QWidget *bottomRight) +{ + applicationState[state] = WidgetForQuadrant(topLeft, topRight, bottomLeft, bottomRight); + if (ui.topLeft->indexOf(topLeft) == -1 && topLeft) { + ui.topLeft->addWidget(topLeft); + } + if (ui.topRight->indexOf(topRight) == -1 && topRight) { + ui.topRight->addWidget(topRight); + } + if (ui.bottomLeft->indexOf(bottomLeft) == -1 && bottomLeft) { + ui.bottomLeft->addWidget(bottomLeft); + } + if(ui.bottomRight->indexOf(bottomRight) == -1 && bottomRight) { + ui.bottomRight->addWidget(bottomRight); + } +} + +void MainWindow::setApplicationState(const QByteArray& state) { + if (!applicationState.keys().contains(state)) + return; + + if (currentApplicationState == state) + return; + + currentApplicationState = state; +#define SET_CURRENT_INDEX( X ) \ + if (applicationState[state].X) { \ + ui.X->setCurrentWidget( applicationState[state].X); \ + ui.X->show(); \ + } else { \ + ui.X->hide(); \ + } + + SET_CURRENT_INDEX( topLeft ) + SET_CURRENT_INDEX( topRight ) + SET_CURRENT_INDEX( bottomLeft ) + SET_CURRENT_INDEX( bottomRight ) +#undef SET_CURRENT_INDEX +} |