diff options
Diffstat (limited to 'qt-ui')
31 files changed, 2083 insertions, 828 deletions
diff --git a/qt-ui/about.cpp b/qt-ui/about.cpp index 1a96b6257..821835f3e 100644 --- a/qt-ui/about.cpp +++ b/qt-ui/about.cpp @@ -1,6 +1,6 @@ #include "about.h" #include "ui_about.h" -#include "../version.h" +#include "version.h" #include <QDebug> #include <QDialogButtonBox> #include <QNetworkReply> diff --git a/qt-ui/completionmodels.cpp b/qt-ui/completionmodels.cpp new file mode 100644 index 000000000..8bd4f5441 --- /dev/null +++ b/qt-ui/completionmodels.cpp @@ -0,0 +1,37 @@ +#include "completionmodels.h" +#include "dive.h" + +#define CREATE_SINGLETON(X) \ +X* X::instance() \ +{ \ + static X* self = new X(); \ + return self; \ +} + +CREATE_SINGLETON(BuddyCompletionModel); +CREATE_SINGLETON(DiveMasterCompletionModel); +CREATE_SINGLETON(LocationCompletionModel); +CREATE_SINGLETON(SuitCompletionModel); + +#undef CREATE_SINGLETON + +#define CREATE_UPDATE_METHOD(Class, diveStructMember) \ +void Class::updateModel() \ +{ \ + QStringList list; \ + struct dive* dive; \ + int i = 0; \ + for_each_dive(i, dive){ \ + QString buddy(dive->diveStructMember); \ + if (!list.contains(buddy)){ \ + list.append(buddy); \ + } \ + } \ + setStringList(list); \ +} + +CREATE_UPDATE_METHOD(BuddyCompletionModel, buddy); +CREATE_UPDATE_METHOD(DiveMasterCompletionModel, divemaster); +CREATE_UPDATE_METHOD(LocationCompletionModel, location); +CREATE_UPDATE_METHOD(SuitCompletionModel, suit); + diff --git a/qt-ui/completionmodels.h b/qt-ui/completionmodels.h new file mode 100644 index 000000000..42b81946c --- /dev/null +++ b/qt-ui/completionmodels.h @@ -0,0 +1,34 @@ +#ifndef COMPLETIONMODELS_H +#define COMPLETIONMODELS_H + +#include <QStringListModel> + +class BuddyCompletionModel : public QStringListModel { + Q_OBJECT +public: + static BuddyCompletionModel* instance(); + void updateModel(); +}; + +class DiveMasterCompletionModel : public QStringListModel { + Q_OBJECT +public: + static DiveMasterCompletionModel* instance(); + void updateModel(); +}; + +class LocationCompletionModel : public QStringListModel { + Q_OBJECT +public: + static LocationCompletionModel* instance(); + void updateModel(); +}; + +class SuitCompletionModel : public QStringListModel { + Q_OBJECT +public: + static SuitCompletionModel* instance(); + void updateModel(); +}; + +#endif 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/divelistview.cpp b/qt-ui/divelistview.cpp index 4b856f601..2e40678ad 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -226,16 +226,7 @@ void DiveListView::currentChanged(const QModelIndex& current, const QModelIndex& { if (!current.isValid()) return; - const QAbstractItemModel *model = current.model(); - int selectedDive = 0; - struct dive *dive = (struct dive*) model->data(current, DiveTripModel::DIVE_ROLE).value<void*>(); - if (!dive) // it's a trip! select first child. - dive = (struct dive*) model->data(current.child(0,0), DiveTripModel::DIVE_ROLE).value<void*>(); - selectedDive = get_divenr(dive); scrollTo(current); - if (selectedDive == selected_dive) - return; - Q_EMIT currentDiveChanged(selectedDive); } void DiveListView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp index 1cabdfacf..ceb5ebdba 100644 --- a/qt-ui/diveplanner.cpp +++ b/qt-ui/diveplanner.cpp @@ -1,28 +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 <cmath> +#include "../divelist.h" +#include "../planner.h" + #include <QMouseEvent> #include <QDebug> -#include <QGraphicsWidget> -#include <QGraphicsProxyWidget> -#include <QPushButton> #include <QGraphicsSceneMouseEvent> #include <QMessageBox> - -#include "ui_diveplanner.h" -#include "mainwindow.h" +#include <QStringListModel> +#include <QListView> +#include <QModelIndex> +#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(); @@ -47,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); @@ -54,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), @@ -62,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); @@ -78,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); @@ -96,33 +125,19 @@ DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent) diveBg->setPen(QPen(QBrush(),0)); scene()->addItem(diveBg); - plusDepth = new Button(); - plusDepth->setPixmap(QPixmap(":plus")); - plusDepth->setPos(fromPercent(5, Qt::Horizontal), fromPercent(5, Qt::Vertical)); - plusDepth->setToolTip("Increase maximum depth by 10m"); - scene()->addItem(plusDepth); - connect(plusDepth, SIGNAL(clicked()), this, SLOT(increaseDepth())); - - plusTime = new Button(); - plusTime->setPixmap(QPixmap(":plus")); - plusTime->setPos(fromPercent(95, Qt::Horizontal), fromPercent(95, Qt::Vertical)); - plusTime->setToolTip("Increase minimum dive time by 10m"); - scene()->addItem(plusTime); - connect(plusTime, SIGNAL(clicked()), this, SLOT(increaseTime())); - - okBtn = new Button(); - okBtn->setText(tr("Ok")); - okBtn->setPos(fromPercent(1, Qt::Horizontal), fromPercent(95, Qt::Vertical)); - scene()->addItem(okBtn); - connect(okBtn, SIGNAL(clicked()), this, SLOT(okClicked())); - - cancelBtn = new Button(); - cancelBtn->setText(tr("Cancel")); - cancelBtn->setPos(okBtn->pos().x() + okBtn->boundingRect().width() + fromPercent(2, Qt::Horizontal), - fromPercent(95, Qt::Vertical)); - scene()->addItem(cancelBtn); - connect(cancelBtn, SIGNAL(clicked()), this, SLOT(cancelClicked())); - +#define ADDBTN(obj, icon, text, horizontal, vertical, tooltip, slot) \ + obj = new Button(); \ + obj->setPixmap(QPixmap(icon)); \ + obj->setPos(fromPercent(horizontal, Qt::Horizontal), fromPercent(vertical, Qt::Vertical)); \ + obj->setToolTip(tooltip); \ + scene()->addItem(obj); \ + connect(obj, SIGNAL(clicked()), this, SLOT(slot)); + + ADDBTN(plusDepth, ":plus", "" , 5, 5, tr("Increase maximum depth by 10m"), increaseDepth()); + 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()); +#undef ADDBTN minMinutes = TIME_INITIAL_MAX; QAction *action = NULL; @@ -142,23 +157,51 @@ DivePlannerGraphics::DivePlannerGraphics(QWidget* parent): QGraphicsView(parent) ADD_ACTION(Qt::Key_Right, keyRightAction()); #undef ADD_ACTION + // Prepare the stuff for the gas-choices. + gasListView = new QListView(); + 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(); } } @@ -166,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(); @@ -181,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){ @@ -197,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){ @@ -224,34 +273,61 @@ 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() { - if(scene()->selectedItems().count()){ + int selCount = scene()->selectedItems().count(); + if(selCount){ + 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)); } } - createDecoStops(); + plannerModel->removeSelectedPoints(selectedIndexes); } } +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() { if (scene()->selectedItems().count()){ scene()->clearSelection(); return; } - - cancelClicked(); + plannerModel->cancelPlan(); } qreal DivePlannerGraphics::fromPercent(qreal percent, Qt::Orientation orientation) @@ -261,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) @@ -295,6 +354,40 @@ void DivePlannerGraphics::increaseTime() createDecoStops(); } +void DivePlannerGraphics::decreaseDepth() +{ + if (depthLine->maximum() - 10 < MIN_DEEPNESS) + return; + + Q_FOREACH(DiveHandler *d, handles){ + if (depthLine->valueAt(d->pos()) > depthLine->maximum() - 10){ + QMessageBox::warning(mainWindow(), + tr("Handler Position Error"), + tr("One or more of your stops will be lost with this operations, \n" + "Please, remove them first.")); + return; + } + } + depthLine->setMaximum(depthLine->maximum() - 10); + depthLine->updateTicks(); + createDecoStops(); +} + +void DivePlannerGraphics::decreaseTime() +{ + if (timeLine->maximum() -10 < TIME_INITIAL_MAX){ + return; + } + if (timeLine->maximum() - 10 < dpMaxTime){ + qDebug() << timeLine->maximum() << dpMaxTime; + return; + } + minMinutes -= 10; + timeLine->setMaximum(timeLine->maximum() -10); + timeLine->updateTicks(); + createDecoStops(); +} + void DivePlannerGraphics::mouseDoubleClickEvent(QMouseEvent* event) { QPointF mappedPos = mapToScene(event->pos()); @@ -303,78 +396,54 @@ 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; - } - } + plannerModel->addStop(meters * 1000, minutes * 60, tr("Air"), 0); +} - DiveHandler *item = new DiveHandler (); - item->sec = minutes * 60; - item->mm = meters * 1000; - item->setPos(QPointF(xpos, ypos)); - scene()->addItem(item); - handles << item; - createDecoStops(); +void DivePlannerGraphics::prepareSelectGas() +{ + currentGasChoice = static_cast<Button*>(sender()); + QPoint c = QCursor::pos(); + gasListView->setGeometry(c.x(), c.y(), 150, 100); + gasListView->show(); +} + +void DivePlannerGraphics::selectGas(const QModelIndex& index) +{ + QString gasSelected = gasListView->model()->data(index, Qt::DisplayRole).toString(); + 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; + } - 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)); + 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); + gases[i]->setText( strForAir(dp)); } // (re-) create the profile with different colors for segments that were @@ -384,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); @@ -411,15 +481,7 @@ void DivePlannerGraphics::createDecoStops() pat.setColorAt(0, profile_color[DEPTH_TOP].first()); diveBg->setBrush(pat); - deleteTemporaryDivePlan(diveplan.dp); -} - -void DivePlannerGraphics::deleteTemporaryDivePlan(divedatapoint* dp) -{ - if (!dp) - return; - deleteTemporaryDivePlan(dp->next); - free(dp); + plannerModel->deleteTemporaryPlan(); } void DivePlannerGraphics::resizeEvent(QResizeEvent* event) @@ -532,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)); @@ -542,7 +610,7 @@ void DivePlannerGraphics::mouseReleaseEvent(QMouseEvent* event) } } -DiveHandler::DiveHandler(): QGraphicsEllipseItem(), from(0), to(0) +DiveHandler::DiveHandler(): QGraphicsEllipseItem() { setRect(-5,-5,10,10); setFlag(QGraphicsItem::ItemIgnoresTransformations); @@ -558,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(); } @@ -571,6 +643,11 @@ void Ruler::setMinimum(double minimum) min = minimum; } +void Ruler::setTextColor(const QColor& color) +{ + textColor = color; +} + Ruler::Ruler() : orientation(Qt::Horizontal) { } @@ -578,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); } } @@ -670,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); @@ -712,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 7f33f044b..8dd8db3e3 100644 --- a/qt-ui/diveplanner.h +++ b/qt-ui/diveplanner.h @@ -4,6 +4,67 @@ #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 @@ -24,13 +85,8 @@ private: class DiveHandler : public QGraphicsEllipseItem{ public: DiveHandler(); - QGraphicsLineItem *from; - QGraphicsLineItem *to; - int sec; - int mm; protected: - void mousePressEvent(QGraphicsSceneMouseEvent* event); - + void mousePressEvent(QGraphicsSceneMouseEvent* event); }; class Ruler : public QGraphicsLineItem{ @@ -48,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 { @@ -71,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(); @@ -85,9 +141,13 @@ private slots: void keyRightAction(); void increaseTime(); void increaseDepth(); - void okClicked(); - void cancelClicked(); - + void decreaseTime(); + void decreaseDepth();; + 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); @@ -97,6 +157,15 @@ private: /* This is the user-entered handles. */ QList<DiveHandler *> handles; + /* this is the user-entered gases. + This must be a button, so the + user cna click to choose a new gas. + */ + QList<Button*> gases; + QListView *gasListView; + QStringListModel *gasChoices; + Button *currentGasChoice; + /* those are the lines that follows the mouse. */ QGraphicsLineItem *verticalLine; QGraphicsLineItem *horizontalLine; @@ -124,10 +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 464626f4b..30b9ccb08 100644 --- a/qt-ui/graphicsview-common.cpp +++ b/qt-ui/graphicsview-common.cpp @@ -31,7 +31,7 @@ void fill_profile_color() profile_color[TEXT_BACKGROUND] = COLOR(CONCRETE1_LOWER_TRANS, WHITE1, CONCRETE1_LOWER_TRANS); profile_color[ALERT_BG] = COLOR(BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS, BROOM1_LOWER_TRANS); - profile_color[ALERT_FG] = COLOR(BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS); + profile_color[ALERT_FG] = COLOR(BLACK1_LOW_TRANS, WHITE1, BLACK1_LOW_TRANS); profile_color[EVENTS] = COLOR(REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1); profile_color[SAMPLE_DEEP] = COLOR(QColor(Qt::red).darker(), BLACK1_LOW_TRANS, PERSIANRED1); profile_color[SAMPLE_SHALLOW] = COLOR(QColor(Qt::red).lighter(), BLACK1_LOW_TRANS, PERSIANRED1); @@ -48,7 +48,7 @@ void fill_profile_color() profile_color[SAC_DEFAULT] = COLOR(WHITE1, BLACK1_LOW_TRANS, FORESTGREEN1); profile_color[BOUNDING_BOX] = COLOR(WHITE1, BLACK1_LOW_TRANS, TUNDORA1_MED_TRANS); profile_color[PRESSURE_TEXT] = COLOR(KILLARNEY1, BLACK1_LOW_TRANS, KILLARNEY1); - profile_color[BACKGROUND] = COLOR(SPRINGWOOD1, BLACK1_LOW_TRANS, SPRINGWOOD1); + profile_color[BACKGROUND] = COLOR(SPRINGWOOD1, WHITE1, SPRINGWOOD1); profile_color[CEILING_SHALLOW] = COLOR(REDORANGE1_HIGH_TRANS, BLACK1_HIGH_TRANS, REDORANGE1_HIGH_TRANS); profile_color[CEILING_DEEP] = COLOR(RED1_MED_TRANS, BLACK1_HIGH_TRANS, RED1_MED_TRANS); profile_color[CALC_CEILING_SHALLOW] = COLOR(FUNGREEN1_HIGH_TRANS, BLACK1_HIGH_TRANS, FUNGREEN1_HIGH_TRANS); @@ -56,3 +56,7 @@ void fill_profile_color() #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 f9d36342f..42250b42c 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -12,17 +12,20 @@ #include "divelistview.h" #include "modeldelegates.h" #include "globe.h" +#include "completionmodels.h" #include <QLabel> +#include <QCompleter> #include <QDebug> #include <QSet> +#include <QTableView> #include <QSettings> +#include <QPalette> MainTab::MainTab(QWidget *parent) : QTabWidget(parent), ui(new Ui::MainTab()), weightModel(new WeightModel()), cylindersModel(new CylindersModel()), - currentDive(0), editMode(NONE) { ui->setupUi(this); @@ -52,7 +55,7 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent), ui->divemaster->installEventFilter(this); ui->buddy->installEventFilter(this); ui->suit->installEventFilter(this); - ui->notes->installEventFilter(this); + ui->notes->viewport()->installEventFilter(this); ui->rating->installEventFilter(this); ui->visibility->installEventFilter(this); @@ -62,82 +65,51 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent), if (label) label->setAlignment(Qt::AlignHCenter); } - - /*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))); - 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(); + ui->cylinders->setTitle(tr("Cylinders")); + ui->cylinders->setBtnToolTip(tr("Add Cylinder")); + connect(ui->cylinders, SIGNAL(addButtonClicked()), this, SLOT(addCylinder_clicked())); + + 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); + completers.location = new QCompleter(LocationCompletionModel::instance(), ui->location); + completers.suit = new QCompleter(SuitCompletionModel::instance(), ui->suit); + ui->buddy->setCompleter(completers.buddy); + ui->divemaster->setCompleter(completers.divemaster); + ui->location->setCompleter(completers.location); + ui->suit->setCompleter(completers.suit); } -void MainTab::tabChanged(int idx) +void MainTab::enableEdition() { - /* if the current tab has become of index 1 (i.e. the equipment tab) call update - * for the plus signs */ - if (idx == 1) - equipmentPlusUpdate(); -} + if (ui->editAccept->isVisible() || !selected_dive) + return; -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); + ui->editAccept->setChecked(true); + ui->editAccept->show(); + ui->editReset->show(); + on_editAccept_clicked(true); } bool MainTab::eventFilter(QObject* object, QEvent* event) { - if (event->type() == QEvent::FocusIn || event->type() == QEvent::MouseButtonPress) { - if (ui->editAccept->isVisible() || !currentDive) - return false; - - ui->editAccept->setChecked(true); - ui->editAccept->show(); - ui->editReset->show(); - on_editAccept_clicked(true); + if (event->type() == QEvent::FocusIn && (object == ui->rating || object == ui->visibility)){ + enableEdition(); } - return false; + + if (event->type() == QEvent::MouseButtonPress) { + enableEdition(); + } + return false; // don't "eat" the event. } void MainTab::clearEquipment() @@ -170,15 +142,15 @@ 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; // 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 @@ -191,7 +163,7 @@ void MainTab::updateDiveInfo(int dive) process_selected_dives(); process_all_dives(d, &prevd); - currentDive = d; + UPDATE_TEXT(d, notes); UPDATE_TEXT(d, location); UPDATE_TEXT(d, suit); @@ -225,6 +197,8 @@ void MainTab::updateDiveInfo(int dive) ui->suit->setVisible(true); ui->rating->setVisible(true); ui->visibility->setVisible(true); + ui->BuddyLabel->setVisible(true); + ui->DivemasterLabel->setVisible(true); ui->divemaster->setReadOnly(false); ui->buddy->setReadOnly(false); ui->suit->setReadOnly(false); @@ -284,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); @@ -312,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(); @@ -337,6 +307,10 @@ void MainTab::addWeight_clicked() void MainTab::reload() { + SuitCompletionModel::instance()->updateModel(); + BuddyCompletionModel::instance()->updateModel(); + LocationCompletionModel::instance()->updateModel(); + DiveMasterCompletionModel::instance()->updateModel(); } void MainTab::on_editAccept_clicked(bool edit) @@ -352,24 +326,39 @@ void MainTab::on_editAccept_clicked(bool edit) mainWindow()->dive_list()->setEnabled(!edit); if (edit) { + + // We may be editing one or more dives here. backup everything. + notesBackup.clear(); + 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(); + notesBackup[NULL].notes = ui->notes->toPlainText(); + notesBackup[NULL].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; + + // We may be editing one or more dives here. backup everything. + struct dive *mydive; + for (int i = 0; i < dive_table.nr; i++) { + mydive = get_dive(i); + if (!mydive) + continue; + if (!mydive->selected) + continue; + + notesBackup[mydive].buddy = QString(mydive->buddy); + notesBackup[mydive].suit = QString(mydive->suit); + notesBackup[mydive].notes = QString(mydive->notes); + notesBackup[mydive].divemaster = QString(mydive->divemaster); + notesBackup[mydive].location = QString(mydive->location); + notesBackup[mydive].rating = mydive->rating; + notesBackup[mydive].visibility = mydive->visibility; + } + editMode = DIVE; } } else { ui->diveNotesMessage->animatedHide(); @@ -377,38 +366,78 @@ void MainTab::on_editAccept_clicked(bool edit) ui->editReset->hide(); /* now figure out if things have changed */ if (mainWindow() && mainWindow()->dive_list()->selectedTrips.count() == 1) { - if (notesBackup.notes != ui->notes->toPlainText() || - notesBackup.location != ui->location->text()) + if (notesBackup[NULL].notes != ui->notes->toPlainText() || + notesBackup[NULL].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()) + struct dive *curr = current_dive; + if (notesBackup[curr].buddy != ui->buddy->text() || + notesBackup[curr].suit != ui->suit->text() || + notesBackup[curr].notes != ui->notes->toPlainText() || + notesBackup[curr].divemaster != ui->divemaster->text() || + notesBackup[curr].location != ui->location->text() || + notesBackup[curr].rating != ui->visibility->currentStars() || + notesBackup[curr].visibility != ui->rating->currentStars()) + mark_divelist_changed(TRUE); - if (notesBackup.location != ui->location->text()) + if (notesBackup[curr].location != ui->location->text()) mainWindow()->globe()->reload(); } editMode = NONE; } + QPalette p; + ui->buddy->setPalette(p); + ui->notes->setPalette(p); + ui->location->setPalette(p); + ui->divemaster->setPalette(p); + ui->suit->setPalette(p); } +#define EDIT_TEXT2(what, text) \ + textByteArray = text.toLocal8Bit(); \ + free(what);\ + what = strdup(textByteArray.data()); + +#define EDIT_TEXT(what, text) \ + QByteArray textByteArray = text.toLocal8Bit(); \ + free(what);\ + what = strdup(textByteArray.data()); + void MainTab::on_editReset_clicked() { if (!ui->editAccept->isChecked()) return; - ui->notes->setText(notesBackup.notes); - ui->location->setText(notesBackup.location); - 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); + if (mainWindow() && mainWindow()->dive_list()->selectedTrips.count() == 1){ + ui->notes->setText(notesBackup[NULL].notes ); + ui->location->setText(notesBackup[NULL].location); + }else{ + struct dive *curr = current_dive; + ui->notes->setText(notesBackup[curr].notes ); + ui->location->setText(notesBackup[curr].location); + ui->buddy->setText(notesBackup[curr].buddy); + ui->suit->setText(notesBackup[curr].suit); + ui->divemaster->setText(notesBackup[curr].divemaster); + ui->rating->setCurrentStars(notesBackup[curr].rating); + ui->visibility->setCurrentStars(notesBackup[curr].visibility); + + struct dive *mydive; + for (int i = 0; i < dive_table.nr; i++) { + mydive = get_dive(i); + if (!mydive) + continue; + if (!mydive->selected) + continue; + + QByteArray textByteArray; + EDIT_TEXT2(mydive->buddy, notesBackup[mydive].buddy); + EDIT_TEXT2(mydive->suit, notesBackup[mydive].suit); + EDIT_TEXT2(mydive->notes, notesBackup[mydive].notes); + EDIT_TEXT2(mydive->divemaster, notesBackup[mydive].divemaster); + EDIT_TEXT2(mydive->location, notesBackup[mydive].location); + mydive->rating = notesBackup[mydive].rating; + mydive->visibility = notesBackup[mydive].visibility; + } } ui->editAccept->setChecked(false); ui->diveNotesMessage->animatedHide(); @@ -424,126 +453,105 @@ void MainTab::on_editReset_clicked() ui->editAccept->hide(); ui->editReset->hide(); + notesBackup.clear(); + QPalette p; + ui->buddy->setPalette(p); + ui->notes->setPalette(p); + ui->location->setPalette(p); + ui->divemaster->setPalette(p); + ui->suit->setPalette(p); editMode = NONE; } +#undef EDIT_TEXT2 + +#define EDIT_SELECTED_DIVES( WHAT ) \ + if (editMode == NONE) \ + return; \ + struct dive *mydive; \ +\ + for (int i = 0; i < dive_table.nr; i++) { \ + mydive = get_dive(i); \ + if (!mydive) \ + continue; \ + if (!mydive->selected) \ + continue; \ +\ + WHAT; \ + } -#define EDIT_TEXT(what, text) \ - QByteArray textByteArray = text.toLocal8Bit(); \ - free(what);\ - what = strdup(textByteArray.data()); +void markChangedWidget(QWidget *w){ + QPalette p; + p.setBrush(QPalette::Base, QColor(Qt::yellow).lighter()); + w->setPalette(p); +} void MainTab::on_buddy_textChanged(const QString& text) { - if (!currentDive) - return; - EDIT_TEXT(currentDive->buddy, text); + EDIT_SELECTED_DIVES( EDIT_TEXT(mydive->buddy, text) ); + markChangedWidget(ui->buddy); } void MainTab::on_divemaster_textChanged(const QString& text) { - if (!currentDive) - return; - EDIT_TEXT(currentDive->divemaster, text); + EDIT_SELECTED_DIVES( EDIT_TEXT(mydive->divemaster, text) ); + markChangedWidget(ui->divemaster); } void MainTab::on_location_textChanged(const QString& text) { + if (editMode == NONE) + return; 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); + EDIT_SELECTED_DIVES( EDIT_TEXT(mydive->location, text) ) } + + markChangedWidget(ui->location); } void MainTab::on_suit_textChanged(const QString& text) { - if (!currentDive) - return; - EDIT_TEXT(currentDive->suit, text); + EDIT_SELECTED_DIVES( EDIT_TEXT(mydive->suit, text) ); + markChangedWidget(ui->suit); } void MainTab::on_notes_textChanged() { + if (editMode == NONE) + return; 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()); + EDIT_SELECTED_DIVES( EDIT_TEXT(mydive->notes, ui->notes->toPlainText()) ); } + markChangedWidget(ui->notes); } #undef EDIT_TEXT void MainTab::on_rating_valueChanged(int value) { - if (!currentDive) - return; - currentDive->rating = value; + EDIT_SELECTED_DIVES(mydive->rating = value ); } void MainTab::on_visibility_valueChanged(int value) { - if (!currentDive) - 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(); + EDIT_SELECTED_DIVES( mydive->visibility = value ); } 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 af76fbb67..f9f65a3ff 100644 --- a/qt-ui/maintab.h +++ b/qt-ui/maintab.h @@ -9,9 +9,12 @@ #include <QTabWidget> #include <QDialog> +#include <QMap> #include "models.h" +class QCompleter; +struct dive; namespace Ui { class MainTab; @@ -27,6 +30,13 @@ struct NotesBackup{ QString divemaster; }; +struct Completers{ + QCompleter *location; + QCompleter *divemaster; + QCompleter *buddy; + QCompleter *suit; +}; + class MainTab : public QTabWidget { Q_OBJECT @@ -36,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(); @@ -59,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); @@ -67,11 +69,10 @@ private: Ui::MainTab *ui; WeightModel *weightModel; CylindersModel *cylindersModel; - NotesBackup notesBackup; - struct dive* currentDive; - QPushButton *addCylinder; - QPushButton *addWeight; + QMap<dive*, NotesBackup> notesBackup; enum { NONE, DIVE, TRIP } editMode; + Completers completers; + void enableEdition(); }; #endif 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 9ccf6608f..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,7 +15,7 @@ #include <QLineEdit> #include <QKeyEvent> #include <QAbstractItemView> -#include <boost/concept_check.hpp> +#include <QStringListModel> // Gets the index of the model in the currentRow and column. // currCombo is defined below. @@ -107,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){ @@ -200,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); } } @@ -237,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) @@ -250,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 0af5c7019..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) { @@ -1438,3 +1457,109 @@ void YearlyStatisticsModel::update_yearly_stats() item->parent = rootItem; } } + +/*################################################################# + * # + * # Table Print Model + * # + * ################################################################ + */ +TablePrintModel::TablePrintModel() +{ + columns = 7; + rows = 0; +} + +TablePrintModel::~TablePrintModel() +{ + for (int i = 0; i < list.size(); i++) + delete list.at(i); +} + +void TablePrintModel::insertRow(int index) +{ + struct TablePrintItem *item = new struct TablePrintItem(); + item->colorBackground = 0xffffffff; + if (index == -1) { + beginInsertRows(QModelIndex(), rows, rows); + list.append(item); + } else { + beginInsertRows(QModelIndex(), index, index); + list.insert(index, item); + } + endInsertRows(); + rows++; +} + +void TablePrintModel::callReset() +{ + reset(); +} + +QVariant TablePrintModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + if (role == Qt::BackgroundRole) + return QColor(list.at(index.row())->colorBackground); + if (role == Qt::DisplayRole) + switch (index.column()) { + case 0: + return list.at(index.row())->number; + case 1: + return list.at(index.row())->date; + case 2: + return list.at(index.row())->depth; + case 3: + return list.at(index.row())->duration; + case 4: + return list.at(index.row())->divemaster; + case 5: + return list.at(index.row())->buddy; + case 6: + return list.at(index.row())->location; + } + return QVariant(); +} + +bool TablePrintModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid()) { + if (role == Qt::DisplayRole) { + switch (index.column()) { + case 0: + list.at(index.row())->number = value.toString(); + case 1: + list.at(index.row())->date = value.toString(); + case 2: + list.at(index.row())->depth = value.toString(); + case 3: + list.at(index.row())->duration = value.toString(); + case 4: + list.at(index.row())->divemaster = value.toString(); + case 5: + list.at(index.row())->buddy = value.toString(); + case 6: + list.at(index.row())->location = value.toString(); + } + return true; + } + if (role == Qt::BackgroundRole) { + list.at(index.row())->colorBackground = value.value<unsigned int>(); + return true; + } + } + return false; +} + +int TablePrintModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return rows; +} + +int TablePrintModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns; +} diff --git a/qt-ui/models.h b/qt-ui/models.h index e8a7daaf3..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; @@ -230,4 +231,45 @@ public: YearlyStatisticsModel(QObject* parent = 0); void update_yearly_stats(); }; + +/* TablePrintModel: + * for now we use a blank table model with row items TablePrintItem. + * these are pretty much the same as DiveItem, but have color + * properties, as well. perhaps later one a more unified model has to be + * considered, but the current TablePrintModel idea has to be extended + * to support variadic column lists and column list orders that can + * be controlled by the user. + */ +struct TablePrintItem { + QString number; + QString date; + QString depth; + QString duration; + QString divemaster; + QString buddy; + QString location; + unsigned int colorBackground; +}; + +class TablePrintModel : public QAbstractTableModel +{ + Q_OBJECT + +private: + QList<struct TablePrintItem *> list; + +public: + ~TablePrintModel(); + TablePrintModel(); + + int rows, columns; + void insertRow(int index = -1); + void callReset(); + + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; +}; + #endif 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/printdialog.cpp b/qt-ui/printdialog.cpp index 542590707..f4bf16d05 100644 --- a/qt-ui/printdialog.cpp +++ b/qt-ui/printdialog.cpp @@ -47,6 +47,7 @@ void PrintDialog::printClicked(void) // printer.setOutputFileName("print.pdf"); // printer.setOutputFormat(QPrinter::PdfFormat); // temporary: use a preview dialog + printer.setResolution(300); QPrintPreviewDialog previewDialog(&printer, this); QObject::connect(&previewDialog, SIGNAL(paintRequested(QPrinter *)), this, SLOT(onPaintRequested(QPrinter *))); previewDialog.exec(); diff --git a/qt-ui/printlayout.cpp b/qt-ui/printlayout.cpp index 2184f7366..7a4944ede 100644 --- a/qt-ui/printlayout.cpp +++ b/qt-ui/printlayout.cpp @@ -1,9 +1,12 @@ +#include <QtCore/qmath.h> #include <QDebug> #include <QPainter> #include <QDesktopWidget> #include <QApplication> -#include <QTextDocument> +#include <QTableView> +#include <QHeaderView> #include "mainwindow.h" +#include "profilegraphics.h" #include "printlayout.h" #include "../dive.h" #include "../display.h" @@ -19,41 +22,34 @@ struct options { }; */ -#define TABLE_PRINT_COL 7 - PrintLayout::PrintLayout(PrintDialog *dialogPtr, QPrinter *printerPtr, struct options *optionsPtr) { dialog = dialogPtr; printer = printerPtr; printOptions = optionsPtr; - // painter = new QPainter(printer); // table print settings - tableColumnNames.append(tr("Dive#")); - tableColumnNames.append(tr("Date")); - tableColumnNames.append(tr("Depth")); - tableColumnNames.append(tr("Duration")); - tableColumnNames.append(tr("Master")); - tableColumnNames.append(tr("Buddy")); - tableColumnNames.append(tr("Location")); - tableColumnWidths.append("7"); - tableColumnWidths.append("10"); - tableColumnWidths.append("10"); - tableColumnWidths.append("10"); - tableColumnWidths.append("15"); - tableColumnWidths.append("15"); - tableColumnWidths.append("100"); + tablePrintHeadingBackground = 0xffeeeeee; + tablePrintColumnNames.append(tr("Dive#")); + tablePrintColumnNames.append(tr("Date")); + tablePrintColumnNames.append(tr("Depth")); + tablePrintColumnNames.append(tr("Duration")); + tablePrintColumnNames.append(tr("Master")); + tablePrintColumnNames.append(tr("Buddy")); + tablePrintColumnNames.append(tr("Location")); + tablePrintColumnWidths.append(7); + tablePrintColumnWidths.append(10); + tablePrintColumnWidths.append(10); + tablePrintColumnWidths.append(10); + tablePrintColumnWidths.append(15); + tablePrintColumnWidths.append(15); + tablePrintColumnWidths.append(33); } void PrintLayout::print() { // we call setup each time to check if the printer properties have changed setup(); - - // temp / debug - printTable(); - return; - // ------------ switch (printOptions->type) { case options::PRETTY: printSixDives(); @@ -78,11 +74,51 @@ void PrintLayout::setup() scaleX = (qreal)printerDpi/(qreal)screenDpiX; scaleY = (qreal)printerDpi/(qreal)screenDpiY; + + // a printer page scalled to screen DPI + scaledPageW = pageRect.width() / scaleX; + scaledPageH = pageRect.height() / scaleY; } +// experimental void PrintLayout::printSixDives() const { - // nop + ProfileGraphicsView *profile = mainWindow()->graphics(); + QPainter painter; + painter.begin(printer); + painter.setRenderHint(QPainter::Antialiasing); + // painter.setRenderHint(QPainter::HighQualityAntialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.scale(scaleX, scaleY); + + profile->clear(); + profile->setPrintMode(true, !printOptions->color_selected); + QSize originalSize = profile->size(); + profile->resize(scaledPageW, scaledPageH); + + int i; + struct dive *dive; + bool firstPage = true; + for_each_dive(i, dive) { + if (!dive->selected && printOptions->print_selected) + continue; + // don't create a new page if still on first page + if (!firstPage) + printer->newPage(); + else + firstPage = false; + profile->plot(dive, true); + QPixmap pm = QPixmap::grabWidget(profile); + QTransform transform; + transform.rotate(270); + pm = QPixmap(pm.transformed(transform)); + painter.drawPixmap(0, 0, pm); + } + painter.end(); + profile->setPrintMode(false); + profile->resize(originalSize); + profile->clear(); + profile->plot(current_dive, true); } void PrintLayout::printTwoDives() const @@ -90,105 +126,112 @@ void PrintLayout::printTwoDives() const // nop } -void PrintLayout::printTable() const +void PrintLayout::printTable() { - QTextDocument doc; - QSizeF pageSize; - pageSize.setWidth(pageRect.width()); - pageSize.setHeight(pageRect.height()); - doc.setPageSize(pageSize); - - QString styleSheet( - "<style type='text/css'>" - "table {" - " border-width: 1px;" - " border-style: solid;" - " border-color: #999999;" - "}" - "th {" - " background-color: #eeeeee;" - " font-size: small;" - " padding: 3px 5px 3px 5px;" - "}" - "td {" - " font-size: small;" - " padding: 3px 5px 3px 5px;" - "}" - "</style>" - ); - // setDefaultStyleSheet() doesn't work here? - QString htmlText = styleSheet + "<table cellspacing='0' width='100%'>"; - QString htmlTextPrev; - int pageCountNew = 1, pageCount; - bool insertHeading = true; - - int i; + // create and setup a table + QTableView table; + table.setAttribute(Qt::WA_DontShowOnScreen); + table.setSelectionMode(QAbstractItemView::NoSelection); + table.setFocusPolicy(Qt::NoFocus); + table.horizontalHeader()->setVisible(false); + table.horizontalHeader()->setResizeMode(QHeaderView::Fixed); + table.verticalHeader()->setVisible(false); + table.verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); + table.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + table.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + // fit table to one page initially + table.resize(scaledPageW, scaledPageH); + + // create and fill a table model + TablePrintModel model; struct dive *dive; + int i, row = 0; + addTablePrintHeadingRow(&model, row); // add one heading row + row++; for_each_dive(i, dive) { if (!dive->selected && printOptions->print_selected) continue; - if (insertHeading) { - htmlText += insertTableHeadingRow(); - insertHeading = false; - } - htmlTextPrev = htmlText; - htmlText += insertTableDataRow(dive); - doc.setHtml(htmlText); - pageCount = pageCountNew; - pageCountNew = doc.pageCount(); - /* if the page count increases after adding this row we 'revert' - * and add a heading instead. */ - if (pageCountNew > pageCount) { - htmlText = htmlTextPrev; - insertHeading = true; + addTablePrintDataRow(&model, row, dive); + row++; + } + table.setModel(&model); // set model to table + // resize columns to percentages from page width + for (int i = 0; i < model.columns; i++) { + int pw = qCeil((qreal)(tablePrintColumnWidths.at(i) * table.width()) / 100); + table.horizontalHeader()->resizeSection(i, pw); + } + // reset the model at this point + model.callReset(); + + // a list of vertical offsets where pages begin and some helpers + QList<unsigned int> pageIndexes; + pageIndexes.append(0); + int tableHeight = 0, rowH = 0, accH = 0; + + // process all rows + for (int i = 0; i < model.rows; i++) { + rowH = table.rowHeight(i); + accH += rowH; + if (accH > scaledPageH) { // push a new page index and add a heading + pageIndexes.append(pageIndexes.last() + (accH - rowH)); + addTablePrintHeadingRow(&model, i); + accH = 0; i--; } + tableHeight += rowH; + } + pageIndexes.append(pageIndexes.last() + accH); + // resize the whole widget so that it can be rendered + table.resize(scaledPageW, tableHeight); + + // attach a painter and render pages by using pageIndexes + QPainter painter(printer); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.scale(scaleX, scaleY); + for (int i = 0; i < pageIndexes.size() - 1; i++) { + if (i > 0) + printer->newPage(); + QRegion region(0, pageIndexes.at(i) - 1, + table.width(), + pageIndexes.at(i + 1) - pageIndexes.at(i) + 2); + table.render(&painter, QPoint(0, 0), region); } - htmlText += "</table>"; - doc.setHtml(htmlText); - doc.print(printer); -} - -QString PrintLayout::insertTableHeadingRow() const -{ - int i; - QString ret("<tr>"); - for (i = 0; i < TABLE_PRINT_COL; i++) - ret += insertTableHeadingCol(i); - ret += "</tr>"; - return ret; -} - -QString PrintLayout::insertTableHeadingCol(int col) const -{ - QString ret("<th align='left' width='"); - ret += tableColumnWidths.at(col); - ret += "%'>"; - ret += tableColumnNames.at(col); - ret += "</th>"; - return ret; } -QString PrintLayout::insertTableDataRow(struct dive *dive) const +void PrintLayout::addTablePrintDataRow(TablePrintModel *model, int row, struct dive *dive) const { - // use the DiveItem class struct DiveItem di; di.dive = dive; + model->insertRow(); + model->setData(model->index(row, 0), QString::number(dive->number), Qt::DisplayRole); + model->setData(model->index(row, 1), di.displayDate(), Qt::DisplayRole); + model->setData(model->index(row, 2), di.displayDepth(), Qt::DisplayRole); + model->setData(model->index(row, 3), di.displayDuration(), Qt::DisplayRole); + model->setData(model->index(row, 4), dive->divemaster, Qt::DisplayRole); + model->setData(model->index(row, 5), dive->buddy, Qt::DisplayRole); + model->setData(model->index(row, 6), dive->location, Qt::DisplayRole); +} - // fill row - QString ret("<tr>"); - ret += insertTableDataCol(QString::number(dive->number)); - ret += insertTableDataCol(di.displayDate()); - ret += insertTableDataCol(di.displayDepth()); - ret += insertTableDataCol(di.displayDuration()); - ret += insertTableDataCol(dive->divemaster); - ret += insertTableDataCol(dive->buddy); - ret += insertTableDataCol(dive->location); - ret += "</tr>"; - return ret; +void PrintLayout::addTablePrintHeadingRow(TablePrintModel *model, int row) const +{ + model->insertRow(row); + for (int i = 0; i < model->columns; i++) { + model->setData(model->index(row, i), tablePrintColumnNames.at(i), Qt::DisplayRole); + model->setData(model->index(row, i), tablePrintHeadingBackground, Qt::BackgroundRole); + } } -QString PrintLayout::insertTableDataCol(QString data) const +// experimental +QPixmap PrintLayout::convertPixmapToGrayscale(QPixmap pixmap) const { - return "<td>" + data + "</td>"; + QImage image = pixmap.toImage(); + int gray, width = pixmap.width(), height = pixmap.height(); + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + gray = qGray(image.pixel(i, j)); + image.setPixel(i, j, qRgb(gray, gray, gray)); + } + } + return pixmap.fromImage(image); } diff --git a/qt-ui/printlayout.h b/qt-ui/printlayout.h index cbb1e42ac..ac363ab8d 100644 --- a/qt-ui/printlayout.h +++ b/qt-ui/printlayout.h @@ -1,10 +1,13 @@ #ifndef PRINTLAYOUT_H #define PRINTLAYOUT_H +#include <QObject> #include <QPrinter> -#include <QStringList> +#include <QList> class PrintDialog; +class TablePrintModel; +struct dive; class PrintLayout : public QObject { Q_OBJECT @@ -19,21 +22,22 @@ private: struct options *printOptions; QPainter *painter; - int screenDpiX, screenDpiY, printerDpi; + int screenDpiX, screenDpiY, printerDpi, scaledPageW, scaledPageH; qreal scaleX, scaleY; QRect pageRect; - QStringList tableColumnNames; - QStringList tableColumnWidths; + QList<QString> tablePrintColumnNames; + QList<unsigned int> tablePrintColumnWidths; + unsigned int tablePrintHeadingBackground; void setup(); void printSixDives() const; void printTwoDives() const; - void printTable() const; - QString insertTableHeadingRow() const; - QString insertTableHeadingCol(int) const; - QString insertTableDataRow(struct dive *) const; - QString insertTableDataCol(QString) const; + void printTable(); + void addTablePrintDataRow(TablePrintModel *model, int row, struct dive *dive) const; + void addTablePrintHeadingRow(TablePrintModel *model, int row) const; + + QPixmap convertPixmapToGrayscale(QPixmap) const; }; #endif diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp index 264e02d96..ad412e4b0 100644 --- a/qt-ui/profilegraphics.cpp +++ b/qt-ui/profilegraphics.cpp @@ -1,7 +1,6 @@ #include "profilegraphics.h" #include "mainwindow.h" #include "divelistview.h" -#include "graphicsview-common.h" #include <QGraphicsScene> #include <QResizeEvent> @@ -46,11 +45,12 @@ extern int evn_used; ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent), toolTip(0) , dive(0), diveDC(0) { + printMode = false; + isGrayscale = false; gc.printer = false; fill_profile_color(); setScene(new QGraphicsScene()); - setBackgroundBrush(profile_color[BACKGROUND].at(0)); scene()->installEventFilter(this); setRenderHint(QPainter::Antialiasing); @@ -189,6 +189,17 @@ void ProfileGraphicsView::refresh() plot(current_dive, TRUE); } +void ProfileGraphicsView::setPrintMode(bool mode, bool grayscale) +{ + printMode = mode; + isGrayscale = grayscale; +} + +QColor ProfileGraphicsView::getColor(const color_indice_t i) +{ + return profile_color[i].at((isGrayscale) ? 1 : 0); +} + void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) { struct divecomputer *dc; @@ -206,6 +217,7 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) if (!isVisible() || !dive) { return; } + setBackgroundBrush(getColor(BACKGROUND)); // best place to put the focus stealer code. setFocusProxy(mainWindow()->dive_list()); @@ -214,6 +226,8 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) toolTip = new ToolTipItem(); installEventFilter(toolTip); scene()->addItem(toolTip); + if (printMode) + toolTip->setVisible(false); // Fix this for printing / screen later. // plot_set_scale(scale_mode_t); @@ -250,7 +264,7 @@ void ProfileGraphicsView::plot(struct dive *d, bool forceRedraw) /* Bounding box */ QPen pen = defaultPen; - pen.setColor(profile_color[TIME_GRID].at(0)); + pen.setColor(getColor(TIME_GRID)); QGraphicsRectItem *rect = new QGraphicsRectItem(profile_grid_area); rect->setPen(pen); scene()->addItem(rect); @@ -338,7 +352,7 @@ void ProfileGraphicsView::plot_depth_scale() case units::FEET: marker = 9144; break; /* 30 ft */ } - QColor c(profile_color[DEPTH_GRID].first()); + QColor c(getColor(DEPTH_GRID)); /* don't write depth labels all the way to the bottom as * there may be other graphs below the depth plot (like @@ -364,7 +378,7 @@ void ProfileGraphicsView::plot_pp_text() pp = floor(gc.pi.maxpp * 10.0) / 10.0 + 0.2; dpp = pp > 4 ? 1.0 : 0.5; hpos = gc.pi.entry[gc.pi.nr - 1].sec; - QColor c = profile_color[PP_LINES].first(); + QColor c = getColor(PP_LINES); for (m = 0.0; m <= pp; m += dpp) { QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(0, m), SCALEGC(hpos, m)); @@ -397,7 +411,7 @@ void ProfileGraphicsView::plot_pp_gas_profile() QColor c; QPointF from, to; if (prefs.pp_graphs.pn2) { - c = profile_color[PN2].first(); + c = getColor(PN2); entry = pi->entry; from = QPointF(SCALEGC(entry->sec, entry->pn2)); for (i = 1; i < pi->nr; i++) { @@ -408,7 +422,7 @@ void ProfileGraphicsView::plot_pp_gas_profile() from = QPointF(SCALEGC(entry->sec, entry->pn2)); } - c = profile_color[PN2_ALERT].first(); + c = getColor(PN2_ALERT); entry = pi->entry; from = QPointF(SCALEGC(entry->sec, entry->pn2)); for (i = 1; i < pi->nr; i++) { @@ -421,7 +435,7 @@ void ProfileGraphicsView::plot_pp_gas_profile() } if (prefs.pp_graphs.phe) { - c = profile_color[PHE].first(); + c = getColor(PHE); entry = pi->entry; from = QPointF(SCALEGC(entry->sec, entry->phe)); @@ -433,7 +447,7 @@ void ProfileGraphicsView::plot_pp_gas_profile() from = QPointF(SCALEGC(entry->sec, entry->phe)); } - c = profile_color[PHE_ALERT].first(); + c = getColor(PHE_ALERT); entry = pi->entry; from = QPointF(SCALEGC(entry->sec, entry->phe)); for (i = 1; i < pi->nr; i++) { @@ -445,7 +459,7 @@ void ProfileGraphicsView::plot_pp_gas_profile() } } if (prefs.pp_graphs.po2) { - c = profile_color[PO2].first(); + c = getColor(PO2); entry = pi->entry; from = QPointF(SCALEGC(entry->sec, entry->po2)); for (i = 1; i < pi->nr; i++) { @@ -456,7 +470,7 @@ void ProfileGraphicsView::plot_pp_gas_profile() from = QPointF(SCALEGC(entry->sec, entry->po2)); } - c = profile_color[PO2_ALERT].first(); + c = getColor(PO2_ALERT); entry = pi->entry; from = QPointF(SCALEGC(entry->sec, entry->po2)); for (i = 1; i < pi->nr; i++) { @@ -739,9 +753,9 @@ QColor ProfileGraphicsView::get_sac_color(int sac, int avg_sac) sac_index = 0; if (sac_index > SAC_COLORS - 1) sac_index = SAC_COLORS - 1; - return profile_color[ (color_indice_t) (SAC_COLORS_START_IDX + sac_index)].first(); + return getColor((color_indice_t)(SAC_COLORS_START_IDX + sac_index)); } - return profile_color[SAC_DEFAULT].first(); + return getColor(SAC_DEFAULT); } void ProfileGraphicsView::plot_events(struct divecomputer *dc) @@ -792,7 +806,7 @@ void ProfileGraphicsView::plot_one_event(struct event *ev) int x = SCALEXGC(ev->time.seconds); int y = SCALEYGC(depth); - EventItem *item = new EventItem(); + EventItem *item = new EventItem(0, isGrayscale); item->setPos(x, y); scene()->addItem(item); @@ -816,8 +830,8 @@ void ProfileGraphicsView::plot_one_event(struct event *ev) } else if (ev->name && name == "SP change") { name += tr("Bailing out to OC"); } else { - name += ev->flags == SAMPLE_FLAGS_BEGIN ? tr("Starts with space!"," begin") : - ev->flags == SAMPLE_FLAGS_END ? tr("Starts with space!", " end") : ""; + name += ev->flags == SAMPLE_FLAGS_BEGIN ? tr(" begin", "Starts with space!") : + ev->flags == SAMPLE_FLAGS_END ? tr(" end", "Starts with space!") : ""; } //item->setToolTipController(toolTip); @@ -858,7 +872,7 @@ void ProfileGraphicsView::plot_depth_profile() last_gc = gc; - QColor c = profile_color[TIME_GRID].at(0); + QColor c = getColor(TIME_GRID); for (i = incr; i < maxtime; i += incr) { QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(i, 0), SCALEGC(i, 1)); QPen pen(defaultPen); @@ -895,7 +909,7 @@ void ProfileGraphicsView::plot_depth_profile() } maxline = MAX(gc.pi.maxdepth + marker, maxdepth * 2 / 3); - c = profile_color[DEPTH_GRID].at(0); + c = getColor(DEPTH_GRID); for (i = marker; i < maxline; i += marker) { QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(0, i), SCALEGC(1, i)); @@ -906,7 +920,7 @@ void ProfileGraphicsView::plot_depth_profile() } gc.leftx = 0; gc.rightx = maxtime; - c = profile_color[MEAN_DEPTH].at(0); + c = getColor(MEAN_DEPTH); /* Show mean depth */ if (! gc.printer) { @@ -955,8 +969,8 @@ void ProfileGraphicsView::plot_depth_profile() } } } - pat.setColorAt(1, profile_color[DEPTH_BOTTOM].first()); - pat.setColorAt(0, profile_color[DEPTH_TOP].first()); + pat.setColorAt(1, getColor(DEPTH_BOTTOM)); + pat.setColorAt(0, getColor(DEPTH_TOP)); neatFill = new QGraphicsPolygonItem(); neatFill->setPolygon(p); @@ -970,8 +984,8 @@ void ProfileGraphicsView::plot_depth_profile() * through so far) */ if (prefs.profile_dc_ceiling && prefs.profile_red_ceiling) { p.clear(); - pat.setColorAt(0, profile_color[CEILING_SHALLOW].first()); - pat.setColorAt(1, profile_color[CEILING_DEEP].first()); + pat.setColorAt(0, getColor(CEILING_SHALLOW)); + pat.setColorAt(1, getColor(CEILING_DEEP)); entry = gc.pi.entry; p.append(QPointF(SCALEGC(0, 0))); @@ -996,8 +1010,8 @@ void ProfileGraphicsView::plot_depth_profile() /* finally, plot the calculated ceiling over all this */ if (prefs.profile_calc_ceiling) { - pat.setColorAt(0, profile_color[CALC_CEILING_SHALLOW].first()); - pat.setColorAt(1, profile_color[CALC_CEILING_DEEP].first()); + pat.setColorAt(0, getColor(CALC_CEILING_SHALLOW)); + pat.setColorAt(1, getColor(CALC_CEILING_DEEP)); entry = gc.pi.entry; p.clear(); @@ -1020,7 +1034,7 @@ void ProfileGraphicsView::plot_depth_profile() if (prefs.profile_calc_ceiling && prefs.calc_all_tissues){ int k; for (k=0; k<16; k++){ - pat.setColorAt(0, profile_color[CALC_CEILING_SHALLOW].first()); + pat.setColorAt(0, getColor(CALC_CEILING_SHALLOW)); pat.setColorAt(1, QColor(100, 100, 100, 50)); entry = gc.pi.entry; @@ -1041,8 +1055,8 @@ void ProfileGraphicsView::plot_depth_profile() } /* next show where we have been bad and crossed the dc's ceiling */ if (prefs.profile_dc_ceiling) { - pat.setColorAt(0, profile_color[CEILING_SHALLOW].first()); - pat.setColorAt(1, profile_color[CEILING_DEEP].first()); + pat.setColorAt(0, getColor(CEILING_SHALLOW)); + pat.setColorAt(1, getColor(CEILING_DEEP)); entry = gc.pi.entry; p.clear(); @@ -1075,7 +1089,7 @@ void ProfileGraphicsView::plot_depth_profile() depth = entry->depth; QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(entry[-1].sec, entry[-1].depth), SCALEGC(sec, depth)); QPen pen(defaultPen); - pen.setColor(profile_color[ (color_indice_t) (VELOCITY_COLORS_START_IDX + entry->velocity)].first()); + pen.setColor(getColor((color_indice_t)(VELOCITY_COLORS_START_IDX + entry->velocity))); item->setPen(pen); scene()->addItem(item); } @@ -1098,15 +1112,16 @@ QGraphicsItemGroup *ProfileGraphicsView::plot_text(text_render_options_t *tro,co QPainterPathStroker stroker; stroker.setWidth(3); QGraphicsPathItem *strokedItem = new QGraphicsPathItem(stroker.createStroke(textPath), group); - strokedItem->setBrush(QBrush(profile_color[TEXT_BACKGROUND].first())); + strokedItem->setBrush(QBrush(getColor(TEXT_BACKGROUND))); strokedItem->setPen(Qt::NoPen); QGraphicsPathItem *textItem = new QGraphicsPathItem(textPath, group); - textItem->setBrush(QBrush(profile_color[tro->color].first())); + textItem->setBrush(QBrush(getColor(tro->color))); textItem->setPen(Qt::NoPen); group->setPos(point.x() + dx, point.y() + dy); - group->setFlag(QGraphicsItem::ItemIgnoresTransformations); + if (!printMode) + group->setFlag(QGraphicsItem::ItemIgnoresTransformations); if (!parent) scene()->addItem(group); @@ -1127,7 +1142,7 @@ void ProfileGraphicsView::plot_temperature_profile() QPointF from; QPointF to; - QColor color = profile_color[TEMP_PLOT].first(); + QColor color = getColor(TEMP_PLOT); for (int i = 0; i < gc.pi.nr; i++) { struct plot_data *entry = gc.pi.entry + i; @@ -1175,36 +1190,14 @@ void ToolTipItem::addToolTip(const QString& toolTip, const QIcon& icon) textItem->setPos(SPACING + ICON_SMALL + SPACING, yValue); textItem->setBrush(QBrush(Qt::white)); textItem->setFlag(ItemIgnoresTransformations); - toolTips[toolTip] = qMakePair(iconItem, textItem); + toolTips.push_back(qMakePair(iconItem, textItem)); expand(); } -void ToolTipItem::removeToolTip(const QString& toolTip) -{ - ToolTip toBeRemoved = toolTips[toolTip]; - delete toBeRemoved.first; - delete toBeRemoved.second; - toolTips.remove(toolTip); - - int toolTipIndex = 0; - - // We removed a toolTip, let's move the others to the correct location - Q_FOREACH(ToolTip t, toolTips) { - double yValue = title->boundingRect().height() + SPACING + toolTipIndex * ICON_SMALL + SPACING; - - // Icons can be null. - if (t.first) - t.first->setPos(SPACING, yValue); - - t.second->setPos(SPACING + ICON_SMALL + SPACING, yValue); - toolTipIndex++; - } -} - void ToolTipItem::refresh(struct graphics_context *gc, QPointF pos) { clear(); - int time = (pos.x() * gc->maxtime) / scene()->sceneRect().width(); + int time = (pos.x() * gc->maxtime) / gc->maxx; char buffer[500]; get_plot_details(gc, time, buffer, 500); addToolTip(QString(buffer)); @@ -1397,8 +1390,14 @@ bool ToolTipItem::eventFilter(QObject* view, QEvent* event) return false; } -EventItem::EventItem(QGraphicsItem* parent): QGraphicsPolygonItem(parent) +QColor EventItem::getColor(const color_indice_t i) +{ + return profile_color[i].at((isGrayscale) ? 1 : 0); +} + +EventItem::EventItem(QGraphicsItem* parent, bool grayscale): QGraphicsPolygonItem(parent) { + isGrayscale = grayscale; setFlag(ItemIgnoresTransformations); setFlag(ItemIsFocusable); setAcceptHoverEvents(true); @@ -1416,17 +1415,18 @@ EventItem::EventItem(QGraphicsItem* parent): QGraphicsPolygonItem(parent) defaultPen.setCosmetic(true); QPen pen = defaultPen; - pen.setBrush(QBrush(profile_color[ALERT_BG].first())); + pen.setBrush(QBrush(getColor(ALERT_BG))); setPolygon(poly); - setBrush(QBrush(profile_color[ALERT_BG].first())); + setBrush(QBrush(getColor(ALERT_BG))); setPen(pen); - QGraphicsLineItem *line = new QGraphicsLineItem(0,5,0,10, this); - line->setPen(QPen(Qt::black, 2)); + QGraphicsLineItem *line = new QGraphicsLineItem(0, 5, 0, 10, this); + line->setPen(QPen(getColor(ALERT_FG), 2)); - QGraphicsEllipseItem *ball = new QGraphicsEllipseItem(-1, 12, 2,2, this); - ball->setBrush(QBrush(Qt::black)); + QGraphicsEllipseItem *ball = new QGraphicsEllipseItem(-1, 12, 2, 2, this); + ball->setBrush(QBrush(getColor(ALERT_FG))); + ball->setPen(QPen(getColor(ALERT_FG))); } GraphicsTextEditor::GraphicsTextEditor(QGraphicsItem* parent): QGraphicsTextItem(parent) diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h index b6df52dec..0bcbf7529 100644 --- a/qt-ui/profilegraphics.h +++ b/qt-ui/profilegraphics.h @@ -1,6 +1,7 @@ #ifndef PROFILEGRAPHICS_H #define PROFILEGRAPHICS_H +#include "graphicsview-common.h" #include "../display.h" #include <QGraphicsView> #include <QGraphicsItem> @@ -34,7 +35,6 @@ public: void expand(); void clear(); 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(); @@ -47,7 +47,7 @@ public slots: private: typedef QPair<QGraphicsPixmapItem*, QGraphicsSimpleTextItem*> ToolTip; - QMap<QString, ToolTip > toolTips; + QList<ToolTip> toolTips; QGraphicsPathItem *background; QGraphicsLineItem *separator; QGraphicsSimpleTextItem *title; @@ -60,12 +60,15 @@ private: class EventItem : public QGraphicsPolygonItem { public: - explicit EventItem(QGraphicsItem* parent = 0); + explicit EventItem(QGraphicsItem* parent = 0, bool grayscale = FALSE); private: ToolTipItem *controller; QString text; QIcon icon; + bool isGrayscale; + + QColor getColor(const color_indice_t i); }; class GraphicsTextEditor : public QGraphicsTextItem{ @@ -92,6 +95,7 @@ public: void plot(struct dive *d, bool forceRedraw = FALSE); bool eventFilter(QObject* obj, QEvent* event); void clear(); + void setPrintMode(bool mode, bool grayscale = FALSE); protected: void resizeEvent(QResizeEvent *event); @@ -124,6 +128,7 @@ private: void plot_pp_text(); void plot_depth_scale(); + QColor getColor(const color_indice_t i); QColor get_sac_color(int sac, int avg_sac); void scrollViewTo(const QPoint pos); @@ -134,6 +139,8 @@ private: struct dive *dive; struct divecomputer *diveDC; int zoomLevel; + bool printMode; + bool isGrayscale; // Top Level Items. QGraphicsItem* profileGrid; 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> |