diff options
Diffstat (limited to 'qt-ui')
-rw-r--r-- | qt-ui/css/tableviews.css | 28 | ||||
-rw-r--r-- | qt-ui/diveplanner.cpp | 718 | ||||
-rw-r--r-- | qt-ui/diveplanner.h | 92 | ||||
-rw-r--r-- | qt-ui/diveplanner.ui | 135 | ||||
-rw-r--r-- | qt-ui/downloadfromdivecomputer.cpp | 190 | ||||
-rw-r--r-- | qt-ui/downloadfromdivecomputer.h | 43 | ||||
-rw-r--r-- | qt-ui/graphicsview-common.cpp | 5 | ||||
-rw-r--r-- | qt-ui/graphicsview-common.h | 1 | ||||
-rw-r--r-- | qt-ui/maintab.cpp | 130 | ||||
-rw-r--r-- | qt-ui/maintab.h | 10 | ||||
-rw-r--r-- | qt-ui/maintab.ui | 134 | ||||
-rw-r--r-- | qt-ui/mainwindow.cpp | 99 | ||||
-rw-r--r-- | qt-ui/mainwindow.h | 6 | ||||
-rw-r--r-- | qt-ui/mainwindow.ui | 57 | ||||
-rw-r--r-- | qt-ui/modeldelegates.cpp | 35 | ||||
-rw-r--r-- | qt-ui/modeldelegates.h | 9 | ||||
-rw-r--r-- | qt-ui/models.cpp | 35 | ||||
-rw-r--r-- | qt-ui/models.h | 1 | ||||
-rw-r--r-- | qt-ui/preferences.ui | 2 | ||||
-rw-r--r-- | qt-ui/tableview.cpp | 90 | ||||
-rw-r--r-- | qt-ui/tableview.h | 45 | ||||
-rw-r--r-- | qt-ui/tableview.ui | 63 |
22 files changed, 1352 insertions, 576 deletions
diff --git a/qt-ui/css/tableviews.css b/qt-ui/css/tableviews.css new file mode 100644 index 000000000..4e8396886 --- /dev/null +++ b/qt-ui/css/tableviews.css @@ -0,0 +1,28 @@ + 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); + } + diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp index 95b6c211c..ceb5ebdba 100644 --- a/qt-ui/diveplanner.cpp +++ b/qt-ui/diveplanner.cpp @@ -1,34 +1,54 @@ #include "diveplanner.h" #include "graphicsview-common.h" +#include "models.h" +#include "modeldelegates.h" +#include "ui_diveplanner.h" +#include "mainwindow.h" +#include "tableview.h" +#include "graphicsview-common.h" #include "../dive.h" #include "../divelist.h" -#include <cmath> +#include "../planner.h" + #include <QMouseEvent> #include <QDebug> -#include <QGraphicsWidget> -#include <QGraphicsProxyWidget> -#include <QPushButton> #include <QGraphicsSceneMouseEvent> #include <QMessageBox> #include <QStringListModel> -#include <QGraphicsProxyWidget> #include <QListView> -#include <QDesktopWidget> #include <QModelIndex> - -#include "ui_diveplanner.h" -#include "mainwindow.h" +#include <QSettings> +#include <QTableView> +#include <QColor> #define TIME_INITIAL_MAX 30 #define MAX_DEEPNESS 150 #define MIN_DEEPNESS 40 -bool handlerLessThenMinutes(DiveHandler *d1, DiveHandler *d2){ - return d1->sec < d2->sec; +QStringListModel *airTypes(){ + static QStringListModel *self = new QStringListModel(QStringList() + << QObject::tr("AIR") + << QObject::tr("EAN32") + << QObject::tr("EAN36")); + return self; +} + +QString strForAir(const divedatapoint& p){ + return p.o2 == 209 ? QObject::tr("AIR") + : p.o2 == 320 ? QObject::tr("EAN32") + : p.o2 == 360 ? QObject::tr("EAN36") + : QObject::tr("Choose Gas"); } +QColor getColor(const color_indice_t i) +{ + return profile_color[i].at(0); +} + +static DivePlannerPointsModel *plannerModel = DivePlannerPointsModel::instance(); + DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent), activeDraggedHandler(0) { fill_profile_color(); @@ -53,6 +73,7 @@ DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent) fromPercent(100, Qt::Horizontal), fromPercent(0, Qt::Vertical) ); + horizontalLine->setPen(QPen(Qt::DotLine)); scene()->addItem(horizontalLine); @@ -60,6 +81,7 @@ DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent) timeLine->setMinimum(0); timeLine->setMaximum(TIME_INITIAL_MAX); timeLine->setTickInterval(10); + timeLine->setColor(getColor(TIME_GRID)); timeLine->setLine( fromPercent(10, Qt::Horizontal), fromPercent(90, Qt::Vertical), @@ -68,7 +90,7 @@ DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent) ); timeLine->setOrientation(Qt::Horizontal); timeLine->setTickSize(fromPercent(1, Qt::Vertical)); - timeLine->setColor(profile_color[TIME_GRID].at(0)); + timeLine->setTextColor(getColor(TIME_TEXT)); timeLine->updateTicks(); scene()->addItem(timeLine); @@ -84,7 +106,8 @@ DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent) ); depthLine->setOrientation(Qt::Vertical); depthLine->setTickSize(fromPercent(1, Qt::Horizontal)); - depthLine->setColor(profile_color[DEPTH_GRID].at(0)); + depthLine->setColor(getColor(DEPTH_GRID)); + depthLine->setTextColor(getColor(SAMPLE_DEEP)); depthLine->updateTicks(); scene()->addItem(depthLine); @@ -111,16 +134,10 @@ DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent) connect(obj, SIGNAL(clicked()), this, SLOT(slot)); ADDBTN(plusDepth, ":plus", "" , 5, 5, tr("Increase maximum depth by 10m"), increaseDepth()); - ADDBTN(plusTime, ":plus", "" , 95, 5, tr("Increase minimum time by 10m"), increaseTime()); + ADDBTN(plusTime, ":plus", "" , 95, 95, tr("Increase minimum time by 10m"), increaseTime()); ADDBTN(lessDepth, ":minimum","" , 2, 5, tr("Decreases maximum depth by 10m"), decreaseDepth()); ADDBTN(lessTime, ":minimum","" , 92, 95, tr("Decreases minimum time by 10m"), decreaseTime()); - ADDBTN(okBtn, "", tr("Ok"), 1, 95, "", okClicked()); - ADDBTN(cancelBtn, "", tr("Cancel"), 0,0, "", cancelClicked()); #undef ADDBTN - - cancelBtn->setPos(okBtn->pos().x() + okBtn->boundingRect().width() - + fromPercent(2, Qt::Horizontal), fromPercent(95, Qt::Vertical)); - minMinutes = TIME_INITIAL_MAX; QAction *action = NULL; @@ -141,30 +158,50 @@ DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent) #undef ADD_ACTION // Prepare the stuff for the gas-choices. - gasChoices = new QStringListModel(QStringList() << tr("AIR") << tr("EAN32") << tr("EAN36")); gasListView = new QListView(); - gasListView->setWindowFlags(Qt::FramelessWindowHint); - gasListView->setModel(gasChoices); + gasListView->setWindowFlags(Qt::Popup); + gasListView->setModel(airTypes()); gasListView->hide(); connect(gasListView, SIGNAL(activated(QModelIndex)), this, SLOT(selectGas(QModelIndex))); + connect(plannerModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(createDecoStops())); + + connect(plannerModel, SIGNAL(rowsInserted(const QModelIndex&,int,int)), + this, SLOT(pointInserted(const QModelIndex&, int, int))); + connect(plannerModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), + this, SLOT(pointsRemoved(const QModelIndex&, int, int))); setRenderHint(QPainter::Antialiasing); } +void DivePlannerGraphics::pointInserted(const QModelIndex& parent, int start , int end) +{ + DiveHandler *item = new DiveHandler (); + scene()->addItem(item); + handles << item; + + Button *gasChooseBtn = new Button(); + scene()->addItem(gasChooseBtn); + gasChooseBtn->setZValue(10); + connect(gasChooseBtn, SIGNAL(clicked()), this, SLOT(prepareSelectGas())); + + gases << gasChooseBtn; + createDecoStops(); +} + void DivePlannerGraphics::keyDownAction() { if(scene()->selectedItems().count()){ Q_FOREACH(QGraphicsItem *i, scene()->selectedItems()){ if (DiveHandler *handler = qgraphicsitem_cast<DiveHandler*>(i)){ - if (handler->mm / 1000 >= depthLine->maximum()) + int row = handles.indexOf(handler); + divedatapoint dp = plannerModel->at(row); + if (dp.depth / 1000 >= depthLine->maximum()) continue; - handler->mm += 1000; - double ypos = depthLine->posAtValue(handler->mm / 1000); - handler->setPos(handler->pos().x(), ypos); + dp.depth += 1000; + plannerModel->editStop(row, dp); } } - createDecoStops(); } } @@ -172,12 +209,14 @@ void DivePlannerGraphics::keyUpAction() { Q_FOREACH(QGraphicsItem *i, scene()->selectedItems()){ if (DiveHandler *handler = qgraphicsitem_cast<DiveHandler*>(i)){ - if (handler->mm / 1000 <= 0) + int row = handles.indexOf(handler); + divedatapoint dp = plannerModel->at(row); + + if (dp.depth / 1000 <= 0) continue; - handler->mm -= 1000; - double ypos = depthLine->posAtValue(handler->mm / 1000); - handler->setPos(handler->pos().x(), ypos); + dp.depth -= 1000; + plannerModel->editStop(row, dp); } } createDecoStops(); @@ -187,12 +226,15 @@ void DivePlannerGraphics::keyLeftAction() { Q_FOREACH(QGraphicsItem *i, scene()->selectedItems()){ if (DiveHandler *handler = qgraphicsitem_cast<DiveHandler*>(i)){ - if (handler->sec / 60 <= 0) + int row = handles.indexOf(handler); + divedatapoint dp = plannerModel->at(row); + + if (dp.time / 60 <= 0) continue; // don't overlap positions. // maybe this is a good place for a 'goto'? - double xpos = timeLine->posAtValue((handler->sec - 60) / 60); + double xpos = timeLine->posAtValue((dp.time - 60) / 60); bool nextStep = false; Q_FOREACH(DiveHandler *h, handles){ if (h->pos().x() == xpos){ @@ -203,23 +245,24 @@ void DivePlannerGraphics::keyLeftAction() if(nextStep) continue; - handler->sec -= 60; - handler->setPos(xpos, handler->pos().y()); + dp.time -= 60; + plannerModel->editStop(row, dp); } } - createDecoStops(); } void DivePlannerGraphics::keyRightAction() { Q_FOREACH(QGraphicsItem *i, scene()->selectedItems()){ if (DiveHandler *handler = qgraphicsitem_cast<DiveHandler*>(i)){ - if (handler->sec / 60 >= timeLine->maximum()) + int row = handles.indexOf(handler); + divedatapoint dp = plannerModel->at(row); + if (dp.time / 60 >= timeLine->maximum()) continue; // don't overlap positions. // maybe this is a good place for a 'goto'? - double xpos = timeLine->posAtValue((handler->sec + 60) / 60); + double xpos = timeLine->posAtValue((dp.time + 60) / 60); bool nextStep = false; Q_FOREACH(DiveHandler *h, handles){ if (h->pos().x() == xpos){ @@ -230,32 +273,52 @@ void DivePlannerGraphics::keyRightAction() if(nextStep) continue; - handler->sec += 60; - handler->setPos(xpos, handler->pos().y()); + dp.time += 60; + plannerModel->editStop(row, dp); } - } createDecoStops(); + } } void DivePlannerGraphics::keyDeleteAction() { int selCount = scene()->selectedItems().count(); if(selCount){ - - while(selCount--){ - Button *btn = gases.takeLast(); - delete btn; - } - + QVector<int> selectedIndexes; Q_FOREACH(QGraphicsItem *i, scene()->selectedItems()){ if (DiveHandler *handler = qgraphicsitem_cast<DiveHandler*>(i)){ - handles.removeAll(handler); - scene()->removeItem(handler); - delete i; + selectedIndexes.push_back(handles.indexOf(handler)); } } + plannerModel->removeSelectedPoints(selectedIndexes); + } +} - createDecoStops(); +void DivePlannerGraphics::pointsRemoved(const QModelIndex& , int start, int end) +{ // start and end are inclusive. + int num = (end - start) + 1; + for(int i = num; i != 0; i--){ + delete handles.back(); + handles.pop_back(); + delete gases.back(); + gases.pop_back(); } + scene()->clearSelection(); + createDecoStops(); +} + +bool intLessThan(int a, int b){ + return a <= b; +} +void DivePlannerPointsModel::removeSelectedPoints(const QVector< int >& rows) +{ + int firstRow = rowCount() - rows.count(); + QVector<int> v2 = rows; + std::sort(v2.begin(), v2.end(), intLessThan); + beginRemoveRows(QModelIndex(), firstRow, rowCount()-1); + for(int i = v2.count()-1; i >= 0; i--){ + divepoints.remove(v2[i]); + } + endRemoveRows(); } void DivePlannerGraphics::keyEscAction() @@ -264,8 +327,7 @@ void DivePlannerGraphics::keyEscAction() scene()->clearSelection(); return; } - - cancelClicked(); + plannerModel->cancelPlan(); } qreal DivePlannerGraphics::fromPercent(qreal percent, Qt::Orientation orientation) @@ -275,23 +337,6 @@ qreal DivePlannerGraphics::fromPercent(qreal percent, Qt::Orientation orientatio return result; } -void DivePlannerGraphics::cancelClicked() -{ - if (handles.size()){ - if (QMessageBox::warning(mainWindow(), tr("Save the Plan?"), - tr("You have a working plan, \n are you sure that you wanna cancel it?"), - QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok){ - return; - } - } - mainWindow()->showProfile(); -} - -void DivePlannerGraphics::okClicked() -{ - // todo. -} - void DivePlannerGraphics::increaseDepth() { if (depthLine->maximum() + 10 > MAX_DEEPNESS) @@ -323,7 +368,6 @@ void DivePlannerGraphics::decreaseDepth() return; } } - depthLine->setMaximum(depthLine->maximum() - 10); depthLine->updateTicks(); createDecoStops(); @@ -352,31 +396,7 @@ void DivePlannerGraphics::mouseDoubleClickEvent(QMouseEvent* event) int minutes = rint(timeLine->valueAt(mappedPos)); int meters = rint(depthLine->valueAt(mappedPos)); - double xpos = timeLine->posAtValue(minutes); - double ypos = depthLine->posAtValue(meters); - Q_FOREACH(DiveHandler* handler, handles){ - if (xpos == handler->pos().x()){ - qDebug() << "There's already an point at that place."; - //TODO: Move this later to a KMessageWidget. - return; - } - } - - DiveHandler *item = new DiveHandler (); - item->sec = minutes * 60; - item->mm = meters * 1000; - item->setPos(QPointF(xpos, ypos)); - scene()->addItem(item); - handles << item; - - Button *gasChooseBtn = new Button(); - gasChooseBtn ->setText(tr("Air")); - scene()->addItem(gasChooseBtn); - gasChooseBtn->setZValue(10); - connect(gasChooseBtn, SIGNAL(clicked()), this, SLOT(prepareSelectGas())); - - gases << gasChooseBtn; - createDecoStops(); + plannerModel->addStop(meters * 1000, minutes * 60, tr("Air"), 0); } void DivePlannerGraphics::prepareSelectGas() @@ -390,77 +410,40 @@ void DivePlannerGraphics::prepareSelectGas() void DivePlannerGraphics::selectGas(const QModelIndex& index) { QString gasSelected = gasListView->model()->data(index, Qt::DisplayRole).toString(); - currentGasChoice->setText(gasSelected); + int idx = gases.indexOf(currentGasChoice); + plannerModel->setData(plannerModel->index(idx, DivePlannerPointsModel::GAS), gasSelected); gasListView->hide(); } - void DivePlannerGraphics::createDecoStops() { qDeleteAll(lines); lines.clear(); - qSort(handles.begin(), handles.end(), handlerLessThenMinutes); - - // This needs to be done in the following steps: - // Get the user-input and calculate the dive info - // Not sure if this is the place to create the diveplan... - // We just start with a surface node at time = 0 - struct diveplan diveplan; - struct divedatapoint *dp = create_dp(0, 0, 209, 0, 0); - dp->entered = TRUE; - diveplan.dp = dp; - diveplan.gflow = 30; - diveplan.gfhigh = 70; - diveplan.surface_pressure = 1013; - DiveHandler *lastH = NULL; - Q_FOREACH(DiveHandler *h, handles) { - // these values need to come from the planner UI, eventually - int o2 = 209; - int he = 0; - int po2 = 0; - int deltaT = lastH ? h->sec - lastH->sec : h->sec; - lastH = h; - dp = plan_add_segment(&diveplan, deltaT, h->mm, o2, he, po2); - dp->entered = TRUE; - qDebug("time %d, depth %d", h->sec, h->mm); - } -#if DEBUG_PLAN - dump_plan(&diveplan); -#endif - char *cache = NULL; - struct dive *dive = NULL; - char *errorString = NULL; - plan(&diveplan, &cache, &dive, &errorString); -#if DEBUG_PLAN - dump_plan(&diveplan); -#endif - while(dp->next) + plannerModel->createTemporaryPlan(); + struct diveplan diveplan = plannerModel->getDiveplan(); + struct divedatapoint *dp = diveplan.dp; + while(dp->next){ dp = dp->next; + } - dpMaxTime = dp->time / 60.0 + 5; - - if (timeLine->maximum() < dp->time / 60.0 + 5 || - dp->time / 60.0 + 15 < timeLine->maximum()) { + if (timeLine->maximum() < dp->time / 60.0 + 5 || dp->time / 60.0 + 15 < timeLine->maximum()) { double newMax = fmax(dp->time / 60.0 + 5, minMinutes); timeLine->setMaximum(newMax); timeLine->updateTicks(); } // Re-position the user generated dive handlers - Q_FOREACH(DiveHandler *h, handles){ - h->setPos(timeLine->posAtValue(h->sec / 60), depthLine->posAtValue(h->mm / 1000)); - } - - int gasCount = gases.count(); - for(int i = 0; i < gasCount; i++){ + for(int i = 0; i < plannerModel->rowCount(); i++){ + divedatapoint dp = plannerModel->at(i); + DiveHandler *h = handles.at(i); + h->setPos(timeLine->posAtValue(dp.time / 60), depthLine->posAtValue(dp.depth / 1000)); QPointF p1 = (i == 0) ? QPointF(timeLine->posAtValue(0), depthLine->posAtValue(0)) : handles[i-1]->pos(); QPointF p2 = handles[i]->pos(); - QLineF line(p1, p2); QPointF pos = line.pointAt(0.5); gases[i]->setPos(pos); - qDebug() << "Adding a gas at" << pos; + gases[i]->setText( strForAir(dp)); } // (re-) create the profile with different colors for segments that were @@ -470,6 +453,7 @@ void DivePlannerGraphics::createDecoStops() QPolygonF poly; poly.append(QPointF(lastx, lasty)); + for (dp = diveplan.dp; dp != NULL; dp = dp->next) { double xpos = timeLine->posAtValue(dp->time / 60.0); double ypos = depthLine->posAtValue(dp->depth / 1000.0); @@ -497,16 +481,7 @@ void DivePlannerGraphics::createDecoStops() pat.setColorAt(0, profile_color[DEPTH_TOP].first()); diveBg->setBrush(pat); - deleteTemporaryDivePlan(diveplan.dp); - delete_single_dive(get_divenr(dive)); -} - -void DivePlannerGraphics::deleteTemporaryDivePlan(divedatapoint* dp) -{ - if (!dp) - return; - deleteTemporaryDivePlan(dp->next); - free(dp); + plannerModel->deleteTemporaryPlan(); } void DivePlannerGraphics::resizeEvent(QResizeEvent* event) @@ -619,8 +594,14 @@ void DivePlannerGraphics::mouseReleaseEvent(QMouseEvent* event) } } - activeDraggedHandler->sec = rint(timeLine->valueAt(mappedPos)) * 60; - activeDraggedHandler->mm = rint(depthLine->valueAt(mappedPos)) * 1000; + int pos = handles.indexOf(activeDraggedHandler); + divedatapoint data = plannerModel->at(pos); + + data.depth = rint(depthLine->valueAt(mappedPos)) * 1000; + data.time = rint(timeLine->valueAt(mappedPos)) * 60; + + plannerModel->editStop(pos, data); + activeDraggedHandler->setBrush(QBrush(Qt::white)); activeDraggedHandler->setPos(QPointF(xpos, ypos)); @@ -645,6 +626,10 @@ void DiveHandler::mousePressEvent(QGraphicsSceneMouseEvent* event) } // mousePressEvent 'grabs' the mouse and keyboard, annoying. ungrabMouse(); + + /* hack. Sometimes the keyboard is grabbed, sometime it's not, + so, let's force a grab and release, to get rid of a warning. */ + grabKeyboard(); ungrabKeyboard(); } @@ -658,6 +643,11 @@ void Ruler::setMinimum(double minimum) min = minimum; } +void Ruler::setTextColor(const QColor& color) +{ + textColor = color; +} + Ruler::Ruler() : orientation(Qt::Horizontal) { } @@ -665,39 +655,70 @@ Ruler::Ruler() : orientation(Qt::Horizontal) void Ruler::setOrientation(Qt::Orientation o) { orientation = o; + // position the elements on the screen. + setMinimum(minimum()); + setMaximum(maximum()); } void Ruler::updateTicks() { qDeleteAll(ticks); ticks.clear(); + qDeleteAll(labels); + labels.clear(); + QLineF m = line(); QGraphicsLineItem *item = NULL; + QGraphicsSimpleTextItem *label = NULL; + + double steps = (max - min) / interval; + qreal pos; + double currValue = min; if (orientation == Qt::Horizontal) { - double steps = (max - min) / interval; double stepSize = (m.x2() - m.x1()) / steps; - qreal pos; - for (pos = m.x1(); pos < m.x2(); pos += stepSize) { + for (pos = m.x1(); pos < m.x2(); pos += stepSize, currValue += interval) { item = new QGraphicsLineItem(pos, m.y1(), pos, m.y1() + tickSize, this); item->setPen(pen()); ticks.push_back(item); + + label = new QGraphicsSimpleTextItem(QString::number(currValue), this); + label->setBrush(QBrush(textColor)); + label->setFlag(ItemIgnoresTransformations); + label->setPos(pos - label->boundingRect().width()/2, m.y1() + tickSize + 5); + labels.push_back(label); } item = new QGraphicsLineItem(pos, m.y1(), pos, m.y1() + tickSize, this); item->setPen(pen()); ticks.push_back(item); + + label = new QGraphicsSimpleTextItem(QString::number(currValue), this); + label->setBrush(QBrush(textColor)); + label->setFlag(ItemIgnoresTransformations); + label->setPos(pos - label->boundingRect().width()/2, m.y1() + tickSize + 5); + labels.push_back(label); } else { - double steps = (max - min) / interval; double stepSize = (m.y2() - m.y1()) / steps; - qreal pos; - for (pos = m.y1(); pos < m.y2(); pos += stepSize) { + for (pos = m.y1(); pos < m.y2(); pos += stepSize, currValue += interval) { item = new QGraphicsLineItem(m.x1(), pos, m.x1() - tickSize, pos, this); item->setPen(pen()); ticks.push_back(item); + + label = new QGraphicsSimpleTextItem(QString::number(currValue), this); + label->setBrush(QBrush(textColor)); + label->setFlag(ItemIgnoresTransformations); + label->setPos(m.x2() - 80, pos); + labels.push_back(label); } item = new QGraphicsLineItem(m.x1(), pos, m.x1() - tickSize, pos, this); item->setPen(pen()); ticks.push_back(item); + + label = new QGraphicsSimpleTextItem(QString::number(currValue), this); + label->setBrush(QBrush(textColor)); + label->setFlag(ItemIgnoresTransformations); + label->setPos(m.x2() - 80, pos); + labels.push_back(label); } } @@ -757,12 +778,17 @@ double Ruler::minimum() const void Ruler::setColor(const QColor& color) { - setPen(QPen(color)); + QPen defaultPen(color); + defaultPen.setJoinStyle(Qt::RoundJoin); + defaultPen.setCapStyle(Qt::RoundCap); + defaultPen.setWidth(2); + defaultPen.setCosmetic(true); + setPen(defaultPen); } Button::Button(QObject* parent): QObject(parent), QGraphicsRectItem() { - icon = new QGraphicsPixmapItem(this); + icon = new QGraphicsPixmapItem(this); text = new QGraphicsSimpleTextItem(this); icon->setPos(0,0); text->setPos(0,0); @@ -799,3 +825,361 @@ void Button::mousePressEvent(QGraphicsSceneMouseEvent* event) event->ignore(); emit clicked(); } + +DivePlannerWidget::DivePlannerWidget(QWidget* parent, Qt::WindowFlags f): QWidget(parent, f), ui(new Ui::DivePlanner()) +{ + ui->setupUi(this); + ui->tableWidget->setTitle(tr("Dive Planner Points")); + ui->tableWidget->setModel(DivePlannerPointsModel::instance()); + ui->tableWidget->view()->setItemDelegateForColumn(DivePlannerPointsModel::GAS, new AirTypesDelegate(this)); + + connect(ui->tableWidget, SIGNAL(addButtonClicked()), DivePlannerPointsModel::instance(), SLOT(addStop())); + connect(ui->startTime, SIGNAL(timeChanged(QTime)), this, SLOT(startTimeChanged(QTime))); + connect(ui->ATMPressure, SIGNAL(textChanged(QString)), this, SLOT(atmPressureChanged(QString))); + connect(ui->bottomSAC, SIGNAL(textChanged(QString)), this, SLOT(bottomSacChanged(QString))); + connect(ui->decoStopSAC, SIGNAL(textChanged(QString)), this, SLOT(decoSacChanged(QString))); + connect(ui->highGF, SIGNAL(textChanged(QString)), this, SLOT(gfhighChanged(QString))); + connect(ui->lowGF, SIGNAL(textChanged(QString)), this, SLOT(gflowChanged(QString))); + connect(ui->highGF, SIGNAL(textChanged(QString)), this, SLOT(gfhighChanged(QString))); + connect(ui->lastStop, SIGNAL(toggled(bool)), this, SLOT(lastStopChanged(bool))); + + // Creating the plan + connect(ui->buttonBox, SIGNAL(accepted()), plannerModel, SLOT(createPlan())); + connect(ui->buttonBox, SIGNAL(rejected()), plannerModel, SLOT(cancelPlan())); + connect(plannerModel, SIGNAL(planCreated()), mainWindow(), SLOT(showProfile())); + connect(plannerModel, SIGNAL(planCreated()), mainWindow(), SLOT(refreshDisplay())); + connect(plannerModel, SIGNAL(planCanceled()), mainWindow(), SLOT(showProfile())); + + /* set defaults. */ + ui->startTime->setTime( QTime(1, 0) ); + ui->ATMPressure->setText( "1013" ); + ui->bottomSAC->setText("20"); + ui->decoStopSAC->setText("17"); + ui->lowGF->setText("30"); + ui->highGF->setText("75"); +} + +void DivePlannerWidget::startTimeChanged(const QTime& time) +{ + plannerModel->setStartTime(time); +} + +void DivePlannerWidget::atmPressureChanged(const QString& pressure) +{ + plannerModel->setSurfacePressure(pressure.toInt()); +} + +void DivePlannerWidget::bottomSacChanged(const QString& bottomSac) +{ + plannerModel->setBottomSac(bottomSac.toInt()); +} + +void DivePlannerWidget::decoSacChanged(const QString& decosac) +{ + plannerModel->setDecoSac(decosac.toInt()); +} + +void DivePlannerWidget::gfhighChanged(const QString& gfhigh) +{ + plannerModel->setGFHigh(gfhigh.toShort()); +} + +void DivePlannerWidget::gflowChanged(const QString& gflow) +{ + plannerModel->setGFLow(gflow.toShort()); +} + +void DivePlannerWidget::lastStopChanged(bool checked) +{ + plannerModel->setLastStop6m(checked); +} + +int DivePlannerPointsModel::columnCount(const QModelIndex& parent) const +{ + return COLUMNS; +} + +QVariant DivePlannerPointsModel::data(const QModelIndex& index, int role) const +{ + if(role == Qt::DisplayRole){ + divedatapoint p = divepoints.at(index.row()); + switch(index.column()){ + case CCSETPOINT: return p.po2; + case DEPTH: return p.depth / 1000; + case DURATION: return p.time / 60; + case GAS: return strForAir(p); + } + } + else if (role == Qt::DecorationRole){ + switch(index.column()){ + case REMOVE : return QIcon(":trash"); + } + } + else if (role == Qt::FontRole){ + return defaultModelFont(); + } + return QVariant(); +} + +bool DivePlannerPointsModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if(role == Qt::EditRole){ + divedatapoint& p = divepoints[index.row()]; + switch(index.column()){ + case DEPTH: p.depth = value.toInt() * 1000; break; + case DURATION: p.time = value.toInt() * 60; break; + case CCSETPOINT:{ + int po2 = 0; + QByteArray gasv = value.toByteArray(); + if (validate_po2(gasv.data(), &po2)) + p.po2 = po2; + } break; + case GAS: { + int o2 = 0; + int he = 0; + QByteArray gasv = value.toByteArray(); + if (validate_gas(gasv.data(), &o2, &he)) { + p.o2 = o2; + p.he = he; + }break; + } + } + editStop(index.row(), p); + } + return QAbstractItemModel::setData(index, value, role); +} + +QVariant DivePlannerPointsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal){ + switch(section){ + case DEPTH: return tr("Final Depth"); + case DURATION: return tr("Duration"); + case GAS: return tr("Used Gas"); + case CCSETPOINT: return tr("CC Set Point"); + } + } + else if (role == Qt::FontRole){ + return defaultModelFont(); + } + return QVariant(); +} + +Qt::ItemFlags DivePlannerPointsModel::flags(const QModelIndex& index) const +{ + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +int DivePlannerPointsModel::rowCount(const QModelIndex& parent) const +{ + return divepoints.count(); +} + +DivePlannerPointsModel::DivePlannerPointsModel(QObject* parent): QAbstractTableModel(parent) +{ +} + +DivePlannerPointsModel* DivePlannerPointsModel::instance() +{ + static DivePlannerPointsModel* self = new DivePlannerPointsModel(); + return self; +} + +void DivePlannerPointsModel::setBottomSac(int sac) +{ + diveplan.bottomsac = sac; + emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, COLUMNS-1)); +} + +void DivePlannerPointsModel::setDecoSac(int sac) +{ + diveplan.decosac = sac; + emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, COLUMNS-1)); +} + +void DivePlannerPointsModel::setGFHigh(short int gfhigh) +{ + diveplan.gfhigh = gfhigh; + emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, COLUMNS-1)); +} + +void DivePlannerPointsModel::setGFLow(short int ghflow) +{ + diveplan.gflow = ghflow; + emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, COLUMNS-1)); +} + +void DivePlannerPointsModel::setSurfacePressure(int pressure) +{ + diveplan.surface_pressure = pressure; + emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, COLUMNS-1)); +} + +void DivePlannerPointsModel::setLastStop6m(bool value) +{ +} + +void DivePlannerPointsModel::setStartTime(const QTime& t) +{ + diveplan.when = t.msec(); + emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, COLUMNS-1)); +} + +bool divePointsLessThan(const divedatapoint& p1, const divedatapoint& p2){ + return p1.time <= p2.time; +} +int DivePlannerPointsModel::addStop(int meters, int minutes, const QString& gas, int ccpoint) +{ + int row = divepoints.count(); + if(meters == 0 && minutes == 0){ + if(row == 0){ + meters = 10000; + minutes = 600; + } + else{ + divedatapoint p = at(row-1); + meters = p.depth; + minutes = p.time + 600; + } + } + + // check if there's already a new stop before this one: + for(int i = 0; i < divepoints.count(); i++){ + const divedatapoint& dp = divepoints.at(i); + if (dp.time > minutes ){ + row = i; + break; + } + } + + // add the new stop + beginInsertRows(QModelIndex(), row, row); + divedatapoint point; + point.depth = meters; + point.time = minutes; + if (row == 0){ + point.o2 = 209; + point.he = 0; + point.po2 = 0; + }else{ + divedatapoint before = at(row-1); + point.o2 = before.o2; + point.he = before.he; + point.po2 = 0; + } + divepoints.append( point ); + std::sort(divepoints.begin(), divepoints.end(), divePointsLessThan); + endInsertRows(); + return row; +} + +void DivePlannerPointsModel::editStop(int row, divedatapoint newData) +{ + divepoints[row] = newData; + std::sort(divepoints.begin(), divepoints.end(), divePointsLessThan); + emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, COLUMNS-1)); +} + +divedatapoint DivePlannerPointsModel::at(int row) +{ + return divepoints.at(row); +} + +void DivePlannerPointsModel::remove(const QModelIndex& index) +{ + if (index.column() != REMOVE) + return; + + beginRemoveRows(QModelIndex(), index.row(), index.row()); + divepoints.remove(index.row()); + endRemoveRows(); +} + +struct diveplan DivePlannerPointsModel::getDiveplan() +{ + return diveplan; +} + +void DivePlannerPointsModel::cancelPlan() +{ + if(rowCount()){ + if (QMessageBox::warning(mainWindow(), tr("Save the Plan?"), + tr("You have a working plan, \n are you sure that you wanna cancel it?"), + QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok){ + return; + } + } + + beginRemoveRows(QModelIndex(), 0, rowCount()-1); + divepoints.clear(); + endRemoveRows(); + emit planCanceled(); +} + +void DivePlannerPointsModel::createTemporaryPlan() +{ + // This needs to be done in the following steps: + // Get the user-input and calculate the dive info + // Not sure if this is the place to create the diveplan... + // We just start with a surface node at time = 0 + struct divedatapoint *dp = create_dp(0, 0, 209, 0, 0); + dp->entered = TRUE; + diveplan.dp = dp; + int lastIndex = -1; + for(int i = 0; i < rowCount(); i++){ + divedatapoint p = at(i); + int deltaT = lastIndex != -1 ? p.time - at(lastIndex).time : p.time; + lastIndex = i; + dp = plan_add_segment(&diveplan, deltaT, p.depth, p.o2, p.he, p.po2); + } +#if DEBUG_PLAN + dump_plan(&diveplan); +#endif + char *cache = NULL; + tempDive = NULL; + char *errorString = NULL; + plan(&diveplan, &cache, &tempDive, &errorString); +#if DEBUG_PLAN + dump_plan(&diveplan); +#endif +} + +void DivePlannerPointsModel::deleteTemporaryPlan() +{ + deleteTemporaryPlan(diveplan.dp); + delete_single_dive(get_divenr(tempDive)); + tempDive = NULL; +} + +void DivePlannerPointsModel::deleteTemporaryPlan(struct divedatapoint *dp) +{ + if (!dp){ + return; + } + + deleteTemporaryPlan(dp->next); + free(dp); +} + +void DivePlannerPointsModel::createPlan() +{ + // Ok, so, here the diveplan creates a dive, + // puts it on the dive list, and we need to remember + // to not delete it later. mumble. ;p + char *cache = NULL; + tempDive = NULL; + char *errorString = NULL; + + createTemporaryPlan(); + plan(&diveplan, &cache, &tempDive, &errorString); + mark_divelist_changed(TRUE); + + // Remove and clean the diveplan, so we don't delete + // the dive by mistake. + diveplan.dp = NULL; + beginRemoveRows(QModelIndex(), 0, rowCount() -1 ); + divepoints.clear(); + endRemoveRows(); + + planCreated(); +} diff --git a/qt-ui/diveplanner.h b/qt-ui/diveplanner.h index 2239e013d..8dd8db3e3 100644 --- a/qt-ui/diveplanner.h +++ b/qt-ui/diveplanner.h @@ -4,11 +4,68 @@ #include <QGraphicsView> #include <QGraphicsPathItem> #include <QDialog> +#include <QAbstractTableModel> +#include <QDateTime> + +#include "dive.h" + +namespace Ui{ + class DivePlanner; +}; class QListView; class QStringListModel; class QModelIndex; +// Return a Model containing the air types. +QStringListModel *airTypes(); + +class DivePlannerPointsModel : public QAbstractTableModel{ + Q_OBJECT +public: + static DivePlannerPointsModel* instance(); + enum Sections{REMOVE, DEPTH, DURATION, GAS, CCSETPOINT, COLUMNS}; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + virtual Qt::ItemFlags flags(const QModelIndex& index) const; + void removeSelectedPoints(const QVector<int>& rows); + + /** + * @return the row number. + */ + void editStop(int row, divedatapoint newData ); + divedatapoint at(int row); + struct diveplan getDiveplan(); +public slots: + int addStop(int meters = 0, int minutes = 0,const QString& gas = QString(), int ccpoint = 0 ); + void setGFHigh(short gfhigh); + void setGFLow(short ghflow); + void setSurfacePressure(int pressure); + void setBottomSac(int sac); + void setDecoSac(int sac); + void setStartTime(const QTime& t); + void setLastStop6m(bool value); + void createPlan(); + void remove(const QModelIndex& index); + void cancelPlan(); + void createTemporaryPlan(); + void deleteTemporaryPlan(); + +signals: + void planCreated(); + void planCanceled(); + +private: + explicit DivePlannerPointsModel(QObject* parent = 0); + struct diveplan diveplan; + QVector<divedatapoint> divepoints; + struct dive *tempDive; + void deleteTemporaryPlan(struct divedatapoint *dp); +}; + class Button : public QObject, public QGraphicsRectItem { Q_OBJECT public: @@ -28,10 +85,8 @@ private: class DiveHandler : public QGraphicsEllipseItem{ public: DiveHandler(); - int sec; - int mm; protected: - void mousePressEvent(QGraphicsSceneMouseEvent* event); + void mousePressEvent(QGraphicsSceneMouseEvent* event); }; class Ruler : public QGraphicsLineItem{ @@ -49,16 +104,19 @@ public: qreal percentAt(const QPointF& p); qreal posAtValue(qreal value); void setColor(const QColor& color); + void setTextColor(const QColor& color); private: Qt::Orientation orientation; QList<QGraphicsLineItem*> ticks; + QList<QGraphicsSimpleTextItem*> labels; double min; double max; double interval; double posBegin; double posEnd; double tickSize; + QColor textColor; }; class DivePlannerGraphics : public QGraphicsView { @@ -72,10 +130,7 @@ protected: virtual void mouseMoveEvent(QMouseEvent* event); virtual void mousePressEvent(QMouseEvent* event); virtual void mouseReleaseEvent(QMouseEvent* event); - - void createDecoStops(); bool isPointOutOfBoundaries(const QPointF& point); - void deleteTemporaryDivePlan(struct divedatapoint* dp); qreal fromPercent(qreal percent, Qt::Orientation orientation); private slots: void keyEscAction(); @@ -88,11 +143,11 @@ private slots: void increaseDepth(); void decreaseTime(); void decreaseDepth();; - void okClicked(); - void cancelClicked(); + void createDecoStops(); void prepareSelectGas(); void selectGas(const QModelIndex& index); - + void pointInserted(const QModelIndex&, int start, int end); + void pointsRemoved(const QModelIndex&, int start, int end); private: void moveActiveHandler(const QPointF& pos); @@ -138,11 +193,26 @@ private: Button *plusDepth; // adds 10 meters to the depth ruler. Button *lessTime; // remove 10 minutes to the time ruler. Button *lessDepth; // remove 10 meters to the depth ruler. - Button *okBtn; // accepts, and creates a new dive based on the plan. - Button *cancelBtn; // rejects, and clears the dive plan. int minMinutes; // this holds the minimum duration of the dive. int dpMaxTime; // this is the time of the dive calculated by the deco. }; +class DivePlannerWidget : public QWidget { + Q_OBJECT +public: + explicit DivePlannerWidget(QWidget* parent = 0, Qt::WindowFlags f = 0); + +public slots: + void startTimeChanged(const QTime& time); + void atmPressureChanged(const QString& pressure); + void bottomSacChanged(const QString& bottomSac); + void decoSacChanged(const QString& decosac); + void gflowChanged(const QString& gflow); + void gfhighChanged(const QString& gfhigh); + void lastStopChanged(bool checked); +private: + Ui::DivePlanner *ui; +}; + #endif diff --git a/qt-ui/diveplanner.ui b/qt-ui/diveplanner.ui index e4903f0f8..1bcd912a4 100644 --- a/qt-ui/diveplanner.ui +++ b/qt-ui/diveplanner.ui @@ -1,74 +1,113 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>DivePlanner</class> - <widget class="QDialog" name="DivePlanner"> + <widget class="QWidget" name="DivePlanner"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>575</width> - <height>451</height> + <width>400</width> + <height>352</height> </rect> </property> <property name="windowTitle"> - <string>Dialog</string> + <string>Form</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="DivePlannerGraphics" name="graphicsView"/> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Start Time</string> + </property> + </widget> </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <item row="0" column="1"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>ATM Pressure</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QTimeEdit" name="startTime"/> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="ATMPressure"/> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="decoStopSAC"/> + </item> + <item row="5" column="0"> + <widget class="QLineEdit" name="lowGF"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Low GF</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Bottom SAC</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLineEdit" name="bottomSAC"/> + </item> + <item row="4" column="1"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>High GF</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QLineEdit" name="highGF"/> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>SAC on DECO Stop</string> </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QCheckBox" name="lastStop"> + <property name="text"> + <string>Last Stop at 6m</string> + </property> + </widget> + </item> + <item row="8" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> <property name="standardButtons"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> </item> + <item row="7" column="0" colspan="2"> + <widget class="TableView" name="tableWidget" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> </layout> </widget> <customwidgets> <customwidget> - <class>DivePlannerGraphics</class> - <extends>QGraphicsView</extends> - <header>diveplanner.h</header> + <class>TableView</class> + <extends>QWidget</extends> + <header>tableview.h</header> + <container>1</container> </customwidget> </customwidgets> <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>DivePlanner</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>274</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>DivePlanner</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>274</y> - </hint> - </hints> - </connection> - </connections> + <connections/> </ui> diff --git a/qt-ui/downloadfromdivecomputer.cpp b/qt-ui/downloadfromdivecomputer.cpp index 65e7a16e2..acdca89d3 100644 --- a/qt-ui/downloadfromdivecomputer.cpp +++ b/qt-ui/downloadfromdivecomputer.cpp @@ -10,6 +10,8 @@ #include <QThread> #include <QDebug> #include <QStringListModel> +#include <QTimer> +#include <QMessageBox> struct product { const char *product; @@ -42,7 +44,8 @@ DownloadFromDCWidget *DownloadFromDCWidget::instance() } DownloadFromDCWidget::DownloadFromDCWidget(QWidget* parent, Qt::WindowFlags f) : - QDialog(parent, f), ui(new Ui::DownloadFromDiveComputer), thread(0), downloading(false) + QDialog(parent, f), ui(new Ui::DownloadFromDiveComputer), thread(0), timer(new QTimer(this)), + currentState(INITIAL) { ui->setupUi(this); ui->progressBar->hide(); @@ -61,17 +64,86 @@ DownloadFromDCWidget::DownloadFromDCWidget(QWidget* parent, Qt::WindowFlags f) : } if (default_dive_computer_device) ui->device->setText(default_dive_computer_device); + + timer->setInterval(200); + connect(timer, SIGNAL(timeout()), this, SLOT(updateProgressBar())); + + updateState(INITIAL); } void DownloadFromDCWidget::runDialog() { - ui->progressBar->hide(); + updateState(INITIAL); + exec(); } -void DownloadFromDCWidget::stoppedDownloading() +void DownloadFromDCWidget::updateProgressBar() +{ + ui->progressBar->setValue(progress_bar_fraction *100); +} + +void DownloadFromDCWidget::updateState(states state) { - downloading = false; + if (state == currentState) + return; + + if (state == INITIAL) { + ui->progressBar->hide(); + markChildrenAsEnabled(); + timer->stop(); + } + + // tries to cancel an on going download + else if (currentState == DOWNLOADING && state == CANCELLING) { + import_thread_cancelled = true; + ui->cancel->setEnabled(false); + } + + // user pressed cancel but the application isn't doing anything. + // means close the window + else if ((currentState == INITIAL || currentState == CANCELLED || currentState == DONE || currentState == ERROR) + && state == CANCELLING) { + timer->stop(); + reject(); + } + + // the cancelation process is finished + else if (currentState == CANCELLING && (state == DONE || state == CANCELLED)) { + timer->stop(); + state = CANCELLED; + ui->progressBar->setValue(0); + ui->progressBar->hide(); + markChildrenAsEnabled(); + } + + // DOWNLOAD is finally done, close the dialog and go back to the main window + else if (currentState == DOWNLOADING && state == DONE) { + timer->stop(); + ui->progressBar->setValue(100); + markChildrenAsEnabled(); + accept(); + } + + // DOWNLOAD is started. + else if (state == DOWNLOADING) { + timer->start(); + ui->progressBar->setValue(0); + ui->progressBar->show(); + markChildrenAsDisabled(); + } + + // got an error + else if (state == ERROR) { + QMessageBox::critical(this, tr("Error"), this->thread->error, QMessageBox::Ok); + + markChildrenAsEnabled(); + ui->progressBar->hide(); + ui->ok->setText(tr("retry")); + } + + // properly updating the widget state + currentState = state; } void DownloadFromDCWidget::on_vendor_currentIndexChanged(const QString& vendor) @@ -132,23 +204,15 @@ void DownloadFromDCWidget::fill_computer_list() void DownloadFromDCWidget::on_cancel_clicked() { - import_thread_cancelled = true; - if (thread) { - thread->wait(); - thread->deleteLater(); - thread = 0; - } - close(); + updateState(CANCELLING); } void DownloadFromDCWidget::on_ok_clicked() { - if (downloading) - return; - - ui->progressBar->setValue(0); - ui->progressBar->show(); + updateState(DOWNLOADING); + // I don't really think that create/destroy the thread + // is really necessary. if (thread) { thread->deleteLater(); } @@ -156,20 +220,22 @@ void DownloadFromDCWidget::on_ok_clicked() data.devname = strdup(ui->device->text().toUtf8().data()); data.vendor = strdup(ui->vendor->currentText().toUtf8().data()); 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); - thread = new InterfaceThread(this, &data); - connect(thread, SIGNAL(updateInterface(int)), - ui->progressBar, SLOT(setValue(int)), Qt::QueuedConnection); // Qt::QueuedConnection == threadsafe. + thread = new DownloadThread(this, &data); - connect(thread, SIGNAL(finished()), this, SLOT(close())); + connect(thread, SIGNAL(finished()), + this, SLOT(onDownloadThreadFinished()), Qt::QueuedConnection); + + MainWindow *w = mainWindow(); + connect(thread, SIGNAL(finished()), w, SLOT(refreshDisplay())); thread->start(); - downloading = true; } bool DownloadFromDCWidget::preferDownloaded() @@ -177,34 +243,78 @@ bool DownloadFromDCWidget::preferDownloaded() return ui->preferDownloaded->isChecked(); } -DownloadThread::DownloadThread(device_data_t* data): data(data) +void DownloadFromDCWidget::reject() { + // we don't want the download window being able to close + // while we're still downloading. + if (currentState != DOWNLOADING && currentState != CANCELLING) + QDialog::reject(); } -void DownloadThread::run() +void DownloadFromDCWidget::onDownloadThreadFinished() { - DownloadFromDCWidget *dfdcw = DownloadFromDCWidget::instance(); - if (!strcmp(data->vendor, "Uemis")) - do_uemis_import(data->devname, data->force_download); - else - do_libdivecomputer_import(data); - process_dives(TRUE, dfdcw->preferDownloaded()); - dfdcw->stoppedDownloading(); + if (currentState == DOWNLOADING) { + if (thread->error.isEmpty()) + updateState(DONE); + else + updateState(ERROR); + } else + updateState(CANCELLED); } -InterfaceThread::InterfaceThread(QObject* parent, device_data_t* data): QThread(parent), data(data) +void DownloadFromDCWidget::markChildrenAsDisabled() { + ui->device->setDisabled(true); + ui->vendor->setDisabled(true); + ui->product->setDisabled(true); + ui->forceDownload->setDisabled(true); + ui->preferDownloaded->setDisabled(true); + ui->ok->setDisabled(true); + ui->search->setDisabled(true); } -void InterfaceThread::run() +void DownloadFromDCWidget::markChildrenAsEnabled() { - DownloadThread *download = new DownloadThread(data); - MainWindow *w = mainWindow(); - connect(download, SIGNAL(finished()), w, SLOT(refreshDisplay())); - download->start(); - while (download->isRunning()) { - msleep(200); - updateInterface(progress_bar_fraction *100); + ui->device->setDisabled(false); + ui->vendor->setDisabled(false); + ui->product->setDisabled(false); + ui->forceDownload->setDisabled(false); + ui->preferDownloaded->setDisabled(false); + ui->ok->setDisabled(false); + ui->cancel->setDisabled(false); + ui->search->setDisabled(false); +} + +DownloadThread::DownloadThread(QObject* parent, device_data_t* data): QThread(parent), + data(data) +{ +} + +static QString str_error(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + const QString str = QString().vsprintf( fmt, args ); + va_end(args); + + return str; +} + +void DownloadThread::run() +{ + DownloadFromDCWidget *dfdcw = DownloadFromDCWidget::instance(); + const char *error; + + if (!strcmp(data->vendor, "Uemis")) + error = do_uemis_import(data->devname, data->force_download); + else + error = do_libdivecomputer_import(data); + + if (error) { + this->error = str_error(error, data->devname, data->vendor, data->product); } - updateInterface(100); + + // I'm not sure if we should really call process_dives even + // if there's an error or a cancelation + process_dives(TRUE, dfdcw->preferDownloaded()); } diff --git a/qt-ui/downloadfromdivecomputer.h b/qt-ui/downloadfromdivecomputer.h index 444c03e81..e10d61b38 100644 --- a/qt-ui/downloadfromdivecomputer.h +++ b/qt-ui/downloadfromdivecomputer.h @@ -15,20 +15,10 @@ struct device_data_t; class DownloadThread : public QThread{ Q_OBJECT public: - explicit DownloadThread(device_data_t* data); + DownloadThread(QObject* parent, device_data_t* data); virtual void run(); -private: - device_data_t *data; -}; -class InterfaceThread : public QThread{ - Q_OBJECT -public: - InterfaceThread(QObject *parent, device_data_t *data) ; - virtual void run(); - -signals: - void updateInterface(int value); + QString error; private: device_data_t *data; }; @@ -39,15 +29,32 @@ class DownloadFromDCWidget : public QDialog{ public: explicit DownloadFromDCWidget(QWidget* parent = 0, Qt::WindowFlags f = 0); static DownloadFromDCWidget *instance(); + void reject(); + + enum states { + INITIAL, + DOWNLOADING, + CANCELLING, + CANCELLED, + ERROR, + DONE, + }; + public slots: void on_ok_clicked(); void on_cancel_clicked(); - void runDialog(); - void stoppedDownloading(); void on_vendor_currentIndexChanged(const QString& vendor); + + void onDownloadThreadFinished(); + void updateProgressBar(); + void runDialog(); + private: + void markChildrenAsDisabled(); + void markChildrenAsEnabled(); + Ui::DownloadFromDiveComputer *ui; - InterfaceThread *thread; + DownloadThread *thread; bool downloading; QStringList vendorList; @@ -58,8 +65,14 @@ private: QStringListModel *vendorModel; QStringListModel *productModel; void fill_computer_list(); + + QTimer *timer; + public: bool preferDownloaded(); + void updateState(states state); + states currentState; + }; #endif diff --git a/qt-ui/graphicsview-common.cpp b/qt-ui/graphicsview-common.cpp index fbe196e98..30b9ccb08 100644 --- a/qt-ui/graphicsview-common.cpp +++ b/qt-ui/graphicsview-common.cpp @@ -55,3 +55,8 @@ void fill_profile_color() profile_color[CALC_CEILING_DEEP] = COLOR(APPLE1_HIGH_TRANS, BLACK1_HIGH_TRANS, APPLE1_HIGH_TRANS); #undef COLOR } + +QColor getColor(const color_indice_t i, bool isGrayscale = false) +{ + return profile_color[i].at((isGrayscale) ? 1 : 0); +} diff --git a/qt-ui/graphicsview-common.h b/qt-ui/graphicsview-common.h index 96cecc8e6..d2499c823 100644 --- a/qt-ui/graphicsview-common.h +++ b/qt-ui/graphicsview-common.h @@ -33,7 +33,6 @@ typedef enum { /* profile_color[color indice] = COLOR(screen color, b/w printer color, color printer}} printer & screen colours could be different */ extern QMap<color_indice_t, QVector<QColor> > profile_color; - void fill_profile_color(); diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 768a521f0..42250b42c 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -18,6 +18,7 @@ #include <QCompleter> #include <QDebug> #include <QSet> +#include <QTableView> #include <QSettings> #include <QPalette> @@ -64,38 +65,19 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent), if (label) label->setAlignment(Qt::AlignHCenter); } + ui->cylinders->setTitle(tr("Cylinders")); + ui->cylinders->setBtnToolTip(tr("Add Cylinder")); + connect(ui->cylinders, SIGNAL(addButtonClicked()), this, SLOT(addCylinder_clicked())); - /*Thid couldn't be done on the ui file because element - is floating, instead of being fixed on the layout. */ - QIcon plusIcon(":plus"); - addCylinder = new QPushButton(plusIcon, QString(), ui->cylindersGroup); - addCylinder->setFlat(true); - addCylinder->setToolTip(tr("Add Cylinder")); - connect(addCylinder, SIGNAL(clicked(bool)), this, SLOT(addCylinder_clicked())); - addCylinder->setEnabled(false); - addWeight = new QPushButton(plusIcon, QString(), ui->weightGroup); - addWeight->setFlat(true); - addWeight->setToolTip(tr("Add Weight System")); - connect(addWeight, SIGNAL(clicked(bool)), this, SLOT(addWeight_clicked())); - addWeight->setEnabled(false); - - connect(ui->cylinders, SIGNAL(clicked(QModelIndex)), ui->cylinders->model(), SLOT(remove(QModelIndex))); - connect(ui->cylinders, SIGNAL(clicked(QModelIndex)), this, SLOT(editCylinderWidget(QModelIndex))); - connect(ui->weights, SIGNAL(clicked(QModelIndex)), ui->weights->model(), SLOT(remove(QModelIndex))); - connect(ui->weights, SIGNAL(clicked(QModelIndex)), this, SLOT(editWeigthWidget(QModelIndex))); - - 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->horizontalHeader()->setResizeMode (WeightModel::REMOVE , QHeaderView::Fixed); - ui->weights->verticalHeader()->setDefaultSectionSize( metrics.height() +8 ); - ui->weights->setItemDelegateForColumn(WeightModel::TYPE, new WSInfoDelegate()); - - connect(this, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); + ui->weights->setTitle(tr("Weights")); + ui->weights->setBtnToolTip(tr("Add Weight System")); + connect(ui->weights, SIGNAL(addButtonClicked()), this, SLOT(addWeight_clicked())); + + connect(ui->cylinders->view(), SIGNAL(clicked(QModelIndex)), this, SLOT(editCylinderWidget(QModelIndex))); + connect(ui->weights->view(), SIGNAL(clicked(QModelIndex)), this, SLOT(editWeigthWidget(QModelIndex))); + + ui->cylinders->view()->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate()); + ui->weights->view()->setItemDelegateForColumn(WeightModel::TYPE, new WSInfoDelegate()); completers.buddy = new QCompleter(BuddyCompletionModel::instance(), ui->buddy); completers.divemaster = new QCompleter(DiveMasterCompletionModel::instance(), ui->divemaster); @@ -105,37 +87,6 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent), ui->divemaster->setCompleter(completers.divemaster); ui->location->setCompleter(completers.location); ui->suit->setCompleter(completers.suit); - - initialUiSetup(); -} - -// We need to manually position the 'plus' on cylinder and weight. -void MainTab::resizeEvent(QResizeEvent* event) -{ - equipmentPlusUpdate(); - QTabWidget::resizeEvent(event); -} - -void MainTab::showEvent(QShowEvent* event) -{ - QTabWidget::showEvent(event); - equipmentPlusUpdate(); -} - -void MainTab::tabChanged(int idx) -{ - /* if the current tab has become of index 1 (i.e. the equipment tab) call update - * for the plus signs */ - if (idx == 1) - equipmentPlusUpdate(); -} - -void MainTab::equipmentPlusUpdate() -{ - if (ui->cylindersGroup->isVisible()) - addCylinder->setGeometry(ui->cylindersGroup->contentsRect().width() - 30, 2, 24,24); - if (ui->weightGroup->isVisible()) - addWeight->setGeometry(ui->weightGroup->contentsRect().width() - 30, 2, 24,24); } void MainTab::enableEdition() @@ -191,13 +142,12 @@ void MainTab::clearStats() ui->timeLimits->clear(); } -#define UPDATE_TEXT(d, field) \ +#define UPDATE_TEXT(d, field) \ if (!d || !d->field) \ ui->field->setText(""); \ - else \ + else \ ui->field->setText(d->field) - void MainTab::updateDiveInfo(int dive) { editMode = NONE; @@ -308,8 +258,6 @@ void MainTab::updateDiveInfo(int dive) ui->timeLimits->setMinimum(get_time_string(stats_selection.shortest_time.seconds, 0)); cylindersModel->setDive(d); weightModel->setDive(d); - addCylinder->setEnabled(true); - addWeight->setEnabled(true); } else { /* make the fields read-only */ ui->location->setReadOnly(true); @@ -336,8 +284,6 @@ void MainTab::updateDiveInfo(int dive) ui->airPressureText->clear(); cylindersModel->clear(); weightModel->clear(); - addCylinder->setEnabled(false); - addWeight->setEnabled(false); ui->depthLimits->clear(); ui->sacLimits->clear(); ui->divesAllText->clear(); @@ -598,56 +544,14 @@ void MainTab::on_visibility_valueChanged(int value) EDIT_SELECTED_DIVES( mydive->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(); - reload(); -} - void MainTab::editCylinderWidget(const QModelIndex& index) { - if (index.column() != CylindersModel::REMOVE) + if (index.isValid() && index.column() != CylindersModel::REMOVE) ui->cylinders->edit(index); } void MainTab::editWeigthWidget(const QModelIndex& index) { - if (index.column() != WeightModel::REMOVE) + if (index.isValid() && index.column() != WeightModel::REMOVE) ui->weights->edit(index); } diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h index 5180b26e2..f9f65a3ff 100644 --- a/qt-ui/maintab.h +++ b/qt-ui/maintab.h @@ -46,16 +46,9 @@ public: void clearInfo(); void clearEquipment(); void reload(); - bool eventFilter(QObject* , QEvent*); - virtual void resizeEvent(QResizeEvent*); - virtual void showEvent(QShowEvent*); - virtual void hideEvent(QHideEvent* ); - void initialUiSetup(); void equipmentPlusUpdate(); - - public slots: void addCylinder_clicked(); void addWeight_clicked(); @@ -69,7 +62,6 @@ public slots: void on_notes_textChanged(); void on_rating_valueChanged(int value); void on_visibility_valueChanged(int value); - void tabChanged(int idx); void editCylinderWidget(const QModelIndex& index); void editWeigthWidget(const QModelIndex& index); @@ -78,8 +70,6 @@ private: WeightModel *weightModel; CylindersModel *cylindersModel; QMap<dive*, NotesBackup> notesBackup; - QPushButton *addCylinder; - QPushButton *addWeight; enum { NONE, DIVE, TRIP } editMode; Completers completers; void enableEdition(); diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index cdddc4cf9..a1f5a718a 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -14,7 +14,7 @@ <string>TabWidget</string> </property> <property name="currentIndex"> - <number>0</number> + <number>1</number> </property> <widget class="QWidget" name="notesTab"> <attribute name="title"> @@ -147,109 +147,33 @@ </attribute> <layout class="QGridLayout" name="gridLayout_4"> <item row="0" column="0"> - <widget class="QSplitter" name="splitter_2"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <widget class="QGroupBox" name="cylindersGroup"> - <property name="title"> - <string>Cylinders</string> + <widget class="QWidget" name="widget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QTableView" name="cylinders"> - <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="alternatingRowColors"> - <bool>true</bool> - </property> - <property name="showGrid"> - <bool>false</bool> - </property> - <attribute name="verticalHeaderVisible"> - <bool>false</bool> - </attribute> - </widget> - </item> - </layout> - </widget> - <widget class="QGroupBox" name="weightGroup"> - <property name="title"> - <string>Weight</string> + <property name="bottomMargin"> + <number>0</number> </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <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> + <item> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="TableView" name="cylinders" native="true"/> + <widget class="TableView" name="weights" native="true"/> + </widget> + </item> + </layout> </widget> </item> </layout> @@ -602,6 +526,12 @@ <header>simplewidgets.h</header> <container>1</container> </customwidget> + <customwidget> + <class>TableView</class> + <extends>QWidget</extends> + <header>tableview.h</header> + <container>1</container> + </customwidget> </customwidgets> <resources/> <connections/> diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index a78b84565..740849033 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -15,7 +15,7 @@ #include <QCloseEvent> #include <QApplication> #include <QFontMetrics> -#include <QTextBrowser> +#include <QWebView> #include <QTableView> #include "divelistview.h" #include "starwidget.h" @@ -45,6 +45,7 @@ MainWindow* mainWindow() MainWindow::MainWindow() : ui(new Ui::MainWindow()), helpView(0) { + instance = this; ui->setupUi(this); setWindowIcon(QIcon(":subsurface-icon")); connect(ui->ListWidget, SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int))); @@ -59,7 +60,6 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), helpView(0) ui->ListWidget->reloadHeaderActions(); ui->ListWidget->setFocus(); ui->globe->reload(); - instance = this; } // this gets called after we download dives from a divecomputer @@ -117,22 +117,7 @@ void MainWindow::on_actionOpen_triggered() QByteArray fileNamePtr = filename.toLocal8Bit(); on_actionClose_triggered(); - - char *error = NULL; - parse_file(fileNamePtr.data(), &error); - set_filename(fileNamePtr.data(), TRUE); - setTitle(MWTF_FILENAME); - - if (error != NULL) { - showError(error); - free(error); - } - process_dives(FALSE, FALSE); - - ui->InfoWidget->reload(); - ui->globe->reload(); - ui->ListWidget->reload(DiveTripModel::TREE); - ui->ListWidget->setFocus(); + loadFiles( QStringList() << filename ); } void MainWindow::on_actionSave_triggered() @@ -193,23 +178,7 @@ void MainWindow::on_actionImport_triggered() settings.setValue("LastDir", fileInfo.dir().path()); settings.endGroup(); - QByteArray fileNamePtr; - char *error = NULL; - for (int i = 0; i < fileNames.size(); ++i) { - fileNamePtr = fileNames.at(i).toLocal8Bit(); - parse_file(fileNamePtr.data(), &error); - if (error != NULL) { - showError(error); - free(error); - error = NULL; - } - } - process_dives(FALSE, FALSE); - - ui->InfoWidget->reload(); - ui->globe->reload(); - ui->ListWidget->reload(DiveTripModel::TREE); - ui->ListWidget->setFocus(); + importFiles(fileNames); } void MainWindow::on_actionExportUDDF_triggered() @@ -238,12 +207,14 @@ void MainWindow::on_actionDivePlanner_triggered() { disableDcShortcuts(); ui->stackedWidget->setCurrentIndex(1); + ui->infoPane->setCurrentIndex(1); } void MainWindow::showProfile() { enableDcShortcuts(); ui->stackedWidget->setCurrentIndex(0); + ui->infoPane->setCurrentIndex(0); } @@ -324,8 +295,6 @@ void MainWindow::on_mainSplitter_splitterMoved(int pos, int idx) void MainWindow::on_infoProfileSplitter_splitterMoved(int pos, int idx) { - /* always update the floating plus sign icons in the equipment tab */ - ui->InfoWidget->equipmentPlusUpdate(); redrawProfile(); } @@ -411,14 +380,14 @@ void MainWindow::on_actionAboutSubsurface_triggered() void MainWindow::on_actionUserManual_triggered() { if(!helpView){ - helpView = new QTextBrowser(); + helpView = new QWebView(); } QString searchPath = getSubsurfaceDataPath("Documentation"); if (searchPath != "") { QUrl url(searchPath.append("/user-manual.html")); - helpView->setSource(url); + helpView->setUrl(url); } else { - helpView->setText(tr("Cannot find the Subsurface manual")); + helpView->setHtml(tr("Cannot find the Subsurface manual")); } helpView->show(); } @@ -755,3 +724,53 @@ void MainWindow::setTitle(enum MainWindowTitleFormat format) break; } } + +void MainWindow::importFiles(const QStringList fileNames) +{ + QByteArray fileNamePtr; + char *error = NULL; + for (int i = 0; i < fileNames.size(); ++i) { + fileNamePtr = fileNames.at(i).toLocal8Bit(); + parse_file(fileNamePtr.data(), &error); + if (error != NULL) { + showError(error); + free(error); + error = NULL; + } + } + process_dives(TRUE, FALSE); + + ui->InfoWidget->reload(); + ui->globe->reload(); + ui->ListWidget->reload(DiveTripModel::TREE); + ui->ListWidget->setFocus(); + WSInfoModel *wsim = WSInfoModel::instance(); + wsim->updateInfo(); +} + +void MainWindow::loadFiles(const QStringList fileNames) +{ + char *error = NULL; + QByteArray fileNamePtr; + + for (int i = 0; i < fileNames.size(); ++i) { + fileNamePtr = fileNames.at(i).toLocal8Bit(); + parse_file(fileNamePtr.data(), &error); + set_filename(fileNamePtr.data(), TRUE); + setTitle(MWTF_FILENAME); + + if (error != NULL) { + showError(error); + free(error); + } + } + + process_dives(FALSE, FALSE); + + ui->InfoWidget->reload(); + ui->globe->reload(); + ui->ListWidget->reload(DiveTripModel::TREE); + ui->ListWidget->setFocus(); + WSInfoModel *wsim = WSInfoModel::instance(); + wsim->updateInfo(); +} diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 2009740fe..6cc99db96 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -29,7 +29,7 @@ class DiveListView; class GlobeGPS; class MainTab; class ProfileGraphicsView; -class QTextBrowser; +class QWebView; enum MainWindowTitleFormat { MWTF_DEFAULT, MWTF_FILENAME }; @@ -51,6 +51,8 @@ public: // when the profile's visible. void disableDcShortcuts(); void enableDcShortcuts(); + void loadFiles(const QStringList files); + void importFiles(const QStringList importFiles); private slots: /* file menu action */ @@ -110,7 +112,7 @@ private: Ui::MainWindow *ui; QAction *actionNextDive; QAction *actionPreviousDive; - QTextBrowser *helpView; + QWebView *helpView; QString filter(); bool askSaveChanges(); void writeSettings(); diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index 731013538..05a59524e 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -24,7 +24,25 @@ <property name="orientation"> <enum>Qt::Horizontal</enum> </property> - <widget class="MainTab" name="InfoWidget" native="true"/> + <widget class="QStackedWidget" name="infoPane"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="page"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="MainTab" name="InfoWidget" native="true"/> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_2"> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="DivePlannerWidget" name="widget" native="true"/> + </item> + </layout> + </widget> + </widget> <widget class="QStackedWidget" name="stackedWidget"> <property name="currentIndex"> <number>0</number> @@ -34,7 +52,16 @@ <property name="spacing"> <number>0</number> </property> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -47,7 +74,16 @@ <property name="spacing"> <number>0</number> </property> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -123,10 +159,7 @@ </widget> </item> <item> - <widget class="KMessageWidget" name="mainErrorMessage" native="true"> - <zorder>mainSplitter</zorder> - <zorder>mainSplitter</zorder> - </widget> + <widget class="KMessageWidget" name="mainErrorMessage" native="true"/> </item> </layout> </widget> @@ -136,7 +169,7 @@ <x>0</x> <y>0</y> <width>763</width> - <height>20</height> + <height>19</height> </rect> </property> <widget class="QMenu" name="menuFile"> @@ -419,7 +452,7 @@ <customwidget> <class>MainTab</class> <extends>QWidget</extends> - <header>maintab.h</header> + <header>qt-ui/maintab.h</header> <container>1</container> </customwidget> <customwidget> @@ -443,6 +476,12 @@ <extends>QGraphicsView</extends> <header>diveplanner.h</header> </customwidget> + <customwidget> + <class>DivePlannerWidget</class> + <extends>QWidget</extends> + <header>diveplanner.h</header> + <container>1</container> + </customwidget> </customwidgets> <resources/> <connections/> diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp index 998c246b8..13201e436 100644 --- a/qt-ui/modeldelegates.cpp +++ b/qt-ui/modeldelegates.cpp @@ -3,6 +3,7 @@ #include "../divelist.h" #include "starwidget.h" #include "models.h" +#include "diveplanner.h" #include <QtDebug> #include <QPainter> @@ -14,6 +15,7 @@ #include <QLineEdit> #include <QKeyEvent> #include <QAbstractItemView> +#include <QStringListModel> // Gets the index of the model in the currentRow and column. // currCombo is defined below. @@ -106,7 +108,7 @@ void ComboBoxDelegate::testActivation(const QString& s) bool ComboBoxDelegate::eventFilter(QObject* object, QEvent* event) { // Reacts on Key_UP and Key_DOWN to show the QComboBox - list of choices. - if (event->type() == QEvent::KeyPress){ + if (event->type() == QEvent::KeyPress || event->type() == QEvent::ShortcutOverride){ if (object == currCombo.comboEditor){ // the 'LineEdit' part QKeyEvent *ev = static_cast<QKeyEvent*>(event); if(ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down){ @@ -199,17 +201,17 @@ QWidget* TankInfoDelegate::createEditor(QWidget* parent, const QStyleOptionViewI return delegate; } -struct RevertWeigthData { +struct RevertWeightData { QString type; - int weigth; -} currWeigth; + int weight; +} currWeight; void WSInfoDelegate::revertModelData(QWidget* widget, QAbstractItemDelegate::EndEditHint hint) { if (hint == QAbstractItemDelegate::NoHint || hint == QAbstractItemDelegate::RevertModelCache){ WeightModel *mymodel = qobject_cast<WeightModel *>(currCombo.model); - mymodel->setData(IDX(WeightModel::TYPE), currWeigth.type, Qt::EditRole); - mymodel->passInData(IDX(WeightModel::WEIGHT), currWeigth.weigth); + mymodel->setData(IDX(WeightModel::TYPE), currWeight.type, Qt::EditRole); + mymodel->passInData(IDX(WeightModel::WEIGHT), currWeight.weight); } } @@ -236,7 +238,6 @@ void WSInfoDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, co } mymodel->setData(IDX(WeightModel::TYPE), v, Qt::EditRole); mymodel->passInData(IDX(WeightModel::WEIGHT), grams); - qDebug() << "Fixme, every weigth is 0.0 grams. see:" << grams; } WSInfoDelegate::WSInfoDelegate(QObject* parent): ComboBoxDelegate(WSInfoModel::instance(), parent) @@ -249,7 +250,23 @@ QWidget* WSInfoDelegate::createEditor(QWidget* parent, const QStyleOptionViewIte QWidget *editor = ComboBoxDelegate::createEditor(parent, option, index); WeightModel *mymodel = qobject_cast<WeightModel *>(currCombo.model); weightsystem_t *ws = mymodel->weightSystemAt(index); - currWeigth.type = ws->description; - currWeigth.weigth = ws->weight.grams; + currWeight.type = ws->description; + currWeight.weight = ws->weight.grams; return editor; } + +void AirTypesDelegate::revertModelData(QWidget* widget, QAbstractItemDelegate::EndEditHint hint) +{ +} + +void AirTypesDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const +{ + if (!index.isValid()) + return; + QComboBox *combo = qobject_cast<QComboBox*>(editor); + model->setData(index, QVariant(combo->currentText())); +} + +AirTypesDelegate::AirTypesDelegate(QObject* parent) : ComboBoxDelegate(airTypes(), parent) +{ +} diff --git a/qt-ui/modeldelegates.h b/qt-ui/modeldelegates.h index 9603d5dce..29d4f3717 100644 --- a/qt-ui/modeldelegates.h +++ b/qt-ui/modeldelegates.h @@ -49,4 +49,13 @@ public slots: void revertModelData(QWidget* widget, QAbstractItemDelegate::EndEditHint hint); }; +class AirTypesDelegate : public ComboBoxDelegate{ + Q_OBJECT +public: + explicit AirTypesDelegate(QObject* parent = 0); + virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; +public slots: + void revertModelData(QWidget* widget, QAbstractItemDelegate::EndEditHint hint); +}; + #endif diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 25429154f..fc91e0558 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -549,7 +549,7 @@ bool WSInfoModel::insertRows(int row, int count, const QModelIndex& parent) bool WSInfoModel::setData(const QModelIndex& index, const QVariant& value, int role) { - struct ws_info *info = &ws_info[index.row()]; + struct ws_info_t *info = &ws_info[index.row()]; switch(index.column()) { case DESCRIPTION: info->name = strdup(value.toByteArray().data()); @@ -577,7 +577,7 @@ QVariant WSInfoModel::data(const QModelIndex& index, int role) const if (!index.isValid()) { return ret; } - struct ws_info *info = &ws_info[index.row()]; + struct ws_info_t *info = &ws_info[index.row()]; int gr = info->grams; switch(role){ @@ -636,7 +636,26 @@ const QString& WSInfoModel::biggerString() const WSInfoModel::WSInfoModel() : QAbstractTableModel(), rows(-1) { - struct ws_info *info = ws_info; + struct ws_info_t *info = ws_info; + 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); + endInsertRows(); + } +} + +void WSInfoModel::updateInfo() +{ + struct ws_info_t *info = ws_info; + beginRemoveRows(QModelIndex(), 0, this->rows); + endRemoveRows(); + rows = -1; for (info = ws_info; info->name; info++, rows++){ QString wsInfoName(info->name); if( wsInfoName.count() > biggerEntry.count()){ @@ -657,7 +676,7 @@ void WSInfoModel::update() endRemoveRows(); rows = -1; } - struct ws_info *info = ws_info; + struct ws_info_t *info = ws_info; for (info = ws_info; info->name; info++, rows++); if (rows > -1) { @@ -687,7 +706,7 @@ bool TankInfoModel::insertRows(int row, int count, const QModelIndex& parent) bool TankInfoModel::setData(const QModelIndex& index, const QVariant& value, int role) { - struct tank_info *info = &tank_info[index.row()]; + struct tank_info_t *info = &tank_info[index.row()]; switch(index.column()) { case DESCRIPTION: info->name = strdup(value.toByteArray().data()); @@ -722,7 +741,7 @@ QVariant TankInfoModel::data(const QModelIndex& index, int role) const return defaultModelFont(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { - struct tank_info *info = &tank_info[index.row()]; + struct tank_info_t *info = &tank_info[index.row()]; int ml = info->ml; double bar = (info->psi) ? psi_to_bar(info->psi) : info->bar; @@ -779,7 +798,7 @@ int TankInfoModel::rowCount(const QModelIndex& parent) const TankInfoModel::TankInfoModel() : QAbstractTableModel(), rows(-1) { - struct tank_info *info = tank_info; + struct tank_info_t *info = tank_info; for (info = tank_info; info->name; info++, rows++){ QString infoName(info->name); if (infoName.count() > biggerEntry.count()){ @@ -800,7 +819,7 @@ void TankInfoModel::update() endRemoveRows(); rows = -1; } - struct tank_info *info = tank_info; + struct tank_info_t *info = tank_info; for (info = tank_info; info->name; info++, rows++); if (rows > -1) { diff --git a/qt-ui/models.h b/qt-ui/models.h index 22cfe4b7e..c60856478 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -59,6 +59,7 @@ public: const QString& biggerString() const; void clear(); void update(); + void updateInfo(); private: int rows; QString biggerEntry; diff --git a/qt-ui/preferences.ui b/qt-ui/preferences.ui index 07a423834..2ecd0963c 100644 --- a/qt-ui/preferences.ui +++ b/qt-ui/preferences.ui @@ -374,7 +374,7 @@ <item row="4" column="0"> <widget class="QLabel" name="label_5"> <property name="text"> - <string>Weigth</string> + <string>Weight</string> </property> </widget> </item> diff --git a/qt-ui/tableview.cpp b/qt-ui/tableview.cpp new file mode 100644 index 000000000..6956d3941 --- /dev/null +++ b/qt-ui/tableview.cpp @@ -0,0 +1,90 @@ +#include "tableview.h" +#include "ui_tableview.h" +#include "models.h" + +#include <QPushButton> +#include <QFile> +#include <QTextStream> +#include <QSettings> + +TableView::TableView(QWidget *parent) : QWidget(parent), ui(new Ui::TableView){ + ui->setupUi(this); + QFile cssFile(":table-css"); + cssFile.open(QIODevice::ReadOnly); + QTextStream reader(&cssFile); + QString css = reader.readAll(); + ui->tableView->setStyleSheet(css); + + QIcon plusIcon(":plus"); + plusBtn = new QPushButton(plusIcon, QString(), ui->groupBox); + plusBtn->setFlat(true); + plusBtn->setToolTip(tr("Add Cylinder")); + connect(plusBtn, SIGNAL(clicked(bool)), this, SIGNAL(addButtonClicked())); +} + +TableView::~TableView() +{ + QSettings s; + s.beginGroup(objectName()); + for (int i = 0; i < ui->tableView->model()->columnCount(); i++) { + s.setValue(QString("colwidth%1").arg(i), ui->tableView->columnWidth(i)); + } + s.endGroup(); + s.sync(); +} + +void TableView::setBtnToolTip(const QString& tooltip) +{ + plusBtn->setToolTip(tooltip); +} + +void TableView::setTitle(const QString& title) +{ + ui->groupBox->setTitle(title); +} + +void TableView::setModel(QAbstractItemModel *model){ + ui->tableView->setModel(model); + connect(ui->tableView, SIGNAL(clicked(QModelIndex)), model, SLOT(remove(QModelIndex))); + + QSettings s; + s.beginGroup(objectName()); + for (int i = 0; i < ui->tableView->model()->columnCount(); i++) { + QVariant width = s.value(QString("colwidth%1").arg(i)); + if (width.isValid()) + ui->tableView->setColumnWidth(i, width.toInt()); + else + ui->tableView->resizeColumnToContents(i); + } + s.endGroup(); + + ui->tableView->horizontalHeader()->setResizeMode(0, QHeaderView::Fixed); + QFontMetrics metrics(defaultModelFont()); + ui->tableView->verticalHeader()->setDefaultSectionSize( metrics.height() + 8 ); +} + +void TableView::fixPlusPosition() +{ + plusBtn->setGeometry(ui->groupBox->contentsRect().width() - 30, 2, 24,24); +} + +// We need to manually position the 'plus' on cylinder and weight. +void TableView::resizeEvent(QResizeEvent* event) +{ + fixPlusPosition(); + QWidget::resizeEvent(event); +} + +void TableView::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); + fixPlusPosition(); +} + +void TableView::edit(const QModelIndex& index){ + ui->tableView->edit(index); +} + +QTableView *TableView::view(){ + return ui->tableView; +} diff --git a/qt-ui/tableview.h b/qt-ui/tableview.h new file mode 100644 index 000000000..d22f466b5 --- /dev/null +++ b/qt-ui/tableview.h @@ -0,0 +1,45 @@ +#ifndef TABLEVIEW_H +#define TABLEVIEW_H + +/* This TableView is prepared to have the CSS, + * the methods to restore / save the state of + * the column widths and the 'plus' button. + */ +#include <QWidget> + +class QPushButton; +class QAbstractItemModel; +class QModelIndex; +class QTableView; +namespace Ui{ + class TableView; +}; + +class TableView : public QWidget { +Q_OBJECT +public: + TableView(QWidget *parent = 0); + virtual ~TableView(); + void setTitle(const QString& title); + /* The model is expected to have a 'remove' slot, that takes a QModelIndex as parameter. + * It's also expected to have the column '1' as a trash icon. I most probably should create a + * proxy model and add that column, will mark that as TODO. see? marked. + */ + void setModel(QAbstractItemModel* model); + void setBtnToolTip(const QString& tooltip); + void fixPlusPosition(); + void edit(const QModelIndex& index); + QTableView *view(); +protected: + virtual void showEvent(QShowEvent* ); + virtual void resizeEvent(QResizeEvent* ); + +signals: + void addButtonClicked(); + +private: + Ui::TableView *ui; + QPushButton *plusBtn; +}; + +#endif diff --git a/qt-ui/tableview.ui b/qt-ui/tableview.ui new file mode 100644 index 000000000..10b5f79f9 --- /dev/null +++ b/qt-ui/tableview.ui @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>TableView</class> + <widget class="QWidget" name="TableView"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string/> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QTableView" name="tableView"/> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> |