diff options
-rw-r--r-- | device.c | 28 | ||||
-rw-r--r-- | device.h | 2 | ||||
-rw-r--r-- | divelist.c | 9 | ||||
-rw-r--r-- | parse-xml.c | 1 | ||||
-rw-r--r-- | qt-ui/divelistview.cpp | 60 | ||||
-rw-r--r-- | qt-ui/divelistview.h | 4 | ||||
-rw-r--r-- | qt-ui/downloadfromdivecomputer.cpp | 1 | ||||
-rw-r--r-- | qt-ui/downloadfromdivecomputer.ui | 2 | ||||
-rw-r--r-- | qt-ui/globe.cpp | 7 | ||||
-rw-r--r-- | qt-ui/maintab.cpp | 243 | ||||
-rw-r--r-- | qt-ui/maintab.h | 5 | ||||
-rw-r--r-- | qt-ui/maintab.ui | 67 | ||||
-rw-r--r-- | qt-ui/mainwindow.cpp | 79 | ||||
-rw-r--r-- | qt-ui/mainwindow.h | 6 | ||||
-rw-r--r-- | qt-ui/mainwindow.ui | 39 | ||||
-rw-r--r-- | qt-ui/modeldelegates.cpp | 62 | ||||
-rw-r--r-- | qt-ui/modeldelegates.h | 17 | ||||
-rw-r--r-- | qt-ui/models.cpp | 171 | ||||
-rw-r--r-- | qt-ui/models.h | 7 | ||||
-rw-r--r-- | qt-ui/profilegraphics.cpp | 268 | ||||
-rw-r--r-- | qt-ui/profilegraphics.h | 40 | ||||
-rw-r--r-- | qt-ui/subsurfacewebservices.cpp | 8 | ||||
-rw-r--r-- | statistics.c | 6 | ||||
-rw-r--r-- | xslt/uddf.xslt | 209 |
24 files changed, 959 insertions, 382 deletions
@@ -114,3 +114,31 @@ struct device_info *remove_device_info(const char *model, uint32_t deviceid) } return entry; } + +struct divecomputer* fake_dc(struct divecomputer* dc) +{ + static struct sample fake[4]; + static struct divecomputer fakedc; + static bool initialized = 0; + if (!initialized){ + fakedc = (*dc); + fakedc.sample = fake; + fakedc.samples = 4; + + /* The dive has no samples, so create a few fake ones. This assumes an + ascent/descent rate of 9 m/min, which is just below the limit for FAST. */ + int duration = dc->duration.seconds; + int maxdepth = dc->maxdepth.mm; + int asc_desc_time = dc->maxdepth.mm*60/9000; + if (asc_desc_time * 2 >= duration) + asc_desc_time = duration / 2; + fake[1].time.seconds = asc_desc_time; + fake[1].depth.mm = maxdepth; + fake[2].time.seconds = duration - asc_desc_time; + fake[2].depth.mm = maxdepth; + fake[3].time.seconds = duration * 1.00; + fakedc.events = dc->events; + } + return &fakedc; +} + @@ -2,6 +2,7 @@ #define DEVICE_INFO_H #ifdef __cplusplus +#include "dive.h" extern "C" { #endif @@ -20,6 +21,7 @@ extern struct device_info *get_different_device_info(const char *model, uint32_t extern struct device_info *create_device_info(const char *model, uint32_t deviceid); extern struct device_info *remove_device_info(const char *model, uint32_t deviceid); extern struct device_info *head_of_device_info_list(void); +extern struct divecomputer *fake_dc(struct divecomputer* dc); extern void remove_dive_computer(const char *model, uint32_t deviceid); #ifdef __cplusplus diff --git a/divelist.c b/divelist.c index 7abad667b..606c30e01 100644 --- a/divelist.c +++ b/divelist.c @@ -607,12 +607,11 @@ char *get_trip_date_string(timestamp_t when, int nr) struct tm tm; utc_mkdate(when, &tm); snprintf(buffer, MAX_DATE_STRING, - /*++GETTEXT 60 char buffer weekday, monthname, day of month, year, nr dives */ - ngettext("Trip %1$s, %2$s %3$d, %4$d (%5$d dive)", - "Trip %1$s, %2$s %3$d, %4$d (%5$d dives)", nr), - weekday(tm.tm_wday), + /*++GETTEXT 60 char buffer monthname, year, nr dives */ + ngettext("%1$s %2$d (%3$d dive)", + "%1$s %2$d (%3$d dives)", nr), monthname(tm.tm_mon), - tm.tm_mday, tm.tm_year + 1900, + tm.tm_year + 1900, nr); } return buffer; diff --git a/parse-xml.c b/parse-xml.c index 58a9019c9..ba952957f 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -1913,6 +1913,7 @@ static struct xslt_files { { "dives", "MacDive.xslt" }, { "DIVELOGSDATA", "divelogs.xslt" }, { "uddf", "uddf.xslt" }, + { "UDDF", "uddf.xslt" }, { "profile", "udcf.xslt" }, { "Divinglog", "DivingLog.xslt" }, { NULL, } diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index cbd62408b..752e12242 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -28,6 +28,8 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelec model->setSortRole(TreeItemDT::SORT_ROLE); model->setFilterKeyColumn(-1); // filter all columns setModel(model); + connect(model, SIGNAL(layoutChanged()), this, SLOT(fixMessyQtModelBehaviour())); + setSortingEnabled(false); setContextMenuPolicy(Qt::DefaultContextMenu); header()->setContextMenuPolicy(Qt::ActionsContextMenu); @@ -40,6 +42,17 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelec searchBox->hide(); connect(showSearchBox, SIGNAL(triggered(bool)), this, SLOT(showSearchEdit())); connect(searchBox, SIGNAL(textChanged(QString)), model, SLOT(setFilterFixedString(QString))); + selectedTrips.clear(); +} + +void DiveListView::fixMessyQtModelBehaviour() +{ + QAbstractItemModel *m = model(); + for(int i = 0; i < model()->rowCount(); i++){ + if (m->rowCount( m->index(i, 0) ) != 0){ + setFirstColumnSpanned(i, QModelIndex(), true); + } + } } void DiveListView::unselectDives() @@ -47,16 +60,19 @@ void DiveListView::unselectDives() selectionModel()->clearSelection(); } -void DiveListView::selectDive(struct dive *dive, bool scrollto) +void DiveListView::selectDive(struct dive *dive, bool scrollto, bool toggle) { QSortFilterProxyModel *m = qobject_cast<QSortFilterProxyModel*>(model()); QModelIndexList match = m->match(m->index(0,0), TreeItemDT::NR, dive->number, 1, Qt::MatchRecursive); + QItemSelectionModel::SelectionFlags flags; QModelIndex idx = match.first(); QModelIndex parent = idx.parent(); if (parent.isValid()) expand(parent); - selectionModel()->select( idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); + flags = toggle ? QItemSelectionModel::Toggle : QItemSelectionModel::Select; + flags |= QItemSelectionModel::Rows; + selectionModel()->select( idx, flags); if (scrollto) scrollTo(idx, PositionAtCenter); } @@ -183,6 +199,12 @@ void DiveListView::reloadHeaderActions() setColumnHidden(i, !shown); } s.endGroup(); + } else { + // Skip first QAction item ( static text Visible ) + for(int i = 0; i < model()->columnCount(); i++) { + QString title = QString("%1").arg(model()->headerData(i, Qt::Horizontal).toString()); + header()->actions()[i+1]->setText( title ); + } } } @@ -224,48 +246,51 @@ void DiveListView::selectionChanged(const QItemSelection& selected, const QItemS disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection))); disconnect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(currentChanged(QModelIndex,QModelIndex))); - Q_FOREACH(const QModelIndex& index, newSelected.indexes()) { + Q_FOREACH(const QModelIndex& index, newDeselected.indexes()) { if (index.column() != 0) continue; - const QAbstractItemModel *model = index.model(); struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value<void*>(); if (!dive) { // it's a trip! if (model->rowCount(index)) { - QItemSelection selection; struct dive *child = (struct dive*) model->data(index.child(0,0), TreeItemDT::DIVE_ROLE).value<void*>(); + if (child && child->divetrip) + selectedTrips.remove(child->divetrip); while (child) { - select_dive(get_index_for_dive(child)); + deselect_dive(get_index_for_dive(child)); child = child->next; } - selection.select(index.child(0,0), index.child(model->rowCount(index) -1 , 0)); - selectionModel()->select(selection, QItemSelectionModel::Select | QItemSelectionModel::Rows); - selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::NoUpdate); - if (!isExpanded(index)) - expand(index); } } else { - select_dive(get_index_for_dive(dive)); + deselect_dive(get_index_for_dive(dive)); } } - Q_FOREACH(const QModelIndex& index, newDeselected.indexes()) { + Q_FOREACH(const QModelIndex& index, newSelected.indexes()) { if (index.column() != 0) continue; + const QAbstractItemModel *model = index.model(); struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value<void*>(); if (!dive) { // it's a trip! if (model->rowCount(index)) { + QItemSelection selection; struct dive *child = (struct dive*) model->data(index.child(0,0), TreeItemDT::DIVE_ROLE).value<void*>(); + if (child && child->divetrip) + selectedTrips.insert(child->divetrip); while (child) { - deselect_dive(get_index_for_dive(child)); + select_dive(get_index_for_dive(child)); child = child->next; } + selection.select(index.child(0,0), index.child(model->rowCount(index) -1 , 0)); + selectionModel()->select(selection, QItemSelectionModel::Select | QItemSelectionModel::Rows); + selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::NoUpdate); + if (!isExpanded(index)) + expand(index); } } else { - deselect_dive(get_index_for_dive(dive)); + select_dive(get_index_for_dive(dive)); } } - QTreeView::selectionChanged(selectionModel()->selection(), newDeselected); connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection))); connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(currentChanged(QModelIndex,QModelIndex))); @@ -320,7 +345,8 @@ void DiveListView::contextMenuEvent(QContextMenuEvent *event) popup.addAction(tr("remove dive from trip"), this, SLOT(removeFromTrip())); } } - popup.addAction(tr("delete dive"), this, SLOT(deleteDive())); + if (d) + popup.addAction(tr("delete dive"), this, SLOT(deleteDive())); // "collapse all" really closes all trips, // "collapse" keeps the trip with the selected dive open QAction * actionTaken = popup.exec(event->globalPos()); diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h index a9b986f97..88f52ba7d 100644 --- a/qt-ui/divelistview.h +++ b/qt-ui/divelistview.h @@ -27,8 +27,9 @@ public: void reload(DiveTripModel::Layout layout, bool forceSort = true); bool eventFilter(QObject* , QEvent* ); void unselectDives(); - void selectDive(struct dive *, bool scrollto = false); + void selectDive(struct dive *, bool scrollto = false, bool toggle = false); void contextMenuEvent(QContextMenuEvent *event); + QSet<dive_trip_t *> selectedTrips; public slots: void toggleColumnVisibilityByIndex(); @@ -38,6 +39,7 @@ public slots: void removeFromTrip(); void deleteDive(); void testSlot(); + void fixMessyQtModelBehaviour(); Q_SIGNALS: void currentDiveChanged(int divenr); diff --git a/qt-ui/downloadfromdivecomputer.cpp b/qt-ui/downloadfromdivecomputer.cpp index b0bdea739..37ca2775b 100644 --- a/qt-ui/downloadfromdivecomputer.cpp +++ b/qt-ui/downloadfromdivecomputer.cpp @@ -157,6 +157,7 @@ void DownloadFromDCWidget::on_ok_clicked() data.product = strdup(ui->product->currentText().toUtf8().data()); data.descriptor = descriptorLookup[ui->vendor->currentText() + ui->product->currentText()]; data.force_download = ui->forceDownload->isChecked(); + data.deviceid = data.diveid = 0; set_default_dive_computer(data.vendor, data.product); set_default_dive_computer_device(data.devname); diff --git a/qt-ui/downloadfromdivecomputer.ui b/qt-ui/downloadfromdivecomputer.ui index 4b46a6e43..41f9a4751 100644 --- a/qt-ui/downloadfromdivecomputer.ui +++ b/qt-ui/downloadfromdivecomputer.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Form</string> + <string>Download From Dive Computer</string> </property> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> diff --git a/qt-ui/globe.cpp b/qt-ui/globe.cpp index 78283e49e..24806d520 100644 --- a/qt-ui/globe.cpp +++ b/qt-ui/globe.cpp @@ -82,6 +82,7 @@ void GlobeGPS::mouseClicked(qreal lon, qreal lat, GeoDataCoordinates::Unit unit) int idx; struct dive *dive; bool clear = !(QApplication::keyboardModifiers() && Qt::ControlModifier); + bool toggle = !clear; bool first = true; for_each_dive(idx, dive) { long lat_diff, lon_diff; @@ -100,7 +101,7 @@ void GlobeGPS::mouseClicked(qreal lon, qreal lat, GeoDataCoordinates::Unit unit) mainWindow()->dive_list()->unselectDives(); clear = false; } - mainWindow()->dive_list()->selectDive(dive, first); + mainWindow()->dive_list()->selectDive(dive, first, toggle); first = false; } } @@ -129,10 +130,10 @@ void GlobeGPS::reload() place->setCoordinate(dive->longitude.udeg / 1000000.0,dive->latitude.udeg / 1000000.0 , 0, GeoDataCoordinates::Degree); // don't add dive locations twice, unless they are at least 50m apart if (locationMap[QString(dive->location)]) { - GeoDataPoint existingLocation = locationMap[QString(dive->location)]->coordinate(); + GeoDataCoordinates existingLocation = locationMap[QString(dive->location)]->coordinate(); GeoDataLineString segment = GeoDataLineString(); segment.append(existingLocation); - GeoDataPoint newLocation = place->coordinate(); + GeoDataCoordinates newLocation = place->coordinate(); segment.append(newLocation); double dist = segment.length(6371); // the dist is scaled to the radius given - so with 6371km as radius diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index b9b5d0ab7..c6b49da39 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -15,19 +15,26 @@ #include <QLabel> #include <QDebug> +#include <QSet> +#include <QSettings> MainTab::MainTab(QWidget *parent) : QTabWidget(parent), ui(new Ui::MainTab()), weightModel(new WeightModel()), cylindersModel(new CylindersModel()), - currentDive(0) + currentDive(0), + editMode(NONE) { ui->setupUi(this); ui->cylinders->setModel(cylindersModel); ui->weights->setModel(weightModel); ui->diveNotesMessage->hide(); ui->diveNotesMessage->setCloseButtonVisible(false); - +#ifdef __APPLE__ + setDocumentMode(false); +#else + setDocumentMode(true); +#endif // we start out with the fields read-only; once things are // filled from a dive, they are made writeable ui->location->setReadOnly(true); @@ -73,13 +80,17 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent), connect(ui->cylinders, SIGNAL(clicked(QModelIndex)), ui->cylinders->model(), SLOT(remove(QModelIndex))); connect(ui->weights, SIGNAL(clicked(QModelIndex)), ui->weights->model(), SLOT(remove(QModelIndex))); - ui->cylinders->setColumnWidth(CylindersModel::REMOVE, 24); - ui->cylinders->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); + QFontMetrics metrics(defaultModelFont()); + QFontMetrics metrics2(font()); + ui->cylinders->horizontalHeader()->setResizeMode(CylindersModel::REMOVE, QHeaderView::Fixed); + ui->cylinders->verticalHeader()->setDefaultSectionSize( metrics.height() +8 ); ui->cylinders->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate()); - ui->weights->setColumnWidth(WeightModel::REMOVE, 24); + ui->weights->horizontalHeader()->setResizeMode (WeightModel::REMOVE , QHeaderView::Fixed); + ui->weights->verticalHeader()->setDefaultSectionSize( metrics.height() +8 ); ui->weights->setItemDelegateForColumn(WeightModel::TYPE, new WSInfoDelegate()); + initialUiSetup(); } // We need to manually position the 'plus' on cylinder and weight. @@ -155,15 +166,11 @@ void MainTab::clearStats() void MainTab::updateDiveInfo(int dive) { - // So, this is what happens now: - // Every tab should be populated from this method, - // it will be called whenever a new dive is selected - // I'm already populating the 'notes' box - // to show how it can be done. - // If you are unsure about the name of something, - // open the file maintab.ui on the designer - // click on the item and check its objectName, - // the access is ui->objectName from here on. + // This method updates ALL tabs whenever a new dive or trip is + // selected. + // If exactly one trip has been selected, we show the location / notes + // for the trip in the Info tab, otherwise we show the info of the + // selected_dive volume_t sacVal; temperature_t temp; struct dive *prevd; @@ -177,18 +184,48 @@ void MainTab::updateDiveInfo(int dive) UPDATE_TEXT(d, suit); UPDATE_TEXT(d, divemaster); UPDATE_TEXT(d, buddy); - /* infoTab */ if (d) { - /* make the fields writeable */ - ui->location->setReadOnly(false); - ui->divemaster->setReadOnly(false); - ui->buddy->setReadOnly(false); - ui->suit->setReadOnly(false); - ui->notes->setReadOnly(false); - ui->rating->setReadOnly(false); - ui->visibility->setReadOnly(false); - /* and fill them from the dive */ - ui->rating->setCurrentStars(d->rating); + if (mainWindow() && mainWindow()->dive_list()->selectedTrips.count() == 1) { + // only use trip relevant fields + ui->divemaster->setVisible(false); + ui->DivemasterLabel->setVisible(false); + ui->buddy->setVisible(false); + ui->BuddyLabel->setVisible(false); + ui->suit->setVisible(false); + ui->SuitLabel->setVisible(false); + ui->rating->setVisible(false); + ui->RatingLabel->setVisible(false); + ui->visibility->setVisible(false); + ui->visibilityLabel->setVisible(false); + // rename the remaining fields and fill data from selected trip + dive_trip_t *currentTrip = *mainWindow()->dive_list()->selectedTrips.begin(); + ui->location->setReadOnly(false); + ui->LocationLabel->setText(tr("Trip Location")); + ui->location->setText(currentTrip->location); + ui->notes->setReadOnly(false); + ui->NotesLabel->setText(tr("Trip Notes")); + ui->notes->setText(currentTrip->notes); + } else { + // make all the fields visible writeable + ui->divemaster->setVisible(true); + ui->buddy->setVisible(true); + ui->suit->setVisible(true); + ui->rating->setVisible(true); + ui->visibility->setVisible(true); + ui->divemaster->setReadOnly(false); + ui->buddy->setReadOnly(false); + ui->suit->setReadOnly(false); + ui->rating->setReadOnly(false); + ui->visibility->setReadOnly(false); + /* and fill them from the dive */ + ui->rating->setCurrentStars(d->rating); + ui->visibility->setCurrentStars(d->visibility); + // reset labels in case we last displayed trip notes + ui->location->setReadOnly(false); + ui->LocationLabel->setText(tr("Location")); + ui->notes->setReadOnly(false); + ui->NotesLabel->setText(tr("Notes")); + } ui->maximumDepthText->setText(get_depth_string(d->maxdepth, TRUE)); ui->averageDepthText->setText(get_depth_string(d->meandepth, TRUE)); ui->otuText->setText(QString("%1").arg(d->otu)); @@ -209,7 +246,6 @@ void MainTab::updateDiveInfo(int dive) ui->airPressureText->setText(QString("%1mbar").arg(d->surface_pressure.mbar)); else ui->airPressureText->clear(); - ui->visibility->setCurrentStars(d->visibility); ui->depthLimits->setMaximum(get_depth_string(stats_selection.max_depth, TRUE)); ui->depthLimits->setMinimum(get_depth_string(stats_selection.min_depth, TRUE)); ui->depthLimits->setAverage(get_depth_string(stats_selection.avg_depth, TRUE)); @@ -272,13 +308,6 @@ void MainTab::updateDiveInfo(int dive) ui->totalTimeAllText->clear(); ui->timeLimits->clear(); } - /* statisticsTab*/ - /* we can access the stats_selection struct, but how do we ensure the relevant dives are selected - * if we don't use the gtk widget to drive this? - * Maybe call process_selected_dives? Or re-write to query our Qt list view. - */ -// qDebug("max temp %u",stats_selection.max_temp); -// qDebug("min temp %u",stats_selection.min_temp); } void MainTab::addCylinder_clicked() @@ -308,30 +337,47 @@ void MainTab::on_editAccept_clicked(bool edit) mainWindow()->dive_list()->setEnabled(!edit); if (edit) { - ui->diveNotesMessage->setText(tr("This dive is being edited. Select Save or Undo when ready.")); - ui->diveNotesMessage->animatedShow(); - notesBackup.buddy = ui->buddy->text(); - notesBackup.suit = ui->suit->text(); - notesBackup.notes = ui->notes->toPlainText(); - notesBackup.divemaster = ui->divemaster->text(); - notesBackup.location = ui->location->text(); - notesBackup.rating = ui->rating->currentStars(); - notesBackup.visibility = ui->visibility->currentStars(); + if (mainWindow() && mainWindow()->dive_list()->selectedTrips.count() == 1) { + // we are editing trip location and notes + ui->diveNotesMessage->setText(tr("This trip is being edited. Select Save or Undo when ready.")); + ui->diveNotesMessage->animatedShow(); + notesBackup.notes = ui->notes->toPlainText(); + notesBackup.location = ui->location->text(); + editMode = TRIP; + } else { + ui->diveNotesMessage->setText(tr("This dive is being edited. Select Save or Undo when ready.")); + ui->diveNotesMessage->animatedShow(); + notesBackup.buddy = ui->buddy->text(); + notesBackup.suit = ui->suit->text(); + notesBackup.notes = ui->notes->toPlainText(); + notesBackup.divemaster = ui->divemaster->text(); + notesBackup.location = ui->location->text(); + notesBackup.rating = ui->rating->currentStars(); + notesBackup.visibility = ui->visibility->currentStars(); + editMode = DIVE; + } } else { ui->diveNotesMessage->animatedHide(); ui->editAccept->hide(); ui->editReset->hide(); /* now figure out if things have changed */ - if (notesBackup.buddy != ui->buddy->text() || - notesBackup.suit != ui->suit->text() || - notesBackup.notes != ui->notes->toPlainText() || - notesBackup.divemaster != ui->divemaster->text() || - notesBackup.location != ui->location->text() || - notesBackup.visibility != ui->visibility->currentStars() || - notesBackup.rating != ui->rating->currentStars()) - mark_divelist_changed(TRUE); - if (notesBackup.location != ui->location->text()) - mainWindow()->globe()->reload(); + if (mainWindow() && mainWindow()->dive_list()->selectedTrips.count() == 1) { + if (notesBackup.notes != ui->notes->toPlainText() || + notesBackup.location != ui->location->text()) + mark_divelist_changed(TRUE); + } else { + if (notesBackup.buddy != ui->buddy->text() || + notesBackup.suit != ui->suit->text() || + notesBackup.notes != ui->notes->toPlainText() || + notesBackup.divemaster != ui->divemaster->text() || + notesBackup.location != ui->location->text() || + notesBackup.visibility != ui->visibility->currentStars() || + notesBackup.rating != ui->rating->currentStars()) + mark_divelist_changed(TRUE); + if (notesBackup.location != ui->location->text()) + mainWindow()->globe()->reload(); + } + editMode = NONE; } } @@ -340,13 +386,15 @@ void MainTab::on_editReset_clicked() if (!ui->editAccept->isChecked()) return; - ui->buddy->setText(notesBackup.buddy); - ui->suit->setText(notesBackup.suit); ui->notes->setText(notesBackup.notes); - ui->divemaster->setText(notesBackup.divemaster); ui->location->setText(notesBackup.location); - ui->rating->setCurrentStars(notesBackup.rating); - ui->visibility->setCurrentStars(notesBackup.visibility); + if (mainWindow() && mainWindow()->dive_list()->selectedTrips.count() != 1) { + ui->buddy->setText(notesBackup.buddy); + ui->suit->setText(notesBackup.suit); + ui->divemaster->setText(notesBackup.divemaster); + ui->rating->setCurrentStars(notesBackup.rating); + ui->visibility->setCurrentStars(notesBackup.visibility); + } ui->editAccept->setChecked(false); ui->diveNotesMessage->animatedHide(); @@ -361,49 +409,62 @@ void MainTab::on_editReset_clicked() ui->editAccept->hide(); ui->editReset->hide(); + editMode = NONE; } -#define EDIT_NOTES(what, text) \ +#define EDIT_TEXT(what, text) \ QByteArray textByteArray = text.toLocal8Bit(); \ - free(currentDive->what);\ - currentDive->what = strdup(textByteArray.data()); + free(what);\ + what = strdup(textByteArray.data()); void MainTab::on_buddy_textChanged(const QString& text) { if (!currentDive) return; - EDIT_NOTES(buddy, text); + EDIT_TEXT(currentDive->buddy, text); } void MainTab::on_divemaster_textChanged(const QString& text) { if (!currentDive) return; - EDIT_NOTES(divemaster, text); + EDIT_TEXT(currentDive->divemaster, text); } void MainTab::on_location_textChanged(const QString& text) { - if (!currentDive) - return; - EDIT_NOTES(location, text); + if (editMode == TRIP && mainWindow() && mainWindow()->dive_list()->selectedTrips.count() == 1) { + // we are editing a trip + dive_trip_t *currentTrip = *mainWindow()->dive_list()->selectedTrips.begin(); + EDIT_TEXT(currentTrip->location, text); + } else if (editMode == DIVE){ + if (!currentDive) + return; + EDIT_TEXT(currentDive->location, text); + } } void MainTab::on_suit_textChanged(const QString& text) { if (!currentDive) return; - EDIT_NOTES(suit, text); + EDIT_TEXT(currentDive->suit, text); } void MainTab::on_notes_textChanged() { - if (!currentDive) - return; - EDIT_NOTES(notes, ui->notes->toPlainText()); + if (editMode == TRIP && mainWindow() && mainWindow()->dive_list()->selectedTrips.count() == 1) { + // we are editing a trip + dive_trip_t *currentTrip = *mainWindow()->dive_list()->selectedTrips.begin(); + EDIT_TEXT(currentTrip->notes, ui->notes->toPlainText()); + } else if (editMode == DIVE) { + if (!currentDive) + return; + EDIT_TEXT(currentDive->notes, ui->notes->toPlainText()); + } } -#undef EDIT_NOTES +#undef EDIT_TEXT void MainTab::on_rating_valueChanged(int value) { @@ -418,3 +479,45 @@ void MainTab::on_visibility_valueChanged(int value) return; currentDive->visibility = value; } + +void MainTab::hideEvent(QHideEvent* event) +{ + QSettings s; + s.beginGroup("MainTab"); + s.beginGroup("Cylinders"); + for (int i = 0; i < CylindersModel::COLUMNS; i++) { + s.setValue(QString("colwidth%1").arg(i), ui->cylinders->columnWidth(i)); + } + s.endGroup(); + s.beginGroup("Weights"); + for (int i = 0; i < WeightModel::COLUMNS; i++) { + s.setValue(QString("colwidth%1").arg(i), ui->weights->columnWidth(i)); + } + s.endGroup(); + s.sync(); +} + +void MainTab::initialUiSetup() +{ + QSettings s; + s.beginGroup("MainTab"); + s.beginGroup("Cylinders"); + for (int i = 0; i < CylindersModel::COLUMNS; i++) { + QVariant width = s.value(QString("colwidth%1").arg(i)); + if (width.isValid()) + ui->cylinders->setColumnWidth(i, width.toInt()); + else + ui->cylinders->resizeColumnToContents(i); + } + s.endGroup(); + s.beginGroup("Weights"); + for (int i = 0; i < WeightModel::COLUMNS; i++) { + QVariant width = s.value(QString("colwidth%1").arg(i)); + if (width.isValid()) + ui->weights->setColumnWidth(i, width.toInt()); + else + ui->weights->resizeColumnToContents(i); + } + s.endGroup(); + +} diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h index 38d01e806..124bcff7e 100644 --- a/qt-ui/maintab.h +++ b/qt-ui/maintab.h @@ -40,6 +40,10 @@ public: bool eventFilter(QObject* , QEvent*); virtual void resizeEvent(QResizeEvent*); virtual void showEvent(QShowEvent*); + virtual void hideEvent(QHideEvent* ); + + void initialUiSetup(); + public Q_SLOTS: void addCylinder_clicked(); @@ -63,6 +67,7 @@ private: struct dive* currentDive; QPushButton *addCylinder; QPushButton *addWeight; + enum { NONE, DIVE, TRIP } editMode; }; #endif diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index 55d4f3a54..c42dc4638 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -6,18 +6,15 @@ <rect> <x>0</x> <y>0</y> - <width>409</width> - <height>368</height> + <width>505</width> + <height>459</height> </rect> </property> <property name="windowTitle"> <string>TabWidget</string> </property> <property name="currentIndex"> - <number>3</number> - </property> - <property name="documentMode"> - <bool>true</bool> + <number>1</number> </property> <widget class="QWidget" name="notesTab"> <attribute name="title"> @@ -25,7 +22,7 @@ </attribute> <layout class="QGridLayout" name="gridLayout_3"> <item row="1" column="0"> - <widget class="QLabel" name="label_2"> + <widget class="QLabel" name="LocationLabel"> <property name="text"> <string>Location</string> </property> @@ -39,14 +36,14 @@ </widget> </item> <item row="3" column="0"> - <widget class="QLabel" name="label_15"> + <widget class="QLabel" name="DivemasterLabel"> <property name="text"> <string>Divemaster</string> </property> </widget> </item> <item row="3" column="1"> - <widget class="QLabel" name="label_3"> + <widget class="QLabel" name="BuddyLabel"> <property name="text"> <string>Buddy</string> </property> @@ -83,7 +80,7 @@ <item row="5" column="0"> <layout class="QHBoxLayout" name="ratingVisibilityLabels"> <item> - <widget class="QLabel" name="label_14"> + <widget class="QLabel" name="RatingLabel"> <property name="text"> <string>Rating</string> </property> @@ -99,14 +96,14 @@ </layout> </item> <item row="5" column="1"> - <widget class="QLabel" name="label_19"> + <widget class="QLabel" name="SuitLabel"> <property name="text"> <string>Suit</string> </property> </widget> </item> <item row="7" column="0"> - <widget class="QLabel" name="label_16"> + <widget class="QLabel" name="NotesLabel"> <property name="text"> <string>Notes</string> </property> @@ -195,6 +192,12 @@ <property name="alternatingRowColors"> <bool>true</bool> </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <attribute name="verticalHeaderVisible"> + <bool>false</bool> + </attribute> </widget> </item> </layout> @@ -205,7 +208,45 @@ </property> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> - <widget class="QTableView" name="weights"/> + <widget class="QTableView" name="weights"> + <property name="styleSheet"> + <string notr="true"> QTableView { + show-decoration-selected: 1; + } + + QTableView::item { + border: 1px solid #d9d9d9; + border-top-color: transparent; + border-bottom-color: transparent; + padding: 2px; + } + + QTableView::item:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #e7effd, stop: 1 #cbdaf1); + border: 1px solid #bfcde4; + } + + QTableView::item:selected { + border: 1px solid #567dbc; + } + + QTableView::item:selected:active{ + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6ea1f1, stop: 1 #567dbc); + } + + QTableView::item:selected:!active { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6b9be8, stop: 1 #577fbf); + } + +</string> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <attribute name="verticalHeaderVisible"> + <bool>false</bool> + </attribute> + </widget> </item> </layout> </widget> diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index a927c0231..53546fa67 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -45,9 +45,9 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), helpView(0) connect(ui->ListWidget, 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->ProfileWidget, SLOT(refresh())); ui->mainErrorMessage->hide(); - ui->ProfileWidget->setFocusProxy(ui->ListWidget); ui->ListWidget->reload(DiveTripModel::TREE); initialUiSetup(); readSettings(); @@ -87,10 +87,27 @@ void MainWindow::on_actionNew_triggered() void MainWindow::on_actionOpen_triggered() { - QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::homePath(), filter()); + QSettings settings; + QString lastDir = QDir::homePath(); + + settings.beginGroup("FileDialog"); + if (settings.contains("LastDir")) { + if(QDir::setCurrent(settings.value("LastDir").toString())) { + lastDir = settings.value("LastDir").toString(); + } + } + settings.endGroup(); + + QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), lastDir, filter()); if (filename.isEmpty()) return; + // Keep last open dir + QFileInfo fileInfo(filename); + settings.beginGroup("FileDialog"); + settings.setValue("LastDir",fileInfo.dir().path()); + settings.endGroup(); + // Needed to convert to char* QByteArray fileNamePtr = filename.toLocal8Bit(); @@ -194,7 +211,15 @@ void MainWindow::on_actionEditDeviceNames_triggered() void MainWindow::on_actionAddDive_triggered() { - qDebug("actionAddDive"); + struct dive *dive; + dive = alloc_dive(); + record_dive(dive); + process_dives(FALSE, FALSE); + + ui->InfoWidget->reload(); + ui->globe->reload(); + ui->ListWidget->reload(DiveTripModel::TREE); + ui->ListWidget->setFocus(); } void MainWindow::on_actionRenumber_triggered() @@ -217,32 +242,54 @@ void MainWindow::on_actionYearlyStatistics_triggered() qDebug("actionYearlyStatistics"); } +/** + * So, here's the deal. + * We have a few QSplitters that takes care of helping us with the + * size of a few widgets, they are ok, and we should continue using them + * to manage the visibility of them too. But the way that we did before was to + * widget->hide(); something, and if you hided something using the splitter, + * by holding it's handle and collapsing the widget, then you used the 'ctrl+number' + * shortcut to show it, it whould only show a gray panel. + * + * This patch makes everything behave using the splitters. + */ + +#define BEHAVIOR QList<int>() void MainWindow::on_actionViewList_triggered() { - ui->InfoWidget->setVisible(false); - ui->ListWidget->setVisible(true); - ui->ProfileWidget->setVisible(false); + ui->listGlobeSplitter->setSizes( BEHAVIOR << EXPANDED << COLLAPSED); + ui->mainSplitter->setSizes( BEHAVIOR << COLLAPSED << EXPANDED); } void MainWindow::on_actionViewProfile_triggered() { - ui->InfoWidget->setVisible(false); - ui->ListWidget->setVisible(false); - ui->ProfileWidget->setVisible(true); + ui->infoProfileSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED); + ui->mainSplitter->setSizes( BEHAVIOR << EXPANDED << COLLAPSED); } void MainWindow::on_actionViewInfo_triggered() { - ui->InfoWidget->setVisible(true); - ui->ListWidget->setVisible(false); - ui->ProfileWidget->setVisible(false); + ui->infoProfileSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED); + ui->mainSplitter->setSizes( BEHAVIOR << EXPANDED << COLLAPSED); } +void MainWindow::on_actionViewGlobe_triggered() +{ + ui->infoProfileSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED); + ui->mainSplitter->setSizes( BEHAVIOR << COLLAPSED << EXPANDED); +} +#undef BEHAVIOR + void MainWindow::on_actionViewAll_triggered() { - ui->InfoWidget->setVisible(true); - ui->ListWidget->setVisible(true); - ui->ProfileWidget->setVisible(true); + // big number squash the info profile to it's minimum. + ui->infoProfileSplitter->setSizes(QList<int>() << 1 << 20000); + + // big number squash the globe view. + ui->listGlobeSplitter->setSizes(QList<int>() << 2000 << 1 ); + + // half and half? + ui->mainSplitter->setSizes( QList<int>() << 1 << 1); } void MainWindow::on_actionPreviousDC_triggered() @@ -382,6 +429,7 @@ void MainWindow::initialUiSetup() resize(sz); ui->mainSplitter->restoreState(settings.value("mainSplitter").toByteArray()); ui->infoProfileSplitter->restoreState(settings.value("infoProfileSplitter").toByteArray()); + ui->listGlobeSplitter->restoreState(settings.value("listGlobeSplitter").toByteArray()); settings.endGroup(); settings.beginGroup("ListWidget"); @@ -473,6 +521,7 @@ void MainWindow::writeSettings() settings.setValue("size",size()); settings.setValue("mainSplitter", ui->mainSplitter->saveState()); settings.setValue("infoProfileSplitter", ui->infoProfileSplitter->saveState()); + settings.setValue("listGlobeSplitter", ui->listGlobeSplitter->saveState()); settings.endGroup(); settings.beginGroup("ListWidget"); diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index f3024ef8d..0b3812469 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -8,13 +8,13 @@ #define MAINWINDOW_H #include <QMainWindow> -#include <QModelIndex> #include <QAction> struct DiveList; class QSortFilterProxyModel; class DiveTripModel; + namespace Ui { class MainWindow; @@ -35,6 +35,8 @@ class MainWindow : public QMainWindow { Q_OBJECT public: + enum {COLLAPSED, EXPANDED}; + MainWindow(); ProfileGraphicsView *graphics(); MainTab *information(); @@ -71,6 +73,7 @@ private Q_SLOTS: void on_actionViewList_triggered(); void on_actionViewProfile_triggered(); void on_actionViewInfo_triggered(); + void on_actionViewGlobe_triggered(); void on_actionViewAll_triggered(); void on_actionPreviousDC_triggered(); void on_actionNextDC_triggered(); @@ -102,6 +105,7 @@ private: void redrawProfile(); void file_save(); void file_save_as(); + void setupSplitters(); }; MainWindow *mainWindow(); diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index fbf10cdf8..1a985f812 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -14,8 +14,8 @@ <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> <widget class="QSplitter" name="mainSplitter"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -92,7 +92,7 @@ </widget> </widget> </item> - <item> + <item row="1" column="0"> <widget class="KMessageWidget" name="mainErrorMessage" native="true"/> </item> </layout> @@ -103,7 +103,7 @@ <x>0</x> <y>0</y> <width>763</width> - <height>20</height> + <height>34</height> </rect> </property> <widget class="QMenu" name="menuFile"> @@ -147,7 +147,9 @@ <addaction name="actionViewList"/> <addaction name="actionViewProfile"/> <addaction name="actionViewInfo"/> + <addaction name="actionViewGlobe"/> <addaction name="actionViewAll"/> + <addaction name="separator"/> <addaction name="actionPreviousDC"/> <addaction name="actionNextDC"/> </widget> @@ -157,12 +159,6 @@ </property> <addaction name="actionSelectEvents"/> </widget> - <widget class="QMenu" name="menuPlanner"> - <property name="title"> - <string>Planner</string> - </property> - <addaction name="actionInputPlan"/> - </widget> <widget class="QMenu" name="menuHelp"> <property name="title"> <string>Help</string> @@ -174,7 +170,6 @@ <addaction name="menuLog"/> <addaction name="menuView"/> <addaction name="menuFilter"/> - <addaction name="menuPlanner"/> <addaction name="menuHelp"/> </widget> <action name="actionNew"> @@ -323,7 +318,7 @@ <string>View All</string> </property> <property name="shortcut"> - <string>Ctrl+4</string> + <string>Ctrl+5</string> </property> </action> <action name="actionPreviousDC"> @@ -365,9 +360,23 @@ <string>F1</string> </property> </action> + <action name="actionViewGlobe"> + <property name="text"> + <string>View Globe</string> + </property> + <property name="shortcut"> + <string>Ctrl+4</string> + </property> + </action> </widget> <customwidgets> <customwidget> + <class>KMessageWidget</class> + <extends>QWidget</extends> + <header>kmessagewidget.h</header> + <container>1</container> + </customwidget> + <customwidget> <class>MainTab</class> <extends>QWidget</extends> <header>maintab.h</header> @@ -389,12 +398,6 @@ <header>globe.h</header> <container>1</container> </customwidget> - <customwidget> - <class>KMessageWidget</class> - <extends>QWidget</extends> - <header>kmessagewidget.h</header> - <container>1</container> - </customwidget> </customwidgets> <resources/> <connections/> diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp index c90a1e8e1..01f5197ce 100644 --- a/qt-ui/modeldelegates.cpp +++ b/qt-ui/modeldelegates.cpp @@ -17,7 +17,6 @@ StarWidgetsDelegate::StarWidgetsDelegate(QWidget* parent): QStyledItemDelegate(parent), parentWidget(parent) { - } void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const @@ -51,19 +50,11 @@ QSize StarWidgetsDelegate::sizeHint(const QStyleOptionViewItem& option, const QM return QSize(IMG_SIZE * TOTALSTARS + SPACING * (TOTALSTARS-1), IMG_SIZE); } -QWidget* TankInfoDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const +ComboBoxDelegate::ComboBoxDelegate(QAbstractItemModel *model, QObject* parent): QStyledItemDelegate(parent), model(model) { - QComboBox *comboDelegate = new QComboBox(parent); - TankInfoModel *model = TankInfoModel::instance(); - comboDelegate->setModel(model); - comboDelegate->setEditable(true); - comboDelegate->setAutoCompletion(true); - comboDelegate->setAutoCompletionCaseSensitivity(Qt::CaseInsensitive); - comboDelegate->completer()->setCompletionMode(QCompleter::PopupCompletion); - return comboDelegate; } -void TankInfoDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const +void ComboBoxDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { QComboBox *c = qobject_cast<QComboBox*>(editor); QString data = index.model()->data(index, Qt::DisplayRole).toString(); @@ -74,6 +65,28 @@ void TankInfoDelegate::setEditorData(QWidget* editor, const QModelIndex& index) c->setEditText(data); } +QWidget* ComboBoxDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QComboBox *comboDelegate = new QComboBox(parent); + comboDelegate->setModel(model); + comboDelegate->setEditable(true); + comboDelegate->setAutoCompletion(true); + comboDelegate->setAutoCompletionCaseSensitivity(Qt::CaseInsensitive); + comboDelegate->completer()->setCompletionMode(QCompleter::PopupCompletion); + return comboDelegate; +} + +void ComboBoxDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QRect defaultRect = option.rect; + defaultRect.setX( defaultRect.x() -1); + defaultRect.setY( defaultRect.y() -1); + defaultRect.setWidth( defaultRect.width() + 2); + defaultRect.setHeight( defaultRect.height() + 2); + editor->setGeometry(defaultRect); +} + + void TankInfoDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& thisindex) const { QComboBox *c = qobject_cast<QComboBox*>(editor); @@ -97,31 +110,8 @@ void TankInfoDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, mymodel->passInData(model->index(thisindex.row(), CylindersModel::SIZE), tankSize); } -TankInfoDelegate::TankInfoDelegate(QObject* parent): QStyledItemDelegate(parent) -{ -} - -QWidget* WSInfoDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const +TankInfoDelegate::TankInfoDelegate(QObject* parent): ComboBoxDelegate(TankInfoModel::instance(), parent) { - QComboBox *comboDelegate = new QComboBox(parent); - WSInfoModel *model = WSInfoModel::instance(); - comboDelegate->setModel(model); - comboDelegate->setEditable(true); - comboDelegate->setAutoCompletion(true); - comboDelegate->setAutoCompletionCaseSensitivity(Qt::CaseInsensitive); - comboDelegate->completer()->setCompletionMode(QCompleter::PopupCompletion); - return comboDelegate; -} - -void WSInfoDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const -{ - QComboBox *c = qobject_cast<QComboBox*>(editor); - QString data = index.model()->data(index, Qt::DisplayRole).toString(); - int i = c->findText(data); - if (i != -1) - c->setCurrentIndex(i); - else - c->setEditText(data); } void WSInfoDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& thisindex) const @@ -145,6 +135,6 @@ void WSInfoDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, co mymodel->passInData(model->index(thisindex.row(), WeightModel::WEIGHT), grams); } -WSInfoDelegate::WSInfoDelegate(QObject* parent): QStyledItemDelegate(parent) +WSInfoDelegate::WSInfoDelegate(QObject* parent): ComboBoxDelegate(WSInfoModel::instance(), parent) { } diff --git a/qt-ui/modeldelegates.h b/qt-ui/modeldelegates.h index 79fbe297b..a33fc891b 100644 --- a/qt-ui/modeldelegates.h +++ b/qt-ui/modeldelegates.h @@ -13,21 +13,28 @@ private: QWidget *parentWidget; }; -class TankInfoDelegate : public QStyledItemDelegate{ +class ComboBoxDelegate : public QStyledItemDelegate{ Q_OBJECT public: - explicit TankInfoDelegate(QObject* parent = 0); + explicit ComboBoxDelegate(QAbstractItemModel *model, QObject* parent = 0); virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual void setEditorData(QWidget* editor, const QModelIndex& index) const; + virtual void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const; +protected: + QAbstractItemModel *model; +}; + +class TankInfoDelegate : public ComboBoxDelegate{ + Q_OBJECT +public: + explicit TankInfoDelegate(QObject* parent = 0); virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; }; -class WSInfoDelegate : public QStyledItemDelegate{ +class WSInfoDelegate : public ComboBoxDelegate{ Q_OBJECT public: explicit WSInfoDelegate(QObject* parent = 0); - virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; - virtual void setEditorData(QWidget* editor, const QModelIndex& index) const; virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; }; diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 868cba952..03300a8d6 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -16,6 +16,13 @@ #include <QFont> #include <QIcon> +QFont defaultModelFont() +{ + QFont font; + font.setPointSizeF( font.pointSizeF() * 0.8); + return font; +} + CylindersModel::CylindersModel(QObject* parent): QAbstractTableModel(parent), current(0), rows(0) { } @@ -23,15 +30,14 @@ CylindersModel::CylindersModel(QObject* parent): QAbstractTableModel(parent), cu QVariant CylindersModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant ret; - QFont font; if (orientation == Qt::Vertical) return ret; switch (role) { case Qt::FontRole: - font.setPointSizeF(font.pointSizeF() * 0.8); - return font; + ret = defaultModelFont(); + break; case Qt::DisplayRole: switch(section) { case TYPE: ret = tr("Type"); break; @@ -54,7 +60,6 @@ int CylindersModel::columnCount(const QModelIndex& parent) const QVariant CylindersModel::data(const QModelIndex& index, int role) const { QVariant ret; - QFont font; if (!index.isValid() || index.row() >= MAX_CYLINDERS) return ret; @@ -62,11 +67,10 @@ QVariant CylindersModel::data(const QModelIndex& index, int role) const cylinder_t *cyl = ¤t->cylinder[index.row()]; switch (role) { case Qt::FontRole: - font.setPointSizeF(font.pointSizeF() * 0.80); - ret = font; + ret = defaultModelFont(); break; case Qt::TextAlignmentRole: - ret = Qt::AlignRight; + ret = Qt::AlignHCenter; break; case Qt::DisplayRole: case Qt::EditRole: @@ -338,7 +342,6 @@ int WeightModel::columnCount(const QModelIndex& parent) const QVariant WeightModel::data(const QModelIndex& index, int role) const { QVariant ret; - QFont font; if (!index.isValid() || index.row() >= MAX_WEIGHTSYSTEMS) return ret; @@ -346,8 +349,7 @@ QVariant WeightModel::data(const QModelIndex& index, int role) const switch (role) { case Qt::FontRole: - font.setPointSizeF(font.pointSizeF() * 0.80); - ret = font; + ret = defaultModelFont(); break; case Qt::TextAlignmentRole: ret = Qt::AlignRight; @@ -431,14 +433,12 @@ int WeightModel::rowCount(const QModelIndex& parent) const QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant ret; - QFont font; if (orientation == Qt::Vertical) return ret; switch (role) { case Qt::FontRole: - font.setPointSizeF(font.pointSizeF() * 0.8); - ret = font; + ret = defaultModelFont(); break; case Qt::DisplayRole: switch(section) { @@ -536,16 +536,21 @@ QVariant WSInfoModel::data(const QModelIndex& index, int role) const struct ws_info *info = &ws_info[index.row()]; int gr = info->grams; - - if (role == Qt::DisplayRole || role == Qt::EditRole) { - switch(index.column()) { - case GR: - ret = gr; - break; - case DESCRIPTION: - ret = QString(info->name); - break; - } + switch(role){ + case Qt::FontRole : + ret = defaultModelFont(); + break; + case Qt::DisplayRole : + case Qt::EditRole : + switch(index.column()) { + case GR: + ret = gr; + break; + case DESCRIPTION: + ret = QString(info->name); + break; + } + break; } return ret; } @@ -557,15 +562,20 @@ QVariant WSInfoModel::headerData(int section, Qt::Orientation orientation, int r if (orientation != Qt::Horizontal) return ret; - if (role == Qt::DisplayRole) { - switch(section) { - case GR: - ret = tr("kg"); - break; - case DESCRIPTION: - ret = tr("Description"); - break; - } + switch(role){ + case Qt::FontRole : + ret = defaultModelFont(); + break; + case Qt::DisplayRole : + switch(section) { + case GR: + ret = tr("kg"); + break; + case DESCRIPTION: + ret = tr("Description"); + break; + } + break; } return ret; } @@ -575,10 +585,20 @@ int WSInfoModel::rowCount(const QModelIndex& parent) const return rows+1; } +const QString& WSInfoModel::biggerString() const +{ + return biggerEntry; +} + WSInfoModel::WSInfoModel() : QAbstractTableModel(), rows(-1) { struct ws_info *info = ws_info; - for (info = ws_info; info->name; info++, rows++); + for (info = ws_info; info->name; info++, rows++){ + QString wsInfoName(info->name); + if( wsInfoName.count() > biggerEntry.count()){ + biggerEntry = wsInfoName; + } + } if (rows > -1) { beginInsertRows(QModelIndex(), 0, rows); @@ -608,6 +628,11 @@ TankInfoModel* TankInfoModel::instance() return self; } +const QString& TankInfoModel::biggerString() const +{ + return biggerEntry; +} + bool TankInfoModel::insertRows(int row, int count, const QModelIndex& parent) { beginInsertRows(parent, rowCount(), rowCount()); @@ -648,6 +673,10 @@ QVariant TankInfoModel::data(const QModelIndex& index, int role) const if (!index.isValid()) { return ret; } + if (role == Qt::FontRole){ + return defaultModelFont(); + } + struct tank_info *info = &tank_info[index.row()]; int ml = info->ml; @@ -682,8 +711,12 @@ QVariant TankInfoModel::headerData(int section, Qt::Orientation orientation, int if (orientation != Qt::Horizontal) return ret; - if (role == Qt::DisplayRole) { - switch(section) { + switch(role){ + case Qt::FontRole: + ret = defaultModelFont(); + break; + case Qt::DisplayRole: + switch(section) { case BAR: ret = tr("Bar"); break; @@ -693,7 +726,8 @@ QVariant TankInfoModel::headerData(int section, Qt::Orientation orientation, int case DESCRIPTION: ret = tr("Description"); break; - } + } + break; } return ret; } @@ -706,7 +740,13 @@ int TankInfoModel::rowCount(const QModelIndex& parent) const TankInfoModel::TankInfoModel() : QAbstractTableModel(), rows(-1) { struct tank_info *info = tank_info; - for (info = tank_info; info->name; info++, rows++); + for (info = tank_info; info->name; info++, rows++){ + QString infoName(info->name); + if (infoName.count() > biggerEntry.count()){ + biggerEntry = infoName; + } + } + if (rows > -1) { beginInsertRows(QModelIndex(), 0, rows); endInsertRows(); @@ -754,21 +794,25 @@ int TreeItemDT::row() const QVariant TreeItemDT::data(int column, int role) const { QVariant ret; - switch (column) { - case NR: ret = tr("#"); break; - case DATE: ret = tr("Date"); break; - case RATING: ret = UTF8_BLACKSTAR; break; - case DEPTH: ret = (get_units()->length == units::METERS) ? tr("m") : tr("ft"); break; - case DURATION: ret = tr("min"); break; - case TEMPERATURE: ret = QString("%1%2").arg(UTF8_DEGREE).arg((get_units()->temperature == units::CELSIUS) ? "C" : "F"); break; - case TOTALWEIGHT: ret = (get_units()->weight == units::KG) ? tr("kg") : tr("lbs"); break; - case SUIT: ret = tr("Suit"); break; - case CYLINDER: ret = tr("Cyl"); break; - case NITROX: ret = QString("O%1%").arg(UTF8_SUBSCRIPT_2); break; - case SAC: ret = tr("SAC"); break; - case OTU: ret = tr("OTU"); break; - case MAXCNS: ret = tr("maxCNS"); break; - case LOCATION: ret = tr("Location"); break; + switch(role){ + case Qt::DisplayRole : + switch (column) { + case NR: ret = tr("#"); break; + case DATE: ret = tr("Date"); break; + case RATING: ret = UTF8_BLACKSTAR; break; + case DEPTH: ret = (get_units()->length == units::METERS) ? tr("m") : tr("ft"); break; + case DURATION: ret = tr("min"); break; + case TEMPERATURE: ret = QString("%1%2").arg(UTF8_DEGREE).arg((get_units()->temperature == units::CELSIUS) ? "C" : "F"); break; + case TOTALWEIGHT: ret = (get_units()->weight == units::KG) ? tr("kg") : tr("lbs"); break; + case SUIT: ret = tr("Suit"); break; + case CYLINDER: ret = tr("Cyl"); break; + case NITROX: ret = QString("O%1%").arg(UTF8_SUBSCRIPT_2); break; + case SAC: ret = tr("SAC"); break; + case OTU: ret = tr("OTU"); break; + case MAXCNS: ret = tr("maxCNS"); break; + case LOCATION: ret = tr("Location"); break; + } + break; } return ret; } @@ -787,11 +831,8 @@ QVariant TripItem::data(int column, int role) const if (role == Qt::DisplayRole) { switch (column) { - case LOCATION: - ret = QString(trip->location); - break; - case DATE: - ret = QString(get_trip_date_string(trip->when, trip->nrdives)); + case NR: + ret = QString(trip->location) + ", " + QString(get_trip_date_string(trip->when, trip->nrdives)); break; } } @@ -837,7 +878,7 @@ QVariant DiveItem::data(int column, int role) const break; case SORT_ROLE: switch (column) { - case NR: retVal = dive->number; break; + case NR: retVal = (qulonglong) dive->when; break; case DATE: retVal = (qulonglong) dive->when; break; case RATING: retVal = dive->rating; break; case DEPTH: retVal = dive->maxdepth.mm; break; @@ -932,7 +973,7 @@ QString DiveItem::displaySac() const QString str; if (get_units()->volume == units::LITER) - str = QString::number(dive->sac / 1000, 'f', 1); + str = QString::number(dive->sac / 1000.0, 'f', 1); else str = QString::number(ml_to_cuft(dive->sac), 'f', 2); @@ -986,9 +1027,7 @@ QVariant DiveTripModel::data(const QModelIndex& index, int role) const return QVariant(); if (role == Qt::FontRole) { - QFont font; - font.setPointSizeF(font.pointSizeF() * 0.7); - return font; + return defaultModelFont(); } TreeItemDT* item = static_cast<TreeItemDT*>(index.internalPointer()); @@ -1009,6 +1048,11 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return rootItem->data(section, role); + switch(role){ + case Qt::FontRole : + return defaultModelFont(); + } + return QVariant(); } @@ -1062,6 +1106,9 @@ void DiveTripModel::setupModelData() endRemoveRows(); } + if (autogroup) + autogroup_dives(); + dive_table.preexisting = dive_table.nr; while (--i >= 0) { struct dive* dive = get_dive(i); update_cylinder_related_info(dive); diff --git a/qt-ui/models.h b/qt-ui/models.h index 5a7d2b214..b61c79cd6 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -14,6 +14,8 @@ #include "../dive.h" #include "../divelist.h" +QFont defaultModelFont(); + /* Encapsulates the tank_info global variable * to show on Qt's Model View System.*/ class TankInfoModel : public QAbstractTableModel { @@ -30,10 +32,12 @@ public: /*reimp*/ int rowCount(const QModelIndex& parent = QModelIndex()) const; /*reimp*/ bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()); /*reimp*/ bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + const QString& biggerString() const; void clear(); void update(); private: int rows; + QString biggerEntry; }; /* Encapsulate ws_info */ @@ -51,11 +55,12 @@ public: /*reimp*/ int rowCount(const QModelIndex& parent = QModelIndex()) const; /*reimp*/ bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()); /*reimp*/ bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + const QString& biggerString() const; void clear(); void update(); private: int rows; - + QString biggerEntry; }; /* Encapsulation of the Cylinder Model, that presents the diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index cfd155132..126690750 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -1,4 +1,6 @@ #include "profilegraphics.h" +#include "mainwindow.h" +#include "divelistview.h" #include <QGraphicsScene> #include <QResizeEvent> @@ -12,11 +14,13 @@ #include <QPropertyAnimation> #include <QGraphicsSceneHoverEvent> #include <QMouseEvent> +#include <qtextdocument.h> #include "../color.h" #include "../display.h" #include "../dive.h" #include "../profile.h" +#include "../device.h" #include <libdivecomputer/parser.h> #include <libdivecomputer/version.h> @@ -145,6 +149,7 @@ void ProfileGraphicsView::wheelEvent(QWheelEvent* event) // Scale the view / do the zoom QPoint toolTipPos = mapFromScene(toolTip->pos()); + double scaleFactor = 1.15; if (event->delta() > 0 && zoomLevel <= 10) { scale(scaleFactor, scaleFactor); @@ -171,14 +176,20 @@ void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event) ensureVisible(event->pos().x() + dx, event->pos().y() + dy, 1, 1); - toolTip->setPos(mapToScene(toolTipPos).x(), mapToScene(toolTipPos).y()); - if (zoomLevel == 0) QGraphicsView::mouseMoveEvent(event); + else + toolTip->setPos(mapToScene(toolTipPos).x(), mapToScene(toolTipPos).y()); } bool ProfileGraphicsView::eventFilter(QObject* obj, QEvent* event) { + if (event->type() == QEvent::Leave) { + if (toolTip && toolTip->isExpanded()) + toolTip->collapse(); + return true; + } + // This will "Eat" the default tooltip behavior. if (event->type() == QEvent::GraphicsSceneHelp) { event->ignore(); @@ -206,7 +217,7 @@ void ProfileGraphicsView::showEvent(QShowEvent* event) // but the dive was not ploted. // force a replot by modifying the dive // hold by the view, and issuing a plot. - if (dive) { + if (dive && !scene()->items().count()) { dive = 0; plot(get_dive(selected_dive)); } @@ -214,10 +225,14 @@ void ProfileGraphicsView::showEvent(QShowEvent* event) void ProfileGraphicsView::clear() { - scene()->clear(); resetTransform(); zoomLevel = 0; - toolTip = 0; + if(toolTip){ + scene()->removeItem(toolTip); + toolTip->deleteLater(); + toolTip = 0; + } + scene()->clear(); } void ProfileGraphicsView::refresh() @@ -243,42 +258,32 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) return; } + // best place to put the focus stealer code. + setFocusProxy(mainWindow()->dive_list()); scene()->setSceneRect(0,0, viewport()->width()-50, viewport()->height()-50); - QSettings s; - s.beginGroup("ProfileMap"); - QPointF toolTipPos = s.value("tooltip_position", QPointF(0,0)).toPointF(); - s.endGroup(); - toolTip = new ToolTipItem(); - toolTip->setPos(toolTipPos); - + installEventFilter(toolTip); scene()->addItem(toolTip); // Fix this for printing / screen later. // plot_set_scale(scale_mode_t); if (!dc->samples) { - static struct sample fake[4]; - static struct divecomputer fakedc; - fakedc = dive->dc; - fakedc.sample = fake; - fakedc.samples = 4; - - /* The dive has no samples, so create a few fake ones. This assumes an - ascent/descent rate of 9 m/min, which is just below the limit for FAST. */ - int duration = dive->dc.duration.seconds; - int maxdepth = dive->dc.maxdepth.mm; - int asc_desc_time = dive->dc.maxdepth.mm*60/9000; - if (asc_desc_time * 2 >= duration) - asc_desc_time = duration / 2; - fake[1].time.seconds = asc_desc_time; - fake[1].depth.mm = maxdepth; - fake[2].time.seconds = duration - asc_desc_time; - fake[2].depth.mm = maxdepth; - fake[3].time.seconds = duration * 1.00; - fakedc.events = dc->events; - dc = &fakedc; + dc = fake_dc(dc); + } + + QString nick(get_dc_nickname(dc->model, dc->deviceid)); + if (nick.isEmpty()) + nick = QString(dc->model); + + if (nick.isEmpty()) + nick = tr("unknown divecomputer"); + + if ( tr("unknown divecomputer") == nick){ + mode = PLAN; + }else{ + mode = DIVE; } /* @@ -291,7 +296,7 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) gc.maxx = (profile_grid_area.width() - 2 * profile_grid_area.x()); gc.maxy = (profile_grid_area.height() - 2 * profile_grid_area.y()); - /* This is per-dive-computer. Right now we just do the first one */ + /* This is per-dive-computer */ gc.pi = *create_plot_info(dive, dc, &gc); /* Depth profile */ @@ -303,7 +308,7 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) plot_temperature_profile(); /* Cylinder pressure plot */ - plot_cylinder_pressure(dive, dc); + plot_cylinder_pressure(dc); /* Text on top of all graphs.. */ plot_temperature_text(); @@ -322,13 +327,6 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) scene()->addItem(rect); /* Put the dive computer name in the lower left corner */ - QString nick(get_dc_nickname(dc->model, dc->deviceid)); - if (nick.isEmpty()) - nick = QString(dc->model); - - if (nick.isEmpty()) - nick = tr("unknown divecomputer"); - gc.leftx = 0; gc.rightx = 1.0; gc.topy = 0; gc.bottomy = 1.0; @@ -342,7 +340,6 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) plot_pp_text(); } - /* now shift the translation back by half the margin; * this way we can draw the vertical scales on both sides */ //cairo_translate(gc->cr, -drawing_area->x / 2.0, 0); @@ -366,6 +363,16 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) if (zoomLevel == 0) { fitInView(sceneRect()); } + toolTip->readPos(); + + if(mode == PLAN){ + timeEditor = new GraphicsTextEditor(); + timeEditor->setPlainText( dive->duration.seconds ? QString::number(dive->duration.seconds/60) : tr("Set Duration: 10 minutes")); + timeEditor->setPos(profile_grid_area.width() - timeEditor->boundingRect().width(), timeMarkers->y()); + timeEditor->document(); + connect(timeEditor, SIGNAL(editingFinished(QString)), this, SLOT(edit_dive_time(QString))); + scene()->addItem(timeEditor); + } } void ProfileGraphicsView::plot_depth_scale() @@ -420,6 +427,17 @@ void ProfileGraphicsView::plot_pp_text() } } +void ProfileGraphicsView::plot_add_line(int sec, double val, QColor c, QPointF &from) +{ + QPointF to = QPointF(SCALEGC(sec, val)); + QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); + QPen pen(defaultPen); + pen.setColor(c); + item->setPen(pen); + scene()->addItem(item); + from = to; +} + void ProfileGraphicsView::plot_pp_gas_profile() { int i; @@ -435,17 +453,10 @@ void ProfileGraphicsView::plot_pp_gas_profile() from = QPointF(SCALEGC(entry->sec, entry->pn2)); for (i = 1; i < pi->nr; i++) { entry++; - if (entry->pn2 < prefs.pp_graphs.pn2_threshold) { - to = QPointF(SCALEGC(entry->sec, entry->pn2)); - QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - QPen pen(defaultPen); - pen.setColor(c); - item->setPen(pen); - scene()->addItem(item); - from = to; - } else { + if (entry->pn2 < prefs.pp_graphs.pn2_threshold) + plot_add_line(entry->sec, entry->pn2, c, from); + else from = QPointF(SCALEGC(entry->sec, entry->pn2)); - } } c = profile_color[PN2_ALERT].first(); @@ -453,17 +464,10 @@ void ProfileGraphicsView::plot_pp_gas_profile() from = QPointF(SCALEGC(entry->sec, entry->pn2)); for (i = 1; i < pi->nr; i++) { entry++; - if (entry->pn2 >= prefs.pp_graphs.pn2_threshold) { - to = QPointF(SCALEGC(entry->sec, entry->pn2)); - QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - QPen pen(defaultPen); - pen.setColor(c); - item->setPen(pen); - scene()->addItem(item); - from = to; - } else { + if (entry->pn2 >= prefs.pp_graphs.pn2_threshold) + plot_add_line(entry->sec, entry->pn2, c, from); + else from = QPointF(SCALEGC(entry->sec, entry->pn2)); - } } } @@ -474,17 +478,10 @@ void ProfileGraphicsView::plot_pp_gas_profile() from = QPointF(SCALEGC(entry->sec, entry->phe)); for (i = 1; i < pi->nr; i++) { entry++; - if (entry->phe < prefs.pp_graphs.phe_threshold) { - to = QPointF(SCALEGC(entry->sec, entry->phe)); - QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - QPen pen(defaultPen); - pen.setColor(c); - item->setPen(pen); - scene()->addItem(item); - from = to; - } else { + if (entry->phe < prefs.pp_graphs.phe_threshold) + plot_add_line(entry->sec, entry->phe, c, from); + else from = QPointF(SCALEGC(entry->sec, entry->phe)); - } } c = profile_color[PHE_ALERT].first(); @@ -492,17 +489,10 @@ void ProfileGraphicsView::plot_pp_gas_profile() from = QPointF(SCALEGC(entry->sec, entry->phe)); for (i = 1; i < pi->nr; i++) { entry++; - if (entry->phe >= prefs.pp_graphs.phe_threshold) { - to = QPointF(SCALEGC(entry->sec, entry->phe)); - QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - QPen pen(defaultPen); - pen.setColor(c); - item->setPen(pen); - scene()->addItem(item); - from = to; - } else { + if (entry->phe >= prefs.pp_graphs.phe_threshold) + plot_add_line(entry->sec, entry->phe, c, from); + else from = QPointF(SCALEGC(entry->sec, entry->phe)); - } } } if (prefs.pp_graphs.po2) { @@ -511,17 +501,10 @@ void ProfileGraphicsView::plot_pp_gas_profile() from = QPointF(SCALEGC(entry->sec, entry->po2)); for (i = 1; i < pi->nr; i++) { entry++; - if (entry->po2 < prefs.pp_graphs.po2_threshold) { - to = QPointF(SCALEGC(entry->sec, entry->po2)); - QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - QPen pen(defaultPen); - pen.setColor(c); - item->setPen(pen); - scene()->addItem(item); - from = to; - } else { + if (entry->po2 < prefs.pp_graphs.po2_threshold) + plot_add_line(entry->sec, entry->po2, c, from); + else from = QPointF(SCALEGC(entry->sec, entry->po2)); - } } c = profile_color[PO2_ALERT].first(); @@ -529,15 +512,10 @@ void ProfileGraphicsView::plot_pp_gas_profile() from = QPointF(SCALEGC(entry->sec, entry->po2)); for (i = 1; i < pi->nr; i++) { entry++; - if (entry->po2 >= prefs.pp_graphs.po2_threshold) { - to = QPointF(SCALEGC(entry->sec, entry->po2)); - QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y()); - item->setPen(QPen(c)); - scene()->addItem(item); - from = to; - } else { + if (entry->po2 >= prefs.pp_graphs.po2_threshold) + plot_add_line(entry->sec, entry->po2, c, from); + else from = QPointF(SCALEGC(entry->sec, entry->po2)); - } } } } @@ -720,7 +698,7 @@ void ProfileGraphicsView::plot_single_temp_text(int sec, int mkelvin) plot_text(&tro, QPointF(sec, mkelvin), QString("%1%2").arg(deg, 0, 'f', 1).arg(unit)); //"%.2g%s" } -void ProfileGraphicsView::plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc) +void ProfileGraphicsView::plot_cylinder_pressure(struct divecomputer *dc) { int i; int last = -1, last_index = -1; @@ -1213,6 +1191,12 @@ void ProfileGraphicsView::plot_temperature_profile() } } +void ProfileGraphicsView::edit_dive_time(const QString& time) +{ + // this should set the full time of the dive. + refresh(); +} + void ToolTipItem::addToolTip(const QString& toolTip, const QIcon& icon) { QGraphicsPixmapItem *iconItem = 0; @@ -1324,6 +1308,9 @@ void ToolTipItem::collapse() animation->setStartValue(boundingRect()); animation->setEndValue(QRect(0, 0, ICON_SMALL, ICON_SMALL)); animation->start(QAbstractAnimation::DeleteWhenStopped); + clear(); + + status = COLLAPSED; } void ToolTipItem::expand() @@ -1358,20 +1345,26 @@ void ToolTipItem::expand() animation->setEndValue(nextRectangle); animation->start(QAbstractAnimation::DeleteWhenStopped); + status = EXPANDED; } ToolTipItem::ToolTipItem(QGraphicsItem* parent): QGraphicsPathItem(parent), background(0) { title = new QGraphicsSimpleTextItem(tr("Information"), this); separator = new QGraphicsLineItem(this); - + dragging = false; setFlag(ItemIgnoresTransformations); - setFlag(ItemIsMovable); - + status = COLLAPSED; updateTitlePosition(); setZValue(99); } +ToolTipItem::~ToolTipItem() +{ + clear(); +} + + void ToolTipItem::updateTitlePosition() { if (rectangle.width() < title->boundingRect().width() + SPACING*4) { @@ -1401,6 +1394,51 @@ void ToolTipItem::updateTitlePosition() } } +bool ToolTipItem::isExpanded() { + return status == EXPANDED; +} + +void ToolTipItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) +{ + persistPos(); + dragging = false; +} + +void ToolTipItem::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + dragging = true; +} + +void ToolTipItem::persistPos() +{ + QPoint currentPos = scene()->views().at(0)->mapFromScene(pos()); + QSettings s; + s.beginGroup("ProfileMap"); + s.setValue("tooltip_position", currentPos); + s.endGroup(); + s.sync(); +} + +void ToolTipItem::readPos() +{ + QSettings s; + s.beginGroup("ProfileMap"); + QPointF value = scene()->views().at(0)->mapToScene( + s.value("tooltip_position").toPoint() + ); + setPos(value); +} + +bool ToolTipItem::eventFilter(QObject* view, QEvent* event) +{ + if (event->type() == QEvent::HoverMove && dragging){ + QHoverEvent *e = static_cast<QHoverEvent*>(event); + QGraphicsView *v = scene()->views().at(0); + setPos( v->mapToScene(e->pos())); + } + return false; +} + EventItem::EventItem(QGraphicsItem* parent): QGraphicsPolygonItem(parent) { setFlag(ItemIgnoresTransformations); @@ -1431,5 +1469,27 @@ EventItem::EventItem(QGraphicsItem* parent): QGraphicsPolygonItem(parent) QGraphicsEllipseItem *ball = new QGraphicsEllipseItem(-1, 12, 2,2, this); ball->setBrush(QBrush(Qt::black)); +} +GraphicsTextEditor::GraphicsTextEditor(QGraphicsItem* parent): QGraphicsTextItem(parent) +{ +} + +void GraphicsTextEditor::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +{ + // Remove the proxy filter so we can focus here. + mainWindow()->graphics()->setFocusProxy(0); + setTextInteractionFlags(Qt::TextEditorInteraction | Qt::TextEditable); +} + +void GraphicsTextEditor::keyReleaseEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return){ + setTextInteractionFlags(Qt::NoTextInteraction); + emit editingFinished( toPlainText() ); + mainWindow()->graphics()->setFocusProxy(mainWindow()->dive_list()); + return; + } + emit textChanged( toPlainText() ); + QGraphicsTextItem::keyReleaseEvent(event); } diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index b440adf5d..ccc4841cb 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -5,6 +5,9 @@ #include <QGraphicsView> #include <QGraphicsItem> #include <QIcon> +#include <QDoubleSpinBox> +#include <QPushButton> +#include <QGraphicsProxyWidget> struct text_render_options; struct graphics_context; @@ -25,6 +28,7 @@ public: enum {ICON_SMALL = 16, ICON_MEDIUM = 24, ICON_BIG = 32, SPACING=4}; explicit ToolTipItem(QGraphicsItem* parent = 0); + virtual ~ToolTipItem(); void collapse(); void expand(); @@ -32,7 +36,12 @@ public: void addToolTip(const QString& toolTip, const QIcon& icon = QIcon()); void removeToolTip(const QString& toolTip); void refresh(struct graphics_context* gc, QPointF pos); - + bool isExpanded(); + void persistPos(); + void readPos(); + void mousePressEvent(QGraphicsSceneMouseEvent* event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event); + bool eventFilter(QObject* , QEvent* ); public Q_SLOTS: void setRect(const QRectF& rect); @@ -42,8 +51,9 @@ private: QGraphicsPathItem *background; QGraphicsLineItem *separator; QGraphicsSimpleTextItem *title; - + Status status; QRectF rectangle; + bool dragging; }; class EventItem : public QGraphicsPolygonItem @@ -57,10 +67,26 @@ private: QIcon icon; }; +class GraphicsTextEditor : public QGraphicsTextItem{ + Q_OBJECT +public: + GraphicsTextEditor(QGraphicsItem* parent = 0); + +protected: + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event); + virtual void keyReleaseEvent(QKeyEvent* event); + +signals: + void textChanged(const QString& text); + void editingFinished(const QString& text); +}; + class ProfileGraphicsView : public QGraphicsView { Q_OBJECT public: + enum Mode{DIVE, PLAN}; + ProfileGraphicsView(QWidget* parent = 0); void plot(struct dive *d, bool forceRedraw = FALSE); bool eventFilter(QObject* obj, QEvent* event); @@ -74,6 +100,7 @@ protected: public Q_SLOTS: void refresh(); + void edit_dive_time(const QString& time); private: void plot_depth_profile(); @@ -81,7 +108,7 @@ private: void plot_events(struct divecomputer *dc); void plot_one_event(struct event *event); void plot_temperature_profile(); - void plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc); + void plot_cylinder_pressure(struct divecomputer *dc); void plot_temperature_text(); void plot_single_temp_text(int sec, int mkelvin); void plot_depth_text(); @@ -91,6 +118,7 @@ private: void plot_pressure_value(int mbar, int sec, double xalign, double yalign); void plot_gas_value(int mbar, int sec, double xalign, double yalign, int o2, int he); void plot_deco_text(); + void plot_add_line(int sec, double val, QColor c, QPointF &from); void plot_pp_gas_profile(); void plot_pp_text(); void plot_depth_scale(); @@ -110,6 +138,12 @@ private: QGraphicsItem* timeMarkers; QGraphicsItem* depthMarkers; QGraphicsItem* diveComputer; + + // For 'Plan' mode.: + GraphicsTextEditor *depthEditor; + GraphicsTextEditor *timeEditor; + + enum Mode mode; }; #endif diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index 2f82d6d26..cd944ca17 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -59,10 +59,14 @@ void SubsurfaceWebServices::buttonClicked(QAbstractButton* button) QSettings s; s.setValue("webservice_uid", ui->userID->text()); s.sync(); + hide(); + close(); } break; case QDialogButtonBox::RejectRole: - manager->deleteLater(); + // we may want to clean up after ourselves, but this + // makes Subsurface throw a SIGSEGV... + // manager->deleteLater(); reply->deleteLater(); ui->progressBar->setMaximum(1); break; @@ -90,7 +94,7 @@ void SubsurfaceWebServices::startDownload() ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); } diff --git a/statistics.c b/statistics.c index 6459b3802..ece5419d9 100644 --- a/statistics.c +++ b/statistics.c @@ -352,7 +352,11 @@ char *get_gaslist(struct dive *dive) if (is_air(o2, he)) snprintf(buf + offset, MAXBUF - offset, (offset > 0) ? ", %s" : "%s", _("air")); else - snprintf(buf + offset, MAXBUF - offset, (offset > 0) ? ", %d/%d" : "%d/%d", + if (he == 0) + snprintf(buf + offset, MAXBUF - offset, (offset > 0) ? _(", EAN%d") : _("EAN%d"), + (o2 + 5) / 10); + else + snprintf(buf + offset, MAXBUF - offset, (offset > 0) ? ", %d/%d" : "%d/%d", (o2 + 5) / 10, (he + 5) / 10); offset = strlen(buf); } diff --git a/xslt/uddf.xslt b/xslt/uddf.xslt index 1d65d2d19..274280169 100644 --- a/xslt/uddf.xslt +++ b/xslt/uddf.xslt @@ -2,7 +2,7 @@ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:u="http://www.streit.cc/uddf/3.2/" xmlns:u1="http://www.streit.cc/uddf/3.1/" - exclude-result-prefixes="u" + exclude-result-prefixes="u u1" version="1.0"> <xsl:import href="commonTemplates.xsl"/> <xsl:strip-space elements="*"/> @@ -12,11 +12,18 @@ <divelog program="subsurface-import" version="2"> <settings> <divecomputerid deviceid="ffffffff"> - <xsl:apply-templates select="/uddf/generator|/u:uddf/u:generator|/u1:uddf/u1:generator"/> + <xsl:choose> + <xsl:when test="/UDDF/history != ''"> + <xsl:apply-templates select="/UDDF/history"/> + </xsl:when> + <xsl:otherwise> + <xsl:apply-templates select="/uddf/generator|/u:uddf/u:generator|/u1:uddf/u1:generator"/> + </xsl:otherwise> + </xsl:choose> </divecomputerid> </settings> <dives> - <xsl:apply-templates select="/uddf/profiledata/repetitiongroup/dive|/u:uddf/u:profiledata/u:repetitiongroup/u:dive|/u1:uddf/u1:profiledata/u1:repetitiongroup/u1:dive"/> + <xsl:apply-templates select="/uddf/profiledata/repetitiongroup/dive|/u:uddf/u:profiledata/u:repetitiongroup/u:dive|/u1:uddf/u1:profiledata/u1:repetitiongroup/u1:dive|/UDDF/dive"/> </dives> </divelog> </xsl:template> @@ -39,11 +46,24 @@ </xsl:if> </xsl:template> - <xsl:template match="gasdefinitions|u:gasdefinitions|u1:gasdefinitions"> - <xsl:for-each select="mix|u:mix|u1:mix"> + <xsl:template match="modified"> + <xsl:if test="application/name != ''"> + <xsl:attribute name="model"> + <xsl:value-of select="application/name"/> + </xsl:attribute> + </xsl:if> + <xsl:if test="application/version != ''"> + <xsl:attribute name="serial"> + <xsl:value-of select="application/version"/> + </xsl:attribute> + </xsl:if> + </xsl:template> + + <xsl:template match="gasdefinitions|u:gasdefinitions|u1:gasdefinitions|gas_def"> + <xsl:for-each select="mix|u:mix|u1:mix|gas_mix"> <cylinder> <xsl:attribute name="description"> - <xsl:value-of select="name|u:name|u1:name"/> + <xsl:value-of select="name|u:name|u1:name|mixname"/> </xsl:attribute> <xsl:attribute name="o2"> @@ -69,7 +89,7 @@ <dive> <!-- Count the amount of temeprature samples during the dive --> <xsl:variable name="temperatureSamples"> - <xsl:call-template name="temperatureSamples" select="samples/waypoint/temperature|u:samples/u:waypoint/u:temperature|u1:samples/u1:waypoint/u1:temperature"> + <xsl:call-template name="temperatureSamples" select="samples/waypoint/temperature|u:samples/u:waypoint/u:temperature|u1:samples/u1:waypoint/u1:temperature|samples/t"> <xsl:with-param name="units" select="'Kelvin'"/> </xsl:call-template> </xsl:variable> @@ -91,8 +111,90 @@ </xsl:call-template> </xsl:when> </xsl:choose> + <xsl:if test="dive_number != ''"> + <xsl:attribute name="number"> + <xsl:value-of select="dive_number"/> + </xsl:attribute> + </xsl:if> + <xsl:if test="dive_duration != '' and dive_duration != 0"> + <xsl:attribute name="duration"> + <xsl:call-template name="timeConvert"> + <xsl:with-param name="timeSec"> + <xsl:value-of select="dive_duration"/> + </xsl:with-param> + </xsl:call-template> + </xsl:attribute> + </xsl:if> - <xsl:for-each select="lowesttemperature|informationafterdive/lowesttemperature|u:lowesttemperature|u:informationafterdive/u:lowesttemperature|u1:lowesttemperature|u1:informationafterdive/u1:lowesttemperature"> + <xsl:if test="condition/visibility != '' and condition/visibility != 0"> + <xsl:attribute name="visibility"> + <xsl:choose> + <xsl:when test="condition/visibility < 1"> + <xsl:value-of select="1"/> + </xsl:when> + <xsl:when test="condition/visibility <= 3"> + <xsl:value-of select="2"/> + </xsl:when> + <xsl:when test="condition/visibility <= 5"> + <xsl:value-of select="3"/> + </xsl:when> + <xsl:when test="condition/visibility <= 10"> + <xsl:value-of select="4"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="5"/> + </xsl:otherwise> + </xsl:choose> + </xsl:attribute> + </xsl:if> + + <xsl:if test="condition/air_temp != ''"> + <divetemperature> + <xsl:attribute name="air"> + <xsl:value-of select="concat(format-number(condition/air_temp - 273.15, '0.0'), ' C')"/> + </xsl:attribute> + </divetemperature> + </xsl:if> + + <xsl:if test="dive_site_ref/@ref != ''"> + <location> + <xsl:variable name="ref"> + <xsl:value-of select="dive_site_ref/@ref"/> + </xsl:variable> + <xsl:for-each select="//dive_site[@id=$ref]/geography/location|//dive_site[@id=$ref]/name"> + <xsl:value-of select="."/> + <xsl:if test=". != '' and following-sibling::*[1]/* != ''"> / </xsl:if> + </xsl:for-each> + </location> + </xsl:if> + + <xsl:if test="buddy_ref/@ref != ''"> + <buddy> + <xsl:variable name="ref"> + <xsl:value-of select="buddy_ref/@ref"/> + </xsl:variable> + <xsl:for-each select="//diver[@id=$ref]/personal/first_name|//diver[@id=$ref]/personal/nick_name|//diver[@id=$ref]/personal/family_name"> + <xsl:value-of select="."/> + <xsl:if test=". != '' and (following-sibling::*[1] != '' or following-sibling::*[2] != '')"> / </xsl:if> + </xsl:for-each> + </buddy> + </xsl:if> + + <xsl:if test="note/text != ''"> + <notes> + <xsl:value-of select="note/text"/> + </notes> + </xsl:if> + + <xsl:if test="equipment_used/weight_used > 0"> + <weightsystem description="unknown"> + <xsl:attribute name="weight"> + <xsl:value-of select="concat(format-number(equipment_used/weight_used, '0.0'), ' kg')"/> + </xsl:attribute> + </weightsystem> + </xsl:if> + + <xsl:for-each select="lowesttemperature|informationafterdive/lowesttemperature|u:lowesttemperature|u:informationafterdive/u:lowesttemperature|u1:lowesttemperature|u1:informationafterdive/u1:lowesttemperature|condition/water_temp"> <temperature> <xsl:if test="$temperatureSamples > 0 or . != 273.15"> <xsl:attribute name="water"> @@ -104,24 +206,76 @@ <divecomputer deviceid="ffffffff"> <xsl:attribute name="model"> - <xsl:value-of select="/uddf/generator/name|/u:uddf/u:generator/u:name|/u1:uddf/u1:generator/u1:name"/> + <xsl:value-of select="/uddf/generator/name|/u:uddf/u:generator/u:name|/u1:uddf/u1:generator/u1:name|/UDDF/history/modified/application/name"/> </xsl:attribute> </divecomputer> + <xsl:if test="equipment_used/tank_used != ''"> + <xsl:for-each select="equipment_used/tank_used"> + <cylinder> + <xsl:variable name="idx"> + <xsl:value-of select="./tank_ref/@ref"/> + </xsl:variable> + <xsl:attribute name="size"> + <xsl:value-of select="//equipment[@id=$idx]/tank/volume"/> + </xsl:attribute> + <xsl:attribute name="description"> + <xsl:value-of select="//equipment[@id=$idx]/general/name"/> + </xsl:attribute> + <xsl:attribute name="start"> + <xsl:value-of select="concat(substring-before(./pressure_start, '.') div 100000, ' bar')"/> + </xsl:attribute> + <xsl:attribute name="end"> + <xsl:value-of select="concat(substring-before(./pressure_end, '.') div 100000, ' bar')"/> + </xsl:attribute> + </cylinder> + </xsl:for-each> + </xsl:if> + <xsl:apply-templates select="/uddf/gasdefinitions|/u:uddf/u:gasdefinitions|/u1:uddf/u1:gasdefinitions"/> <depth> - <xsl:for-each select="greatestdepth|informationafterdive/greatestdepth|u:greatestdepth|u:informationafterdive/u:greatestdepth|u1:greatestdepth|u1:informationafterdive/u1:greatestdepth"> + <xsl:for-each select="greatestdepth|informationafterdive/greatestdepth|u:greatestdepth|u:informationafterdive/u:greatestdepth|u1:greatestdepth|u1:informationafterdive/u1:greatestdepth|max_depth"> <xsl:attribute name="max"> - <xsl:value-of select="concat(., ' m')"/> + <xsl:value-of select="concat(format-number(., '0.00'), ' m')"/> </xsl:attribute> </xsl:for-each> <xsl:for-each select="averagedepth|informationafterdive/averagedepth|u:averagedepth|u:informationafterdive/u:averagedepth|u1:averagedepth|u1:informationafterdive/u1:averagedepth"> <xsl:attribute name="mean"> - <xsl:value-of select="concat(., ' m')"/> + <xsl:value-of select="concat(format-number(., '0.00'), ' m')"/> </xsl:attribute> </xsl:for-each> </depth> + <!-- Aquadivelog gas switches require more lookups than other UDDF + formats I have seen --> + <xsl:for-each select="samples/switch"> + <xsl:variable name="tank_idx"> + <xsl:value-of select="./@tank"/> + </xsl:variable> + <xsl:variable name="idx"> + <xsl:value-of select="//equipment_used/tank_used[@id=$tank_idx]/gas_ref/@ref"/> + </xsl:variable> + + <event name="gaschange" type="11"> + <xsl:attribute name="time"> + <xsl:call-template name="timeConvert"> + <xsl:with-param name="timeSec"> + <xsl:value-of select="following-sibling::t"/> + </xsl:with-param> + </xsl:call-template> + </xsl:attribute> + + <xsl:attribute name="value"> + <xsl:call-template name="gasConvert"> + <xsl:with-param name="mix"> + <xsl:value-of select="//gas_def/gas_mix[@id=$idx]/o2"/> + </xsl:with-param> + </xsl:call-template> + </xsl:attribute> + </event> + </xsl:for-each> + + <!-- Other gas switches than Aquadivelog --> <xsl:for-each select="samples/waypoint/switchmix|u:samples/u:waypoint/u:switchmix|u1:samples/u1:waypoint/u1:switchmix"> <!-- Index to lookup gas per cent --> <xsl:variable name="idx"> @@ -147,26 +301,33 @@ </event> </xsl:for-each> - <xsl:for-each select="samples/waypoint|u:samples/u:waypoint|u1:samples/u1:waypoint"> + <xsl:for-each select="samples/waypoint|u:samples/u:waypoint|u1:samples/u1:waypoint|samples/d"> <sample> <xsl:attribute name="time"> <xsl:call-template name="timeConvert"> <xsl:with-param name="timeSec"> - <xsl:value-of select="divetime|u:divetime|u1:divetime"/> + <xsl:value-of select="divetime|u:divetime|u1:divetime|preceding-sibling::t[1]"/> </xsl:with-param> </xsl:call-template> </xsl:attribute> - <xsl:if test="depth != ''"> - <xsl:attribute name="depth"> - <xsl:value-of select="concat(depth, ' m')"/> - </xsl:attribute> - </xsl:if> - <xsl:if test="u:depth|u1:depth != ''"> - <xsl:attribute name="depth"> - <xsl:value-of select="concat(u:depth|u1:depth, ' m')"/> - </xsl:attribute> - </xsl:if> + <xsl:choose> + <xsl:when test="depth != ''"> + <xsl:attribute name="depth"> + <xsl:value-of select="concat(format-number(depth, '0.00'), ' m')"/> + </xsl:attribute> + </xsl:when> + <xsl:when test="u:depth|u1:depth != ''"> + <xsl:attribute name="depth"> + <xsl:value-of select="concat(format-number(u:depth|u1:depth, '0.00'), ' m')"/> + </xsl:attribute> + </xsl:when> + <xsl:when test=". != 0"> + <xsl:attribute name="depth"> + <xsl:value-of select="concat(format-number(., '0.00'), ' m')"/> + </xsl:attribute> + </xsl:when> + </xsl:choose> <xsl:if test="temperature != '' and $temperatureSamples > 0"> <xsl:attribute name="temperature"> |