diff options
author | Lubomir I. Ivanov <neolit123@gmail.com> | 2013-10-03 17:50:40 +0300 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2013-10-03 09:38:37 -0700 |
commit | e727b899a633a6c7eaf075dcf7380aadd6f85b7f (patch) | |
tree | 3293413f356cf1589c7ccc23f8e67d36ad866b7d /qt-ui | |
parent | 374f3d0de62722cf4153d12754edf6fb110ce33d (diff) | |
download | subsurface-e727b899a633a6c7eaf075dcf7380aadd6f85b7f.tar.gz |
Print: provide means to print profile tables
This patch adds a couple of classes and some other modifications
in PrintLayout that handle the printing of tables under a profile.
models.h : ProfilePrintModel
The class uses a 'struct *dive' to output all required data
for a certain dive at specific rows and columns. It also handles
font formatting and text alignment.
modeldelagatates.h : ProfilePrintDelegate
The class is used only for drawing a custom grid for profile tables.
PrintLayout::createProfileTable()
The function is used to create and setup the profile table object
PrintLayout::printProfileDives()
The function now has correct padding of dive profiles on a page
and also the printing of actual tables below them.
Signed-off-by: Lubomir I. Ivanov <neolit123@gmail.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'qt-ui')
-rw-r--r-- | qt-ui/modeldelegates.cpp | 27 | ||||
-rw-r--r-- | qt-ui/modeldelegates.h | 12 | ||||
-rw-r--r-- | qt-ui/models.cpp | 197 | ||||
-rw-r--r-- | qt-ui/models.h | 20 | ||||
-rw-r--r-- | qt-ui/printlayout.cpp | 172 | ||||
-rw-r--r-- | qt-ui/printlayout.h | 8 |
6 files changed, 403 insertions, 33 deletions
diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp index 6141f79fc..e80afab59 100644 --- a/qt-ui/modeldelegates.cpp +++ b/qt-ui/modeldelegates.cpp @@ -300,3 +300,30 @@ void AirTypesDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, AirTypesDelegate::AirTypesDelegate(QObject* parent) : ComboBoxDelegate(airTypes(), parent) { } + +ProfilePrintDelegate::ProfilePrintDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + +/* this method overrides the default table drawing method and places grid lines only at certain rows and columns */ +void ProfilePrintDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + const QRect rect(option.rect); + const int row = index.row(); + const int col = index.column(); + + // grid color + painter->setPen(QPen(QColor(0xff999999))); + // top line + if (row == 2 || row == 3 || row == 10 || col == 3 || col == 4) + painter->drawLine(rect.topLeft(), rect.topRight()); + if (row > 1 && row < 10) { + // left line - draw always for these rows + painter->drawLine(rect.topLeft(), rect.bottomLeft()); + // "fix" for missing (?) right line after col 5 + if (col > 5 || (col > 4 && row == 2)) + painter->drawLine(rect.topRight(), rect.bottomRight()); + } + QStyledItemDelegate::paint(painter, option, index); +} diff --git a/qt-ui/modeldelegates.h b/qt-ui/modeldelegates.h index a873e066d..a16e472d6 100644 --- a/qt-ui/modeldelegates.h +++ b/qt-ui/modeldelegates.h @@ -3,6 +3,7 @@ #include <QStyledItemDelegate> class QComboBox; +class QPainter; class StarWidgetsDelegate : public QStyledItemDelegate { Q_OBJECT @@ -60,4 +61,15 @@ public slots: void revertModelData(QWidget* widget, QAbstractItemDelegate::EndEditHint hint); }; +/* ProfilePrintDelagate: + * this delegate is used to modify the look of the table that is printed + * bellow profiles. + */ +class ProfilePrintDelegate : public QStyledItemDelegate +{ +public: + explicit ProfilePrintDelegate(QObject *parent = 0); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + #endif diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 9446b7d3b..6831b9271 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -1567,3 +1567,200 @@ int TablePrintModel::columnCount(const QModelIndex &parent) const Q_UNUSED(parent); return columns; } + +/*################################################################# + * # + * # Profile Print Model + * # + * ################################################################ + */ + +ProfilePrintModel::ProfilePrintModel(QObject *parent) +{ +} + +/* this is just a helper function to truncate C strings near 'maxlen' characters + * by finding word bounderies and adding '...' at the end of the truncated string. + * not really optimal for all languages! + */ +QString ProfilePrintModel::truncateString(char *str, const int maxlen) const +{ + if (!str) + return QString(""); + QString trunc = QString(str); + const int len = trunc.length(); + for (int i = 0; i < len; i++) { + char c = trunc.at(i).toAscii(); + if (c == ' ' || c == '\n' || c == '\t') { + if (i > maxlen) { + trunc = trunc.left(i) + QString("..."); + break; + } + } + } + return trunc; +} + +void ProfilePrintModel::setDive(struct dive *divePtr) +{ + dive = divePtr; + // reset(); +} + +int ProfilePrintModel::rowCount(const QModelIndex &parent) const +{ + return 11; +} + +int ProfilePrintModel::columnCount(const QModelIndex &parent) const +{ + return 7; +} + +QVariant ProfilePrintModel::data(const QModelIndex &index, int role) const +{ + const int row = index.row(); + const int col = index.column(); + + switch (role) { + case Qt::DisplayRole: { + struct DiveItem di; + di.dive = dive; + QString unit; + char buf[80]; + const QString empty = QString(""); + const QString unknown = QString(tr("unknown")); + + // dive# + date, depth, location, duration + if (row == 0) { + if (col == 0) + return QString(tr("Dive #%1 - %2")).arg(dive->number).arg(di.displayDate()); + if (col == 5) { + unit = (get_units()->length == units::METERS) ? "m" : "ft"; + return QString(tr("Max depth: %1 %2")).arg(di.displayDepth()).arg(unit); + } + } + if (row == 1) { + if (col == 0) + return truncateString(dive->location, 32); + if (col == 5) + return QString(tr("Duration: %1 min")).arg(di.displayDuration()); + } + // cylinder headings + if (row == 2) { + if (col == 0) + return QString(tr("Cylinder")); + if (col == 1) + return QString(tr("Gasmix")); + if (col == 2) + return QString(tr("Gas Used")); + } + // cylinder data + if (row > 2 && row < 10 && row - 3 < MAX_CYLINDERS) { + cylinder_t *cyl = &dive->cylinder[row - 3]; + if (cyl->type.description) { // how do we check if a cylinder is added? + if (col == 0) { + if (cyl->type.description[0] != '\0') + return QString(cyl->type.description); + return unknown; + } + if (col == 1) { + get_gas_string(cyl->gasmix.o2.permille, cyl->gasmix.he.permille, buf, sizeof(buf)); + return QString(buf); + } + if (col == 2) { + return get_cylinder_used_gas_string(cyl, true); + } + } + } + // dive notes + if (row == 10 && col == 0) + return truncateString(dive->notes, 64); + // sac, cns, otu - headings + if (col == 3) { + if (row == 2) + return QString(tr("SAC")); + if (row == 4) + return QString(tr("Max. CNS")); + if (row == 6) + return QString(tr("OTU")); + } + // sac, cns, otu - data + if (col == 4) { + if (row == 2) + return di.displaySac(); + if (row == 4) + return QString::number(dive->maxcns); + if (row == 6) + return QString::number(dive->otu); + } + // weights heading + if (row == 2 && col == 5) + return QString(tr("Weights")); + // total weight + if (row == 9) { + weight_t tw = { total_weight(dive) }; + if (tw.grams) { + if (col == 5) + return QString("Total weight"); + if (col == 6) + return get_weight_string(tw, true); + } + } + // weight data + if (row > 2 && row < 10 && row - 3 < MAX_WEIGHTSYSTEMS) { + weightsystem_t *ws = &dive->weightsystem[row - 3]; + if (ws->weight.grams) { + if (col == 5) { + if (ws->description && ws->description[0] != '\0') + return QString(ws->description); + return unknown; + } + if (col == 6) { + return get_weight_string(ws->weight, true); + } + } + } + return empty; + } + case Qt::FontRole: { + QFont font; + const int baseSize = 8; + // dive # + if (row == 0 && col == 0) { + font.setBold(true); + font.setPixelSize(baseSize + 1); + return QVariant::fromValue(font); + } + // dive location + if (row == 1 && col == 0) { + font.setPixelSize(baseSize); + font.setBold(true); + return QVariant::fromValue(font); + } + // depth/duration + if ((row == 0 || row == 1) && col == 5) { + font.setPixelSize(baseSize); + return QVariant::fromValue(font); + } + // notes + if (row == 9 && col == 0) { + font.setPixelSize(baseSize + 1); + return QVariant::fromValue(font); + } + font.setPixelSize(baseSize); + return QVariant::fromValue(font); + } + case Qt::TextAlignmentRole: { + unsigned int align = Qt::AlignCenter; + // dive #, location, notes + if ((row < 2 || row == 10) && col == 0) + align = Qt::AlignLeft | Qt::AlignVCenter; + // depth, duration + if (row < 2 && col == 5) + align = Qt::AlignRight | Qt::AlignVCenter; + return QVariant::fromValue(align); + } + } // switch (role) + return QVariant(); +} diff --git a/qt-ui/models.h b/qt-ui/models.h index 2a9945aeb..75c3e08ac 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -274,4 +274,24 @@ public: int columnCount(const QModelIndex &parent) const; }; +/* ProfilePrintModel: + * this model is used when printing a data table under a profile. it requires + * some exact usage of setSpan(..) on the target QTableView widget. + */ +class ProfilePrintModel : public QAbstractTableModel +{ + Q_OBJECT + +private: + struct dive *dive; + QString truncateString(char *str, const int maxlen) const; + +public: + ProfilePrintModel(QObject *parent = 0); + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + void setDive(struct dive *divePtr); +}; + #endif diff --git a/qt-ui/printlayout.cpp b/qt-ui/printlayout.cpp index 3d0937636..4f741d64e 100644 --- a/qt-ui/printlayout.cpp +++ b/qt-ui/printlayout.cpp @@ -11,22 +11,12 @@ #include "../dive.h" #include "../display.h" #include "models.h" - -/* -struct options { - enum { PRETTY, TABLE, TWOPERPAGE } type; - int print_selected; - int color_selected; - bool notes_up; - int profile_height, notes_height, tanks_height; -}; -*/ +#include "modeldelegates.h" PrintLayout::PrintLayout(PrintDialog *dialogPtr, QPrinter *printerPtr, struct options *optionsPtr) { dialog = dialogPtr; printer = printerPtr; - painter = NULL; printOptions = optionsPtr; // table print settings @@ -45,6 +35,27 @@ PrintLayout::PrintLayout(PrintDialog *dialogPtr, QPrinter *printerPtr, struct op tablePrintColumnWidths.append(15); tablePrintColumnWidths.append(15); tablePrintColumnWidths.append(33); + // profile print settings + const int dw = 15; // base percentage + profilePrintColumnWidths.append(dw); + profilePrintColumnWidths.append(dw); + profilePrintColumnWidths.append(dw); + profilePrintColumnWidths.append(dw); + profilePrintColumnWidths.append(dw - 5); + profilePrintColumnWidths.append(dw + 5); + profilePrintColumnWidths.append(dw - 5); // fit to 100% + const int sr = 8; // smallest row height in pixels + profilePrintRowHeights.append(sr + 2); + profilePrintRowHeights.append(sr + 7); + profilePrintRowHeights.append(sr); + profilePrintRowHeights.append(sr); + profilePrintRowHeights.append(sr); + profilePrintRowHeights.append(sr); + profilePrintRowHeights.append(sr); + profilePrintRowHeights.append(sr); + profilePrintRowHeights.append(sr); + profilePrintRowHeights.append(sr); + profilePrintRowHeights.append(sr + 12); } void PrintLayout::print() @@ -81,14 +92,25 @@ void PrintLayout::setup() scaledPageH = pageRect.height() / scaleY; } +/* the used formula here is: + * s = (S - (n - 1) * p) / n + * where: + * s is the length of a single element (unknown) + * S is the total available length + * n is the number of elements to fit + * p is the padding between elements + */ +#define ESTIMATE_DIVE_DIM(S, n, p) \ + ((S) - ((n) - 1) * (p)) / (n); + void PrintLayout::printProfileDives(int divesPerRow, int divesPerColumn) { // setup a painter - painter = new QPainter(); - painter->begin(printer); - painter->setRenderHint(QPainter::Antialiasing); - painter->setRenderHint(QPainter::SmoothPixmapTransform); - painter->scale(scaleX, scaleY); + QPainter painter; + painter.begin(printer); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.scale(scaleX, scaleY); // setup the profile widget ProfileGraphicsView *profile = mainWindow()->graphics(); @@ -101,15 +123,24 @@ void PrintLayout::printProfileDives(int divesPerRow, int divesPerColumn) divesPerColumn = divesPerRow; divesPerRow = swap; } - // estimate profile and table height and resize the widget - const int scaledW = scaledPageW / divesPerColumn; - const int scaledH = scaledPageH / divesPerRow; - /* make the table 1/3 of the reserved height. potentially this could depend - * on orientation and other parameters as well. */ - const int tableHeight = scaledH / 3; - profile->resize(scaledW, scaledH - tableHeight); - - // plot the dives at specific rows and columns + // padding in pixels between two dives. no padding if only one dive per page. + const int padDef = 20; + const int padW = (divesPerColumn < 2) ? 0 : padDef; + const int padH = (divesPerRow < 2) ? 0 : padDef; + // estimate dimensions for a single dive + const int scaledW = ESTIMATE_DIVE_DIM(scaledPageW, divesPerColumn, padW); + const int scaledH = ESTIMATE_DIVE_DIM(scaledPageH, divesPerRow, padH); + // padding in pixels between profile and table + const int padPT = 10; + // create a model and table + ProfilePrintModel model; + QTableView *table = createProfileTable(&model, scaledW); + // profilePrintTableMaxH updates after the table is created + const int tableH = profilePrintTableMaxH; + // resize the profile widget + profile->resize(scaledW, scaledH - tableH - padPT); + + // plot the dives at specific rows and columns on the page int i, row = 0, col = 0; struct dive *dive; for_each_dive(i, dive) { @@ -123,23 +154,102 @@ void PrintLayout::printProfileDives(int divesPerRow, int divesPerColumn) printer->newPage(); } } + // draw a profile profile->plot(dive, true); - QPixmap pm = QPixmap::grabWidget(profile); - painter->drawPixmap(scaledW * col, scaledH * row, pm); - /* TODO: table should be drawn here, preferably by another function */ + QPixmap profilePm = QPixmap::grabWidget(profile); // Qt4 + painter.drawPixmap((scaledW + padW) * col, + (scaledH + padH) * row, + profilePm); + // draw a table + model.setDive(dive); + QPixmap tablePm = QPixmap::grabWidget(table); // Qt4 + painter.drawPixmap((scaledW + padW) * col, + (scaledH + padH) * row + (scaledH - tableH), + tablePm); col++; } // cleanup - painter->end(); - delete painter; - painter = NULL; + painter.end(); + delete table; profile->setPrintMode(false); profile->resize(originalSize); profile->clear(); profile->plot(current_dive, true); } +/* we create a table that has a fixed height, but can stretch to fit certain width */ +QTableView *PrintLayout::createProfileTable(ProfilePrintModel *model, const int tableW) +{ + // setup a new table + QTableView *table = new QTableView(); + QHeaderView *vHeader = table->verticalHeader(); + QHeaderView *hHeader = table->horizontalHeader(); + table->setAttribute(Qt::WA_DontShowOnScreen); + table->setSelectionMode(QAbstractItemView::NoSelection); + table->setFocusPolicy(Qt::NoFocus); + table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + hHeader->setVisible(false); + hHeader->setResizeMode(QHeaderView::Fixed); + vHeader->setVisible(false); + vHeader->setResizeMode(QHeaderView::Fixed); + // set the model + table->setModel(model); + + /* setup cell span for the table using QTableView::setSpan(). + * changes made here reflect on ProfilePrintModel::data(). */ + const int cols = model->columnCount(); + const int rows = model->rowCount(); + // top section + table->setSpan(0, 0, 1, cols - 2); + table->setSpan(1, 0, 1, cols - 2); + table->setSpan(10, 0, 1, cols); + table->setSpan(0, 5, 1, 2); + table->setSpan(1, 5, 1, 12); + // sac, cns, otu + table->setSpan(2, 3, 2, 1); + table->setSpan(4, 3, 2, 1); + table->setSpan(6, 3, 2, 1); + table->setSpan(8, 3, 2, 1); + table->setSpan(2, 4, 2, 1); + table->setSpan(4, 4, 2, 1); + table->setSpan(6, 4, 2, 1); + table->setSpan(8, 4, 2, 1); + // weights + table->setSpan(2, 5, 1, 2); + + /* resize row heights to the 'profilePrintRowHeights' indexes. + * profilePrintTableMaxH will then hold the table height. */ + int i; + profilePrintTableMaxH = 0; + for (i = 0; i < rows; i++) { + int h = profilePrintRowHeights.at(i); + profilePrintTableMaxH += h; + vHeader->resizeSection(i, h); + } + // resize columns. columns widths are percentages from the table width. + int accW = 0; + for (i = 0; i < cols; i++) { + int pw = qCeil((qreal)(profilePrintColumnWidths.at(i) * tableW) / 100.0); + accW += pw; + if (i == cols - 1 && accW > tableW) /* adjust last column */ + pw -= accW - tableW; + hHeader->resizeSection(i, pw); + } + // resize + table->resize(tableW, profilePrintTableMaxH); + // hide the grid and set a stylesheet + table->setItemDelegate(new ProfilePrintDelegate()); + table->setShowGrid(false); + table->setStyleSheet( + "QTableView { border: none }" + "QTableView::item { border: 0px; padding-left: 2px; padding-right: 2px; }" + ); + // return + return table; +} + void PrintLayout::printTable() { // create and setup a table diff --git a/qt-ui/printlayout.h b/qt-ui/printlayout.h index 79c1d658e..a1f348664 100644 --- a/qt-ui/printlayout.h +++ b/qt-ui/printlayout.h @@ -5,8 +5,10 @@ #include <QPrinter> #include <QList> +class QTableView; class PrintDialog; class TablePrintModel; +class ProfilePrintModel; struct dive; class PrintLayout : public QObject { @@ -19,7 +21,6 @@ public: private: PrintDialog *dialog; QPrinter *printer; - QPainter *painter; struct options *printOptions; int screenDpiX, screenDpiY, printerDpi, scaledPageW, scaledPageH; @@ -27,11 +28,14 @@ private: QRect pageRect; QList<QString> tablePrintColumnNames; - QList<unsigned int> tablePrintColumnWidths; unsigned int tablePrintHeadingBackground; + QList<unsigned int> tablePrintColumnWidths; + unsigned int profilePrintTableMaxH; + QList<unsigned int> profilePrintColumnWidths, profilePrintRowHeights; void setup(); void printProfileDives(int divesPerRow, int divesPerColumn); + QTableView *createProfileTable(ProfilePrintModel *model, const int tableW); void printTable(); void addTablePrintDataRow(TablePrintModel *model, int row, struct dive *dive) const; void addTablePrintHeadingRow(TablePrintModel *model, int row) const; |