Added a custom widget for tagging dives

A custom tag widget has been added to MainTab.
Tags are seperated by a comma ",". The implementation
supports escaping a comma by using "\,".
While typing, the widget supports the user by suggesting
tags using a QCompleter.

Signed-off-by: Maximilian Güntner <maximilian.guentner@gmail.com>
This commit is contained in:
Maximilian Güntner 2013-11-02 02:20:02 +01:00
parent 6fe8cb6521
commit 04cdfce782
10 changed files with 582 additions and 50 deletions

View file

@ -12,6 +12,7 @@ CREATE_SINGLETON(BuddyCompletionModel);
CREATE_SINGLETON(DiveMasterCompletionModel); CREATE_SINGLETON(DiveMasterCompletionModel);
CREATE_SINGLETON(LocationCompletionModel); CREATE_SINGLETON(LocationCompletionModel);
CREATE_SINGLETON(SuitCompletionModel); CREATE_SINGLETON(SuitCompletionModel);
CREATE_SINGLETON(TagCompletionModel);
#undef CREATE_SINGLETON #undef CREATE_SINGLETON
@ -35,3 +36,15 @@ CREATE_UPDATE_METHOD(DiveMasterCompletionModel, divemaster);
CREATE_UPDATE_METHOD(LocationCompletionModel, location); CREATE_UPDATE_METHOD(LocationCompletionModel, location);
CREATE_UPDATE_METHOD(SuitCompletionModel, suit); CREATE_UPDATE_METHOD(SuitCompletionModel, suit);
void TagCompletionModel::updateModel()
{
if(g_tag_list == NULL)
return;
QStringList list;
struct tag_entry *current_tag_entry = g_tag_list->next;
while (current_tag_entry != NULL) {
list.append(QString(current_tag_entry->tag->name));
current_tag_entry = current_tag_entry->next;
}
setStringList(list);
}

View file

@ -31,4 +31,11 @@ public:
void updateModel(); void updateModel();
}; };
class TagCompletionModel : public QStringListModel {
Q_OBJECT
public:
static TagCompletionModel* instance();
void updateModel();
};
#endif #endif

204
qt-ui/groupedlineedit.cpp Normal file
View file

@ -0,0 +1,204 @@
/*
* This file is part of the Nepomuk widgets collection
* Copyright (c) 2013 Denis Steckelmacher <steckdenis@yahoo.fr>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2.1 as published by the Free Software Foundation,
* or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "groupedlineedit.h"
#include <QtGui/QStyleOptionFrameV3>
#include <QtGui/QFontMetrics>
#include <QtGui/QApplication>
#include <QtGui/QScrollBar>
#include <QtGui/QTextDocument>
#include <QtGui/QTextBlock>
#include <QtGui/QTextLayout>
#include <QtGui/QTextLine>
#include <QtGui/QPainter>
#include <QtGui/QPainterPath>
#include <QtGui/QBrush>
#include <QtGui/QColor>
#include <QtGui/QPalette>
struct GroupedLineEdit::Private
{
struct Block {
int start;
int end;
QString text;
};
QVector<Block> blocks;
QVector<QColor> colors;
};
GroupedLineEdit::GroupedLineEdit(QWidget* parent)
: QPlainTextEdit(parent),
d(new Private)
{
setWordWrapMode(QTextOption::NoWrap);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
document()->setMaximumBlockCount(1);
}
GroupedLineEdit::~GroupedLineEdit()
{
delete d;
}
QString GroupedLineEdit::text() const
{
// Remove the block crosses from the text
return toPlainText();
}
int GroupedLineEdit::cursorPosition() const
{
return textCursor().positionInBlock();
}
void GroupedLineEdit::addBlock(int start, int end)
{
Private::Block block;
block.start = start;
block.end = end;
block.text = text().mid(start, end-start+1).trimmed();
d->blocks.append(block);
viewport()->update();
}
void GroupedLineEdit::addColor(QColor color)
{
d->colors.append(color);
}
void GroupedLineEdit::removeAllColors()
{
d->colors.clear();
}
QStringList GroupedLineEdit::getBlockStringList()
{
QStringList retList;
Private::Block block;
foreach(block, d->blocks)
retList.append(block.text);
return retList;
}
void GroupedLineEdit::setCursorPosition(int position)
{
QTextCursor c = textCursor();
c.setPosition(position, QTextCursor::MoveAnchor);
setTextCursor(c);
}
void GroupedLineEdit::setText(const QString &text)
{
setPlainText(text);
}
void GroupedLineEdit::clear()
{
QPlainTextEdit::clear();
removeAllBlocks();
}
void GroupedLineEdit::selectAll()
{
QTextCursor c = textCursor();
c.select(QTextCursor::LineUnderCursor);
setTextCursor(c);
}
void GroupedLineEdit::removeAllBlocks()
{
d->blocks.clear();
viewport()->update();
}
QSize GroupedLineEdit::sizeHint() const
{
QSize rs(
40,
document()->findBlock(0).layout()->lineAt(0).height() +
document()->documentMargin() * 2 +
frameWidth() * 2
);
return rs;
}
QSize GroupedLineEdit::minimumSizeHint() const
{
return sizeHint();
}
void GroupedLineEdit::keyPressEvent(QKeyEvent *e)
{
switch (e->key()) {
case Qt::Key_Return:
case Qt::Key_Enter:
emit editingFinished();
return;
}
QPlainTextEdit::keyPressEvent(e);
}
void GroupedLineEdit::paintEvent(QPaintEvent *e)
{
QTextLine line = document()->findBlock(0).layout()->lineAt(0);
QPainter painter(viewport());
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setRenderHint(QPainter::HighQualityAntialiasing, true);
painter.fillRect(0, 0, viewport()->width(), viewport()->height(), palette().base());
QVectorIterator<QColor> i(d->colors);
i.toFront();
foreach (const Private::Block &block, d->blocks) {
qreal start_x = line.cursorToX(block.start, QTextLine::Trailing);
qreal end_x = line.cursorToX(block.end + 1, QTextLine::Leading);
QPainterPath path;
QRectF rectangle(
start_x - 1.0 - double(horizontalScrollBar()->value()),
1.0,
end_x - start_x + 2.0,
double(viewport()->height() - 2)
);
if (! i.hasNext())
i.toFront();
path.addRoundedRect(rectangle, 5.0, 5.0);
painter.setPen(i.peekNext());
painter.setBrush(i.next().lighter(180));
painter.drawPath(path);
}
QPlainTextEdit::paintEvent(e);
}

66
qt-ui/groupedlineedit.h Normal file
View file

@ -0,0 +1,66 @@
/* Original License:
*
* This file is part of the Nepomuk widgets collection
* Copyright (c) 2013 Denis Steckelmacher <steckdenis@yahoo.fr>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2.1 as published by the Free Software Foundation,
* or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
*
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GROUPEDLINEEDIT_H__
#define __GROUPEDLINEEDIT_H__
#include <QtGui/QPlainTextEdit>
#include <QStringList>
class GroupedLineEdit : public QPlainTextEdit
{
Q_OBJECT
public:
explicit GroupedLineEdit(QWidget *parent = 0);
virtual ~GroupedLineEdit();
QString text() const;
int cursorPosition() const;
void setCursorPosition(int position);
void setText(const QString &text);
void clear();
void selectAll();
void removeAllBlocks();
void addBlock(int start, int end);
QStringList getBlockStringList();
void addColor(QColor color);
void removeAllColors();
virtual QSize sizeHint() const;
virtual QSize minimumSizeHint() const;
signals:
void editingFinished();
protected:
virtual void paintEvent(QPaintEvent *e);
virtual void keyPressEvent(QKeyEvent *e);
private:
struct Private;
Private *d;
};
#endif

View file

@ -58,6 +58,7 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
ui.airtemp->installEventFilter(this); ui.airtemp->installEventFilter(this);
ui.watertemp->installEventFilter(this); ui.watertemp->installEventFilter(this);
ui.dateTimeEdit->installEventFilter(this); ui.dateTimeEdit->installEventFilter(this);
ui.tagWidget->installEventFilter(this);
QList<QObject *> statisticsTabWidgets = ui.statisticsTab->children(); QList<QObject *> statisticsTabWidgets = ui.statisticsTab->children();
Q_FOREACH(QObject* obj, statisticsTabWidgets) { Q_FOREACH(QObject* obj, statisticsTabWidgets) {
@ -87,10 +88,12 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
completers.divemaster = new QCompleter(DiveMasterCompletionModel::instance(), ui.divemaster); completers.divemaster = new QCompleter(DiveMasterCompletionModel::instance(), ui.divemaster);
completers.location = new QCompleter(LocationCompletionModel::instance(), ui.location); completers.location = new QCompleter(LocationCompletionModel::instance(), ui.location);
completers.suit = new QCompleter(SuitCompletionModel::instance(), ui.suit); completers.suit = new QCompleter(SuitCompletionModel::instance(), ui.suit);
completers.tags = new QCompleter(TagCompletionModel::instance(), ui.tagWidget);
ui.buddy->setCompleter(completers.buddy); ui.buddy->setCompleter(completers.buddy);
ui.divemaster->setCompleter(completers.divemaster); ui.divemaster->setCompleter(completers.divemaster);
ui.location->setCompleter(completers.location); ui.location->setCompleter(completers.location);
ui.suit->setCompleter(completers.suit); ui.suit->setCompleter(completers.suit);
ui.tagWidget->setCompleter(completers.tags);
setMinimumHeight(0); setMinimumHeight(0);
setMinimumWidth(0); setMinimumWidth(0);
@ -161,6 +164,9 @@ void MainTab::enableEdition(EditMode newEditMode)
notesBackup[mydive].airtemp = get_temperature_string(mydive->airtemp, true); notesBackup[mydive].airtemp = get_temperature_string(mydive->airtemp, true);
notesBackup[mydive].watertemp = get_temperature_string(mydive->watertemp, true); notesBackup[mydive].watertemp = get_temperature_string(mydive->watertemp, true);
notesBackup[mydive].datetime = QDateTime::fromTime_t(mydive->when - gettimezoneoffset()).toString(QString("M/d/yy h:mm")); notesBackup[mydive].datetime = QDateTime::fromTime_t(mydive->when - gettimezoneoffset()).toString(QString("M/d/yy h:mm"));
char buf[1024];
taglist_get_tagstring(mydive->tag_list, buf, 1024);
notesBackup[mydive].tags = QString(buf);
// maybe this is a place for memset? // maybe this is a place for memset?
for (int i = 0; i < MAX_CYLINDERS; i++) { for (int i = 0; i < MAX_CYLINDERS; i++) {
@ -182,7 +188,9 @@ bool MainTab::eventFilter(QObject* object, QEvent* event)
enableEdition(); enableEdition();
} }
if (isEnabled() && event->type() == QEvent::FocusIn && (object == ui.rating || object == ui.visibility)) { if (isEnabled() && event->type() == QEvent::FocusIn && (object == ui.rating ||
object == ui.visibility ||
object == ui.tagWidget)) {
tabBar()->setTabIcon(currentIndex(), QIcon(":warning")); tabBar()->setTabIcon(currentIndex(), QIcon(":warning"));
enableEdition(); enableEdition();
} }
@ -215,6 +223,7 @@ void MainTab::clearInfo()
ui.airTemperatureText->clear(); ui.airTemperatureText->clear();
ui.airPressureText->clear(); ui.airPressureText->clear();
ui.salinityText->clear(); ui.salinityText->clear();
ui.tagWidget->clear();
} }
void MainTab::clearStats() void MainTab::clearStats()
@ -356,6 +365,11 @@ void MainTab::updateDiveInfo(int dive)
ui.timeLimits->setMaximum(get_time_string(stats_selection.longest_time.seconds, 0)); ui.timeLimits->setMaximum(get_time_string(stats_selection.longest_time.seconds, 0));
ui.timeLimits->setMinimum(get_time_string(stats_selection.shortest_time.seconds, 0)); ui.timeLimits->setMinimum(get_time_string(stats_selection.shortest_time.seconds, 0));
char buf[1024];
taglist_get_tagstring(d->tag_list, buf, 1024);
ui.tagWidget->setText(QString(buf));
multiEditEquipmentPlaceholder = *d; multiEditEquipmentPlaceholder = *d;
cylindersModel->setDive(&multiEditEquipmentPlaceholder); cylindersModel->setDive(&multiEditEquipmentPlaceholder);
weightModel->setDive(&multiEditEquipmentPlaceholder); weightModel->setDive(&multiEditEquipmentPlaceholder);
@ -393,6 +407,7 @@ void MainTab::reload()
BuddyCompletionModel::instance()->updateModel(); BuddyCompletionModel::instance()->updateModel();
LocationCompletionModel::instance()->updateModel(); LocationCompletionModel::instance()->updateModel();
DiveMasterCompletionModel::instance()->updateModel(); DiveMasterCompletionModel::instance()->updateModel();
TagCompletionModel::instance()->updateModel();
} }
void MainTab::acceptChanges() void MainTab::acceptChanges()
@ -423,7 +438,8 @@ void MainTab::acceptChanges()
notesBackup[curr].airtemp != ui.airtemp->text() || notesBackup[curr].airtemp != ui.airtemp->text() ||
notesBackup[curr].watertemp != ui.watertemp->text() || notesBackup[curr].watertemp != ui.watertemp->text() ||
notesBackup[curr].datetime != ui.dateTimeEdit->dateTime().toString(QString("M/d/yy h:mm")) || notesBackup[curr].datetime != ui.dateTimeEdit->dateTime().toString(QString("M/d/yy h:mm")) ||
notesBackup[curr].visibility != ui.rating->currentStars()) { notesBackup[curr].visibility != ui.rating->currentStars() ||
notesBackup[curr].tags != ui.tagWidget->text()) {
mark_divelist_changed(TRUE); mark_divelist_changed(TRUE);
} }
if (notesBackup[curr].location != ui.location->text() || if (notesBackup[curr].location != ui.location->text() ||
@ -431,6 +447,9 @@ void MainTab::acceptChanges()
mainWindow()->globe()->reload(); mainWindow()->globe()->reload();
} }
if (notesBackup[curr].tags != ui.tagWidget->text())
saveTags();
if (cylindersModel->changed) { if (cylindersModel->changed) {
mark_divelist_changed(TRUE); mark_divelist_changed(TRUE);
Q_FOREACH (dive *d, notesBackup.keys()) { Q_FOREACH (dive *d, notesBackup.keys()) {
@ -480,6 +499,7 @@ void MainTab::resetPallete()
ui.airtemp->setPalette(p); ui.airtemp->setPalette(p);
ui.watertemp->setPalette(p); ui.watertemp->setPalette(p);
ui.dateTimeEdit->setPalette(p); ui.dateTimeEdit->setPalette(p);
ui.tagWidget->setPalette(p);
} }
#define EDIT_TEXT2(what, text) \ #define EDIT_TEXT2(what, text) \
@ -522,6 +542,7 @@ void MainTab::rejectChanges()
ui.airtemp->setText(notesBackup[curr].airtemp); ui.airtemp->setText(notesBackup[curr].airtemp);
ui.watertemp->setText(notesBackup[curr].watertemp); ui.watertemp->setText(notesBackup[curr].watertemp);
ui.dateTimeEdit->setDateTime(QDateTime::fromString(notesBackup[curr].datetime, QString("M/d/y h:mm"))); ui.dateTimeEdit->setDateTime(QDateTime::fromString(notesBackup[curr].datetime, QString("M/d/y h:mm")));
ui.tagWidget->setText(notesBackup[curr].tags);
struct dive *mydive; struct dive *mydive;
for (int i = 0; i < dive_table.nr; i++) { for (int i = 0; i < dive_table.nr; i++) {
@ -628,6 +649,21 @@ void MainTab::on_dateTimeEdit_dateTimeChanged(const QDateTime& datetime)
markChangedWidget(ui.dateTimeEdit); markChangedWidget(ui.dateTimeEdit);
} }
void MainTab::saveTags()
{
EDIT_SELECTED_DIVES(
QString tag;
taglist_clear(mydive->tag_list);
foreach (tag, ui.tagWidget->getBlockStringList())
taglist_add_tag(mydive->tag_list, tag.toAscii().data());
);
}
void MainTab::on_tagWidget_textChanged()
{
markChangedWidget(ui.tagWidget);
}
void MainTab::on_location_textChanged(const QString& text) void MainTab::on_location_textChanged(const QString& text)
{ {
if (editMode == NONE) if (editMode == NONE)

View file

@ -31,6 +31,7 @@ struct NotesBackup{
int rating; int rating;
int visibility; int visibility;
QString divemaster; QString divemaster;
QString tags;
cylinder_t cylinders[MAX_CYLINDERS]; cylinder_t cylinders[MAX_CYLINDERS];
weightsystem_t weightsystem[MAX_WEIGHTSYSTEMS ]; weightsystem_t weightsystem[MAX_WEIGHTSYSTEMS ];
}; };
@ -40,6 +41,7 @@ struct Completers{
QCompleter *divemaster; QCompleter *divemaster;
QCompleter *buddy; QCompleter *buddy;
QCompleter *suit; QCompleter *suit;
QCompleter *tags;
}; };
class MainTab : public QTabWidget class MainTab : public QTabWidget
@ -73,6 +75,7 @@ public slots:
void on_dateTimeEdit_dateTimeChanged(const QDateTime& datetime); void on_dateTimeEdit_dateTimeChanged(const QDateTime& datetime);
void on_rating_valueChanged(int value); void on_rating_valueChanged(int value);
void on_visibility_valueChanged(int value); void on_visibility_valueChanged(int value);
void on_tagWidget_textChanged();
void editCylinderWidget(const QModelIndex& index); void editCylinderWidget(const QModelIndex& index);
void editWeightWidget(const QModelIndex& index); void editWeightWidget(const QModelIndex& index);
void addDiveStarted(); void addDiveStarted();
@ -94,6 +97,7 @@ private:
Completers completers; Completers completers;
void resetPallete(); void resetPallete();
void saveTags();
QString printGPSCoords(int lat, int lon); QString printGPSCoords(int lat, int lon);
}; };

View file

@ -37,8 +37,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>513</width> <width>501</width>
<height>468</height> <height>535</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
@ -102,6 +102,13 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="4" column="0" colspan="2">
<widget class="QLineEdit" name="location">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="LocationLabel"> <widget class="QLabel" name="LocationLabel">
<property name="text"> <property name="text">
@ -109,8 +116,22 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0" colspan="2"> <item row="11" column="0">
<widget class="QLineEdit" name="location"> <widget class="QLabel" name="TagLabel">
<property name="text">
<string>Tags</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QLineEdit" name="coordinates">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLineEdit" name="divemaster">
<property name="readOnly"> <property name="readOnly">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -123,8 +144,15 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0" colspan="2"> <item row="7" column="1">
<widget class="QLineEdit" name="coordinates"> <widget class="QLabel" name="BuddyLabel">
<property name="text">
<string>Buddy</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLineEdit" name="buddy">
<property name="readOnly"> <property name="readOnly">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -137,22 +165,8 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="10" column="1">
<widget class="QLabel" name="BuddyLabel"> <widget class="QLineEdit" name="suit">
<property name="text">
<string>Buddy</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLineEdit" name="divemaster">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLineEdit" name="buddy">
<property name="readOnly"> <property name="readOnly">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -176,6 +190,13 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="14" column="0" colspan="2">
<widget class="QTextEdit" name="notes">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="9" column="1"> <item row="9" column="1">
<widget class="QLabel" name="SuitLabel"> <widget class="QLabel" name="SuitLabel">
<property name="text"> <property name="text">
@ -193,34 +214,45 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="10" column="1"> <item row="13" column="0">
<widget class="QLineEdit" name="suit">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="NotesLabel"> <widget class="QLabel" name="NotesLabel">
<property name="text"> <property name="text">
<string>Notes</string> <string>Notes</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0" colspan="2"> <item row="15" column="0" colspan="2">
<widget class="QTextEdit" name="notes">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="13" column="0" colspan="2">
<widget class="QDialogButtonBox" name="notesButtonBox"> <widget class="QDialogButtonBox" name="notesButtonBox">
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0" colspan="2">
<widget class="TagWidget" name="tagWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>
@ -248,8 +280,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>513</width> <width>515</width>
<height>468</height> <height>473</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout_5"> <layout class="QGridLayout" name="gridLayout_5">
@ -335,8 +367,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>513</width> <width>515</width>
<height>468</height> <height>473</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout_6"> <layout class="QGridLayout" name="gridLayout_6">
@ -584,7 +616,7 @@
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="4" column="0">
<widget class="QGroupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>Salinity</string> <string>Salinity</string>
</property> </property>
@ -642,8 +674,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>513</width> <width>515</width>
<height>468</height> <height>473</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout_7"> <layout class="QGridLayout" name="gridLayout_7">
@ -802,6 +834,11 @@
<header>tableview.h</header> <header>tableview.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>TagWidget</class>
<extends>QPlainTextEdit</extends>
<header>qt-ui/tagwidget.h</header>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

135
qt-ui/tagwidget.cpp Normal file
View file

@ -0,0 +1,135 @@
#include "tagwidget.h"
#include <QPair>
#include <QDebug>
TagWidget::TagWidget(QWidget *parent) : GroupedLineEdit(parent), m_completer(NULL)
{
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(reparse()));
addColor(QColor(0x00, 0xAE, 0xFF));
addColor(QColor(0x00, 0x78, 0xB0));
}
void TagWidget::setCompleter(QCompleter *completer)
{
m_completer = completer;
m_completer->setWidget(this);
connect(m_completer, SIGNAL(activated(QString)), this, SLOT(completionSelected(QString)));
}
QPair<int,int> TagWidget::getCursorTagPosition() {
int i = 0, start = 0, end = 0;
/* Parse string near cursor */
i = cursorPosition();
while (--i > 0) {
if (text().at(i) == ',') {
if (i > 0 && text().at(i-1) != '\\') {
i++;
break;
}
}
}
start = i;
while (++i < text().length()) {
if (text().at(i) == ',') {
if (i > 0 && text().at(i-1) != '\\')
break;
}
}
end = i;
if (start < 0 || end < 0) {
start = 0;
end = 0;
}
return QPair<int,int>(start,end);
}
enum ParseState {FINDSTART, FINDEND};
void TagWidget::highlight() {
int i = 0, start = 0, end = 0;
ParseState state = FINDEND;
removeAllBlocks();
while(i < text().length()) {
if (text().at(i) == ',') {
if (state == FINDSTART) {
/* Detect empty tags */
} else if (state == FINDEND) {
/* Found end of tag */
if (i > 1) {
if(text().at(i-1) != '\\') {
addBlock(start, end);
state = FINDSTART;
}
} else {
state = FINDSTART;
}
}
} else if (text().at(i) == ' ') {
/* Handled */
} else {
/* Found start of tag */
if (state == FINDSTART) {
state = FINDEND;
start = i;
} else if (state == FINDEND) {
end = i;
}
}
i++;
}
if (state == FINDEND) {
if (end < start)
end = text().length()-1;
if (text().length() > 0)
addBlock(start, end);
}
}
void TagWidget::reparse()
{
highlight();
QPair<int,int> pos = getCursorTagPosition();
QString currentText;
if (pos.first >= 0 && pos.second > 0)
currentText = text().mid(pos.first, pos.second-pos.first).trimmed();
else
currentText = "";
if (m_completer) {
m_completer->setCompletionPrefix(currentText);
m_completer->complete();
}
}
void TagWidget::completionSelected(QString completion) {
QPair <int,int> pos;
pos = getCursorTagPosition();
if (pos.first >= 0 && pos.second > 0) {
setText(text().remove(pos.first, pos.second-pos.first).insert(pos.first, completion));
setCursorPosition(pos.first+completion.length());
}
else {
setText(completion.append(", "));
setCursorPosition(text().length());
}
}
void TagWidget::setCursorPosition(int position) {
blockSignals(true);
GroupedLineEdit::setCursorPosition(position);
blockSignals(false);
}
void TagWidget::setText(QString text) {
blockSignals(true);
GroupedLineEdit::setText(text);
blockSignals(false);
highlight();
}
void TagWidget::clear() {
blockSignals(true);
GroupedLineEdit::clear();
blockSignals(false);
}

26
qt-ui/tagwidget.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef __TAGWIDGET_H
#define __TAGWIDGET_H
#include "groupedlineedit.h"
#include <QCompleter>
#include <QPair>
class TagWidget : public GroupedLineEdit
{
Q_OBJECT
public:
explicit TagWidget(QWidget *parent = 0);
void setCompleter(QCompleter *completer);
QPair<int, int> getCursorTagPosition();
void highlight();
void setText(QString text);
void clear();
void setCursorPosition(int position);
public slots:
void reparse();
void completionSelected(QString);
private:
QCompleter *m_completer;
};
#endif /* __TAGWIDGET_H */

View file

@ -56,7 +56,9 @@ HEADERS = \
subsurfacestartup.h \ subsurfacestartup.h \
uemis.h \ uemis.h \
webservice.h \ webservice.h \
qt-ui/csvimportdialog.h qt-ui/csvimportdialog.h \
qt-ui/tagwidget.h \
qt-ui/groupedlineedit.h
SOURCES = \ SOURCES = \
deco.c \ deco.c \
@ -103,7 +105,9 @@ SOURCES = \
time.c \ time.c \
uemis.c \ uemis.c \
uemis-downloader.c \ uemis-downloader.c \
qt-ui/csvimportdialog.cpp qt-ui/csvimportdialog.cpp \
qt-ui/tagwidget.cpp \
qt-ui/groupedlineedit.cpp
linux*: SOURCES += linux.c linux*: SOURCES += linux.c
mac: SOURCES += macos.c mac: SOURCES += macos.c
@ -121,7 +125,7 @@ FORMS = \
qt-ui/renumber.ui \ qt-ui/renumber.ui \
qt-ui/webservices.ui \ qt-ui/webservices.ui \
qt-ui/tableview.ui \ qt-ui/tableview.ui \
qt-ui/csvimportdialog.ui qt-ui/csvimportdialog.ui
RESOURCES = subsurface.qrc RESOURCES = subsurface.qrc