summaryrefslogtreecommitdiffstats
path: root/qt-ui
diff options
context:
space:
mode:
authorGravatar Lubomir I. Ivanov <neolit123@gmail.com>2013-10-03 17:50:40 +0300
committerGravatar Dirk Hohndel <dirk@hohndel.org>2013-10-03 09:38:37 -0700
commite727b899a633a6c7eaf075dcf7380aadd6f85b7f (patch)
tree3293413f356cf1589c7ccc23f8e67d36ad866b7d /qt-ui
parent374f3d0de62722cf4153d12754edf6fb110ce33d (diff)
downloadsubsurface-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.cpp27
-rw-r--r--qt-ui/modeldelegates.h12
-rw-r--r--qt-ui/models.cpp197
-rw-r--r--qt-ui/models.h20
-rw-r--r--qt-ui/printlayout.cpp172
-rw-r--r--qt-ui/printlayout.h8
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;