Skip to content

Commit 7c1d237

Browse files
committed
Support alternative display formats in the Browse Data tab
This is a proof-of-concept or even a basic first implementation of a new feature I'd like to have in DB4S which at the moment I call display formats. The idea here is to allow the user to change the data in the Browse Data tab on a per column basis before displaying it. This means even though the data is stored in format X in the database it can be shown in format Y in the browser. This should be useful in cases where the original format X is hard to read or just not useful in a particular case. This first implementation allows the user to right click on the header of a column and open a new dialog for setting the display format which offers a (limited) list of pre-defined formats. The selected format is then integrated into the SELECT statement which is sent to SQLite. While it works, this draft implementation lacks a number of features. Here are the most prominent ones I'm currently aware of: * Data not editable (or only via the Edit Dialog) because it isn't transformed back yet. * More display formats needed; maybe customizable ones, too. * No indication in the UI for which columns a format has been set. * Could _maybe_ be integrated into the import/export etc. for optional use.
1 parent 944e22a commit 7c1d237

11 files changed

+349
-10
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ set(SQLB_MOC_HDR
8787
src/ExportSqlDialog.h
8888
src/SqlUiLexer.h
8989
src/FileDialog.h
90+
src/ColumnDisplayFormatDialog.h
9091
)
9192

9293
set(SQLB_SRC
@@ -116,6 +117,7 @@ set(SQLB_SRC
116117
src/ExportSqlDialog.cpp
117118
src/SqlUiLexer.cpp
118119
src/FileDialog.cpp
120+
src/ColumnDisplayFormatDialog.cpp
119121
)
120122

121123
set(SQLB_FORMS
@@ -131,6 +133,7 @@ set(SQLB_FORMS
131133
src/VacuumDialog.ui
132134
src/CipherDialog.ui
133135
src/ExportSqlDialog.ui
136+
src/ColumnDisplayFormatDialog.ui
134137
)
135138

136139
set(SQLB_RESOURCES

src/ColumnDisplayFormatDialog.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "ColumnDisplayFormatDialog.h"
2+
#include "ui_ColumnDisplayFormatDialog.h"
3+
4+
ColumnDisplayFormatDialog::ColumnDisplayFormatDialog(const QString& colname, QString current_format, QWidget* parent)
5+
: QDialog(parent),
6+
ui(new Ui::ColumnDisplayFormatDialog),
7+
column_name(colname)
8+
{
9+
// Create UI
10+
ui->setupUi(this);
11+
ui->comboDisplayFormat->addItem(tr("Default"), "default");
12+
ui->comboDisplayFormat->addItem(tr("Lower case"), "lower");
13+
ui->comboDisplayFormat->addItem(tr("Upper case"), "upper");
14+
ui->comboDisplayFormat->addItem(tr("Unix epoch to date"), "epoch");
15+
ui->comboDisplayFormat->addItem(tr("Julian day to date"), "julian");
16+
ui->comboDisplayFormat->addItem(tr("Round number"), "round");
17+
ui->labelDisplayFormat->setText(ui->labelDisplayFormat->text().arg(column_name));
18+
19+
// Set the current format, if it's empty set the default format
20+
if(current_format.isEmpty())
21+
{
22+
ui->comboDisplayFormat->setCurrentIndex(0);
23+
updateSqlCode();
24+
} else {
25+
ui->comboDisplayFormat->addItem(tr("Custom"), "custom");
26+
ui->comboDisplayFormat->setCurrentIndex(ui->comboDisplayFormat->findData("custom"));
27+
ui->editDisplayFormat->setText(current_format);
28+
}
29+
}
30+
31+
ColumnDisplayFormatDialog::~ColumnDisplayFormatDialog()
32+
{
33+
delete ui;
34+
}
35+
36+
QString ColumnDisplayFormatDialog::selectedDisplayFormat() const
37+
{
38+
if(ui->comboDisplayFormat->currentData().toString() == "default")
39+
return QString();
40+
else
41+
return ui->editDisplayFormat->text();
42+
}
43+
44+
void ColumnDisplayFormatDialog::updateSqlCode()
45+
{
46+
QString format = ui->comboDisplayFormat->currentData().toString();
47+
if(format == "default")
48+
ui->editDisplayFormat->setText("`" + column_name + "`");
49+
else if(format == "lower")
50+
ui->editDisplayFormat->setText("lower(`" + column_name + "`)");
51+
else if(format == "upper")
52+
ui->editDisplayFormat->setText("upper(`" + column_name + "`)");
53+
else if(format == "epoch")
54+
ui->editDisplayFormat->setText("datetime(`" + column_name + "`, 'unixepoch')");
55+
else if(format == "julian")
56+
ui->editDisplayFormat->setText("datetime(`" + column_name + "`)");
57+
else if(format == "round")
58+
ui->editDisplayFormat->setText("round(`" + column_name + "`)");
59+
}

src/ColumnDisplayFormatDialog.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef COLUMNDISPLAYFORMATDIALOG_H
2+
#define COLUMNDISPLAYFORMATDIALOG_H
3+
4+
#include <QDialog>
5+
6+
namespace Ui {
7+
class ColumnDisplayFormatDialog;
8+
}
9+
10+
class ColumnDisplayFormatDialog : public QDialog
11+
{
12+
Q_OBJECT
13+
14+
public:
15+
explicit ColumnDisplayFormatDialog(const QString& colname, QString current_format, QWidget* parent = 0);
16+
~ColumnDisplayFormatDialog();
17+
18+
QString selectedDisplayFormat() const;
19+
20+
private slots:
21+
void updateSqlCode();
22+
23+
private:
24+
Ui::ColumnDisplayFormatDialog* ui;
25+
QString column_name;
26+
};
27+
28+
#endif

src/ColumnDisplayFormatDialog.ui

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>ColumnDisplayFormatDialog</class>
4+
<widget class="QDialog" name="ColumnDisplayFormatDialog">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>624</width>
10+
<height>205</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Choose display format</string>
15+
</property>
16+
<layout class="QVBoxLayout" name="verticalLayout">
17+
<item>
18+
<widget class="QGroupBox" name="groupBox">
19+
<property name="title">
20+
<string>Display format</string>
21+
</property>
22+
<layout class="QVBoxLayout" name="verticalLayout_2">
23+
<item>
24+
<widget class="QLabel" name="labelDisplayFormat">
25+
<property name="text">
26+
<string>Choose a display format for the column '%1' which is applied to each value prior to showing it.</string>
27+
</property>
28+
</widget>
29+
</item>
30+
<item>
31+
<widget class="QComboBox" name="comboDisplayFormat"/>
32+
</item>
33+
<item>
34+
<widget class="SqlTextEdit" name="editDisplayFormat">
35+
<property name="readOnly">
36+
<bool>true</bool>
37+
</property>
38+
</widget>
39+
</item>
40+
</layout>
41+
</widget>
42+
</item>
43+
<item>
44+
<widget class="QDialogButtonBox" name="buttonBox">
45+
<property name="orientation">
46+
<enum>Qt::Horizontal</enum>
47+
</property>
48+
<property name="standardButtons">
49+
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
50+
</property>
51+
</widget>
52+
</item>
53+
</layout>
54+
</widget>
55+
<customwidgets>
56+
<customwidget>
57+
<class>SqlTextEdit</class>
58+
<extends>QTextEdit</extends>
59+
<header>sqltextedit.h</header>
60+
<container>1</container>
61+
</customwidget>
62+
</customwidgets>
63+
<tabstops>
64+
<tabstop>comboDisplayFormat</tabstop>
65+
<tabstop>editDisplayFormat</tabstop>
66+
</tabstops>
67+
<resources/>
68+
<connections>
69+
<connection>
70+
<sender>buttonBox</sender>
71+
<signal>accepted()</signal>
72+
<receiver>ColumnDisplayFormatDialog</receiver>
73+
<slot>accept()</slot>
74+
<hints>
75+
<hint type="sourcelabel">
76+
<x>227</x>
77+
<y>188</y>
78+
</hint>
79+
<hint type="destinationlabel">
80+
<x>157</x>
81+
<y>204</y>
82+
</hint>
83+
</hints>
84+
</connection>
85+
<connection>
86+
<sender>buttonBox</sender>
87+
<signal>rejected()</signal>
88+
<receiver>ColumnDisplayFormatDialog</receiver>
89+
<slot>reject()</slot>
90+
<hints>
91+
<hint type="sourcelabel">
92+
<x>295</x>
93+
<y>194</y>
94+
</hint>
95+
<hint type="destinationlabel">
96+
<x>286</x>
97+
<y>204</y>
98+
</hint>
99+
</hints>
100+
</connection>
101+
<connection>
102+
<sender>comboDisplayFormat</sender>
103+
<signal>currentIndexChanged(int)</signal>
104+
<receiver>ColumnDisplayFormatDialog</receiver>
105+
<slot>updateSqlCode()</slot>
106+
<hints>
107+
<hint type="sourcelabel">
108+
<x>125</x>
109+
<y>69</y>
110+
</hint>
111+
<hint type="destinationlabel">
112+
<x>244</x>
113+
<y>4</y>
114+
</hint>
115+
</hints>
116+
</connection>
117+
</connections>
118+
<slots>
119+
<slot>updateSqlCode()</slot>
120+
</slots>
121+
</ui>

src/FilterTableHeader.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ FilterTableHeader::FilterTableHeader(QTableView* parent) :
5858
connect(this, SIGNAL(sectionResized(int,int,int)), this, SLOT(adjustPositions()));
5959
connect(parent->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjustPositions()));
6060
connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjustPositions()));
61+
62+
// Set custom context menu handling
63+
setContextMenuPolicy(Qt::CustomContextMenu);
6164
}
6265

6366
void FilterTableHeader::generateFilters(int number)

src/MainWindow.cpp

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "ExportSqlDialog.h"
1919
#include "SqlUiLexer.h"
2020
#include "FileDialog.h"
21+
#include "ColumnDisplayFormatDialog.h"
2122

2223
#include <QFile>
2324
#include <QApplication>
@@ -116,6 +117,9 @@ void MainWindow::init()
116117
popupSaveSqlFileMenu->addAction(ui->actionSqlSaveFileAs);
117118
ui->actionSqlSaveFilePopup->setMenu(popupSaveSqlFileMenu);
118119

120+
popupBrowseDataHeaderMenu = new QMenu(this);
121+
popupBrowseDataHeaderMenu->addAction(ui->actionBrowseTableEditDisplayFormat);
122+
119123
// Add menu item for log dock
120124
ui->viewMenu->insertAction(ui->viewDBToolbarAction, ui->dockLog->toggleViewAction());
121125
ui->viewMenu->actions().at(0)->setShortcut(QKeySequence(tr("Ctrl+L")));
@@ -155,6 +159,7 @@ void MainWindow::init()
155159
connect(editWin, SIGNAL(goingAway()), this, SLOT(editWinAway()));
156160
connect(editWin, SIGNAL(updateRecordText(int, int, QByteArray)), this, SLOT(updateRecordText(int, int, QByteArray)));
157161
connect(ui->dbTreeWidget->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(changeTreeSelection()));
162+
connect(ui->dataTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showDataColumnPopupMenu(QPoint)));
158163

159164
// Load window settings
160165
tabifyDockWidget(ui->dockLog, ui->dockPlot);
@@ -319,16 +324,41 @@ void MainWindow::populateTable(const QString& tablename)
319324
// Set model
320325
ui->dataTable->setModel(m_browseTableModel);
321326

327+
// Search stored table settings for this table
328+
QMap<QString, BrowseDataTableSettings>::ConstIterator tableIt;
329+
tableIt = browseTableSettings.constFind(tablename);
330+
bool storedDataFound = tableIt != browseTableSettings.constEnd();
331+
322332
// Set new table
323-
m_browseTableModel->setTable(tablename);
333+
if(!storedDataFound)
334+
{
335+
m_browseTableModel->setTable(tablename);
336+
} else {
337+
QVector<QString> v;
338+
bool only_defaults = true;
339+
for(int i=0;i<db.getObjectByName(tablename).table.fields().size();i++)
340+
{
341+
QString format = tableIt.value().displayFormats[i+1];
342+
if(format.size())
343+
{
344+
v.push_back(format);
345+
only_defaults = false;
346+
} else {
347+
v.push_back("`" + db.getObjectByName(tablename).table.fields().at(i)->name() + "`");
348+
}
349+
}
350+
if(only_defaults)
351+
m_browseTableModel->setTable(tablename);
352+
else
353+
m_browseTableModel->setTable(tablename, v);
354+
}
324355
ui->dataTable->setColumnHidden(0, true);
325356

326357
// Update the filter row
327358
qobject_cast<FilterTableHeader*>(ui->dataTable->horizontalHeader())->generateFilters(m_browseTableModel->columnCount());
328359

329360
// Restore table settings
330-
QMap<QString, BrowseDataTableSettings>::ConstIterator tableIt;
331-
if((tableIt = browseTableSettings.constFind(tablename)) != browseTableSettings.constEnd())
361+
if(storedDataFound)
332362
{
333363
// There is information stored for this table, so extract it and apply it
334364

@@ -2176,3 +2206,41 @@ void MainWindow::jumpToRow(const QString& table, QString column, const QByteArra
21762206
// Set filter
21772207
ui->dataTable->filterHeader()->setFilter(column_index+1, value);
21782208
}
2209+
2210+
void MainWindow::showDataColumnPopupMenu(const QPoint& pos)
2211+
{
2212+
// Get the index of the column which the user has clicked on and store it in the action. This is sort of hack-ish and it might be the heat in my room
2213+
// but I haven't come up with a better solution so far
2214+
ui->actionBrowseTableEditDisplayFormat->setProperty("clicked_column", ui->dataTable->horizontalHeader()->logicalIndexAt(pos));
2215+
2216+
// Calculate the proper position for the context menu and display it
2217+
popupBrowseDataHeaderMenu->exec(ui->dataTable->horizontalHeader()->mapToGlobal(pos));
2218+
}
2219+
2220+
void MainWindow::editDataColumnDisplayFormat()
2221+
{
2222+
// Get the current table name and fetch its table object, then retrieve the fields of that table and look up the index of the clicked table header
2223+
// section using it as the table field array index. Subtract one from the header index to get the column index because in the the first (though hidden)
2224+
// column is always the rowid column. Ultimately, get the column name from the column object
2225+
QString current_table = ui->comboBrowseTable->currentText();
2226+
int field_number = sender()->property("clicked_column").toInt();
2227+
QString field_name = db.getObjectByName(current_table).table.fields().at(field_number-1)->name();
2228+
2229+
// Get the current display format of the field
2230+
QString current_displayformat = browseTableSettings[current_table].displayFormats[field_number];
2231+
2232+
// Open the dialog
2233+
ColumnDisplayFormatDialog dialog(field_name, current_displayformat, this);
2234+
if(dialog.exec())
2235+
{
2236+
// Set the newly selected display format
2237+
QString new_format = dialog.selectedDisplayFormat();
2238+
if(new_format.size())
2239+
browseTableSettings[current_table].displayFormats[field_number] = new_format;
2240+
else
2241+
browseTableSettings[current_table].displayFormats.remove(field_number);
2242+
2243+
// Refresh view
2244+
populateTable(current_table);
2245+
}
2246+
}

src/MainWindow.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ class MainWindow : public QMainWindow
3838
Qt::SortOrder sortOrderMode;
3939
QMap<int, int> columnWidths;
4040
QMap<int, QString> filterValues;
41+
QMap<int, QString> displayFormats;
4142

4243
friend QDataStream& operator<<(QDataStream& stream, const MainWindow::BrowseDataTableSettings& object)
4344
{
4445
stream << object.sortOrderIndex;
4546
stream << static_cast<int>(object.sortOrderMode);
4647
stream << object.columnWidths;
4748
stream << object.filterValues;
49+
stream << object.displayFormats;
4850

4951
return stream;
5052
}
@@ -56,6 +58,7 @@ class MainWindow : public QMainWindow
5658
object.sortOrderMode = static_cast<Qt::SortOrder>(sortordermode);
5759
stream >> object.columnWidths;
5860
stream >> object.filterValues;
61+
stream >> object.displayFormats;
5962

6063
return stream;
6164
}
@@ -98,6 +101,7 @@ class MainWindow : public QMainWindow
98101
QMenu *popupTableMenu;
99102
QMenu *recentFilesMenu;
100103
QMenu *popupSaveSqlFileMenu;
104+
QMenu* popupBrowseDataHeaderMenu;
101105

102106
QLabel* statusEncodingLabel;
103107
QLabel* statusEncryptionLabel;
@@ -207,6 +211,8 @@ private slots:
207211
void copyCurrentCreateStatement();
208212
void on_comboLineType_currentIndexChanged(int index);
209213
void on_comboPointShape_currentIndexChanged(int index);
214+
void showDataColumnPopupMenu(const QPoint& pos);
215+
void editDataColumnDisplayFormat();
210216
};
211217

212218
#endif

0 commit comments

Comments
 (0)