mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-27 20:58:47 +00:00
Cleanup: Move dive-equipment tab into own translation units
Most tabs in the dive-information widget have there own translation units and ui-files. Only the equipment tab was married with the main tab. Move it out to get more reasonably sized translation units and some isolation. Currently, this needs ugly hacks when entering / checking for edit mode: Access to MainTab is via the MainWindow. And vice/versa, when accessing the DiveEquipmentTab from the MainTab, the former is hardcoded as the first item of an array. These hacks will soon be removed though, when making equipment editing undoable. The tabs will then be independent. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
4bb002d137
commit
fe61f6b69e
8 changed files with 414 additions and 322 deletions
|
@ -48,6 +48,7 @@ set (SUBSURFACE_UI
|
|||
tab-widgets/TabDiveInformation.ui
|
||||
tab-widgets/TabDivePhotos.ui
|
||||
tab-widgets/TabDiveExtraInfo.ui
|
||||
tab-widgets/TabDiveEquipment.ui
|
||||
tab-widgets/TabDiveSite.ui
|
||||
)
|
||||
|
||||
|
@ -118,6 +119,8 @@ set(SUBSURFACE_INTERFACE
|
|||
tab-widgets/TabBase.h
|
||||
tab-widgets/TabDiveExtraInfo.cpp
|
||||
tab-widgets/TabDiveExtraInfo.h
|
||||
tab-widgets/TabDiveEquipment.cpp
|
||||
tab-widgets/TabDiveEquipment.h
|
||||
tab-widgets/TabDiveInformation.cpp
|
||||
tab-widgets/TabDiveInformation.h
|
||||
tab-widgets/TabDivePhotos.cpp
|
||||
|
|
260
desktop-widgets/tab-widgets/TabDiveEquipment.cpp
Normal file
260
desktop-widgets/tab-widgets/TabDiveEquipment.cpp
Normal file
|
@ -0,0 +1,260 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "TabDiveEquipment.h"
|
||||
#include "maintab.h"
|
||||
#include "desktop-widgets/mainwindow.h" // TODO: Only used temporarilly for edit mode changes
|
||||
#include "desktop-widgets/simplewidgets.h" // For isGnome3Session()
|
||||
#include "desktop-widgets/modeldelegates.h"
|
||||
|
||||
#include "profile-widget/profilewidget2.h"
|
||||
|
||||
#include "qt-models/cylindermodel.h"
|
||||
#include "qt-models/weightmodel.h"
|
||||
|
||||
#include "core/divelist.h"
|
||||
#include "core/subsurface-string.h"
|
||||
|
||||
#include <QSettings>
|
||||
TabDiveEquipment::TabDiveEquipment(QWidget *parent) : TabBase(parent),
|
||||
cylindersModel(new CylindersModel(this)),
|
||||
weightModel(new WeightModel(this))
|
||||
{
|
||||
ui.setupUi(this);
|
||||
|
||||
// This makes sure we only delete the models
|
||||
// after the destructor of the tables,
|
||||
// this is needed to save the column sizes.
|
||||
cylindersModel->setParent(ui.cylinders);
|
||||
weightModel->setParent(ui.weights);
|
||||
|
||||
ui.cylinders->setModel(cylindersModel);
|
||||
ui.weights->setModel(weightModel);
|
||||
|
||||
connect(ui.cylinders->view(), &QTableView::clicked, this, &TabDiveEquipment::editCylinderWidget);
|
||||
connect(ui.weights->view(), &QTableView::clicked, this, &TabDiveEquipment::editWeightWidget);
|
||||
|
||||
// Current display of things on Gnome3 looks like shit, so
|
||||
// let's fix that.
|
||||
if (isGnome3Session()) {
|
||||
QPalette p;
|
||||
p.setColor(QPalette::Window, QColor(Qt::white));
|
||||
ui.scrollArea->viewport()->setPalette(p);
|
||||
}
|
||||
|
||||
ui.cylinders->view()->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate(this));
|
||||
ui.cylinders->view()->setItemDelegateForColumn(CylindersModel::USE, new TankUseDelegate(this));
|
||||
ui.weights->view()->setItemDelegateForColumn(WeightModel::TYPE, new WSInfoDelegate(this));
|
||||
ui.cylinders->view()->setColumnHidden(CylindersModel::DEPTH, true);
|
||||
|
||||
ui.cylinders->setTitle(tr("Cylinders"));
|
||||
ui.cylinders->setBtnToolTip(tr("Add cylinder"));
|
||||
connect(ui.cylinders, &TableView::addButtonClicked, this, &TabDiveEquipment::addCylinder_clicked);
|
||||
|
||||
ui.weights->setTitle(tr("Weights"));
|
||||
ui.weights->setBtnToolTip(tr("Add weight system"));
|
||||
connect(ui.weights, &TableView::addButtonClicked, this, &TabDiveEquipment::addWeight_clicked);
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup("cylinders_dialog");
|
||||
for (int i = 0; i < CylindersModel::COLUMNS; i++) {
|
||||
if ((i == CylindersModel::REMOVE) || (i == CylindersModel::TYPE))
|
||||
continue;
|
||||
bool checked = s.value(QString("column%1_hidden").arg(i)).toBool();
|
||||
QAction *action = new QAction(cylindersModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), ui.cylinders->view());
|
||||
action->setCheckable(true);
|
||||
action->setData(i);
|
||||
action->setChecked(!checked);
|
||||
connect(action, &QAction::triggered, this, &TabDiveEquipment::toggleTriggeredColumn);
|
||||
ui.cylinders->view()->setColumnHidden(i, checked);
|
||||
ui.cylinders->view()->horizontalHeader()->addAction(action);
|
||||
}
|
||||
|
||||
ui.cylinders->view()->horizontalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
ui.weights->view()->horizontalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
}
|
||||
|
||||
TabDiveEquipment::~TabDiveEquipment()
|
||||
{
|
||||
QSettings s;
|
||||
s.beginGroup("cylinders_dialog");
|
||||
for (int i = 0; i < CylindersModel::COLUMNS; i++) {
|
||||
if ((i == CylindersModel::REMOVE) || (i == CylindersModel::TYPE))
|
||||
continue;
|
||||
s.setValue(QString("column%1_hidden").arg(i), ui.cylinders->view()->isColumnHidden(i));
|
||||
}
|
||||
}
|
||||
|
||||
void TabDiveEquipment::toggleTriggeredColumn()
|
||||
{
|
||||
QAction *action = qobject_cast<QAction *>(sender());
|
||||
int col = action->data().toInt();
|
||||
QTableView *view = ui.cylinders->view();
|
||||
|
||||
if (action->isChecked()) {
|
||||
view->showColumn(col);
|
||||
if (view->columnWidth(col) <= 15)
|
||||
view->setColumnWidth(col, 80);
|
||||
} else
|
||||
view->hideColumn(col);
|
||||
}
|
||||
|
||||
void TabDiveEquipment::updateData()
|
||||
{
|
||||
cylindersModel->updateDive();
|
||||
weightModel->updateDive();
|
||||
|
||||
ui.cylinders->view()->hideColumn(CylindersModel::DEPTH);
|
||||
if (get_dive_dc(&displayed_dive, dc_number)->divemode == CCR)
|
||||
ui.cylinders->view()->showColumn(CylindersModel::USE);
|
||||
else
|
||||
ui.cylinders->view()->hideColumn(CylindersModel::USE);
|
||||
}
|
||||
|
||||
void TabDiveEquipment::clear()
|
||||
{
|
||||
cylindersModel->clear();
|
||||
weightModel->clear();
|
||||
}
|
||||
|
||||
void TabDiveEquipment::addCylinder_clicked()
|
||||
{
|
||||
MainWindow::instance()->mainTab->enableEdition();
|
||||
cylindersModel->add();
|
||||
}
|
||||
|
||||
void TabDiveEquipment::addWeight_clicked()
|
||||
{
|
||||
MainWindow::instance()->mainTab->enableEdition();
|
||||
weightModel->add();
|
||||
}
|
||||
|
||||
void TabDiveEquipment::editCylinderWidget(const QModelIndex &index)
|
||||
{
|
||||
if (cylindersModel->changed && !MainWindow::instance()->mainTab->isEditing()) {
|
||||
MainWindow::instance()->mainTab->enableEdition();
|
||||
return;
|
||||
}
|
||||
if (index.isValid() && index.column() != CylindersModel::REMOVE) {
|
||||
MainWindow::instance()->mainTab->enableEdition();
|
||||
ui.cylinders->edit(index);
|
||||
}
|
||||
}
|
||||
|
||||
void TabDiveEquipment::editWeightWidget(const QModelIndex &index)
|
||||
{
|
||||
MainWindow::instance()->mainTab->enableEdition();
|
||||
|
||||
if (index.isValid() && index.column() != WeightModel::REMOVE)
|
||||
ui.weights->edit(index);
|
||||
}
|
||||
|
||||
// tricky little macro to edit all the selected dives
|
||||
// loop ove all DIVES and do WHAT.
|
||||
#define MODIFY_DIVES(DIVES, WHAT) \
|
||||
do { \
|
||||
for (dive *mydive: DIVES) { \
|
||||
WHAT; \
|
||||
} \
|
||||
mark_divelist_changed(true); \
|
||||
} while (0)
|
||||
|
||||
// Get the list of selected dives, but put the current dive at the last position of the vector
|
||||
static QVector<dive *> getSelectedDivesCurrentLast()
|
||||
{
|
||||
QVector<dive *> res;
|
||||
struct dive *d;
|
||||
int i;
|
||||
for_each_dive (i, d) {
|
||||
if (d->selected && d != current_dive)
|
||||
res.append(d);
|
||||
}
|
||||
res.append(current_dive);
|
||||
return res;
|
||||
}
|
||||
|
||||
void TabDiveEquipment::acceptChanges()
|
||||
{
|
||||
bool do_replot = false;
|
||||
|
||||
// now check if something has changed and if yes, edit the selected dives that
|
||||
// were identical with the master dive shown (and mark the divelist as changed)
|
||||
struct dive *cd = current_dive;
|
||||
|
||||
// Get list of selected dives, but put the current dive last;
|
||||
// this is required in case the invocation wants to compare things
|
||||
// to the original value in current_dive like it should
|
||||
QVector<dive *> selectedDives = getSelectedDivesCurrentLast();
|
||||
|
||||
if (cylindersModel->changed) {
|
||||
mark_divelist_changed(true);
|
||||
MODIFY_DIVES(selectedDives,
|
||||
for (int i = 0; i < MAX_CYLINDERS; i++) {
|
||||
if (mydive != cd) {
|
||||
if (same_string(mydive->cylinder[i].type.description, cd->cylinder[i].type.description)) {
|
||||
// if we started out with the same cylinder description (for multi-edit) or if we do copt & paste
|
||||
// make sure that we have the same cylinder type and copy the gasmix, but DON'T copy the start
|
||||
// and end pressures (those are per dive after all)
|
||||
if (!same_string(mydive->cylinder[i].type.description, displayed_dive.cylinder[i].type.description)) {
|
||||
free((void*)mydive->cylinder[i].type.description);
|
||||
mydive->cylinder[i].type.description = copy_string(displayed_dive.cylinder[i].type.description);
|
||||
}
|
||||
mydive->cylinder[i].type.size = displayed_dive.cylinder[i].type.size;
|
||||
mydive->cylinder[i].type.workingpressure = displayed_dive.cylinder[i].type.workingpressure;
|
||||
mydive->cylinder[i].gasmix = displayed_dive.cylinder[i].gasmix;
|
||||
mydive->cylinder[i].cylinder_use = displayed_dive.cylinder[i].cylinder_use;
|
||||
mydive->cylinder[i].depth = displayed_dive.cylinder[i].depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
for (int i = 0; i < MAX_CYLINDERS; i++) {
|
||||
// copy the cylinder but make sure we have our own copy of the strings
|
||||
free((void*)cd->cylinder[i].type.description);
|
||||
cd->cylinder[i] = displayed_dive.cylinder[i];
|
||||
cd->cylinder[i].type.description = copy_string(displayed_dive.cylinder[i].type.description);
|
||||
}
|
||||
/* if cylinders changed we may have changed gas change events
|
||||
* and sensor idx in samples as well
|
||||
* - so far this is ONLY supported for a single selected dive */
|
||||
struct divecomputer *tdc = ¤t_dive->dc;
|
||||
struct divecomputer *sdc = &displayed_dive.dc;
|
||||
while(tdc && sdc) {
|
||||
free_events(tdc->events);
|
||||
copy_events(sdc, tdc);
|
||||
free(tdc->sample);
|
||||
copy_samples(sdc, tdc);
|
||||
tdc = tdc->next;
|
||||
sdc = sdc->next;
|
||||
}
|
||||
do_replot = true;
|
||||
}
|
||||
|
||||
if (weightModel->changed) {
|
||||
mark_divelist_changed(true);
|
||||
MODIFY_DIVES(selectedDives,
|
||||
for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
|
||||
if (mydive != cd && (same_string(mydive->weightsystem[i].description, cd->weightsystem[i].description))) {
|
||||
mydive->weightsystem[i] = displayed_dive.weightsystem[i];
|
||||
mydive->weightsystem[i].description = copy_string(displayed_dive.weightsystem[i].description);
|
||||
}
|
||||
}
|
||||
);
|
||||
for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
|
||||
cd->weightsystem[i] = displayed_dive.weightsystem[i];
|
||||
cd->weightsystem[i].description = copy_string(displayed_dive.weightsystem[i].description);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_replot)
|
||||
MainWindow::instance()->graphics->replot();
|
||||
|
||||
cylindersModel->changed = false;
|
||||
weightModel->changed = false;
|
||||
}
|
||||
|
||||
void TabDiveEquipment::rejectChanges()
|
||||
{
|
||||
cylindersModel->changed = false;
|
||||
weightModel->changed = false;
|
||||
cylindersModel->updateDive();
|
||||
weightModel->updateDive();
|
||||
}
|
37
desktop-widgets/tab-widgets/TabDiveEquipment.h
Normal file
37
desktop-widgets/tab-widgets/TabDiveEquipment.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#ifndef TAB_DIVE_EQUIPMENT_H
|
||||
#define TAB_DIVE_EQUIPMENT_H
|
||||
|
||||
#include "TabBase.h"
|
||||
#include "ui_TabDiveEquipment.h"
|
||||
|
||||
namespace Ui {
|
||||
class TabDiveEquipment;
|
||||
};
|
||||
|
||||
class WeightModel;
|
||||
class CylindersModel;
|
||||
|
||||
class TabDiveEquipment : public TabBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TabDiveEquipment(QWidget *parent = 0);
|
||||
~TabDiveEquipment();
|
||||
void updateData() override;
|
||||
void clear() override;
|
||||
void acceptChanges();
|
||||
void rejectChanges();
|
||||
private slots:
|
||||
void addCylinder_clicked();
|
||||
void addWeight_clicked();
|
||||
void toggleTriggeredColumn();
|
||||
void editCylinderWidget(const QModelIndex &index);
|
||||
void editWeightWidget(const QModelIndex &index);
|
||||
private:
|
||||
Ui::TabDiveEquipment ui;
|
||||
|
||||
CylindersModel *cylindersModel;
|
||||
WeightModel *weightModel;
|
||||
};
|
||||
|
||||
#endif // TAB_DIVE_EQUIPMENT_H
|
83
desktop-widgets/tab-widgets/TabDiveEquipment.ui
Normal file
83
desktop-widgets/tab-widgets/TabDiveEquipment.ui
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TabDiveEquipment</class>
|
||||
<widget class="QWidget" name="equipmentTab">
|
||||
<attribute name="title">
|
||||
<string>Equipment</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>523</width>
|
||||
<height>739</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="equipmentTabScrollAreaLayout">
|
||||
<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>
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QVBoxLayout" name="cylinderWeightsLayout">
|
||||
<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="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>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -19,6 +19,14 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TableView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>desktop-widgets/tableview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
|
@ -10,15 +10,12 @@
|
|||
#include "desktop-widgets/mapwidget.h"
|
||||
#include "core/qthelper.h"
|
||||
#include "core/statistics.h"
|
||||
#include "desktop-widgets/modeldelegates.h"
|
||||
#include "qt-models/diveplannermodel.h"
|
||||
#include "desktop-widgets/divelistview.h"
|
||||
#include "core/display.h"
|
||||
#include "profile-widget/profilewidget2.h"
|
||||
#include "desktop-widgets/diveplanner.h"
|
||||
#include "core/divesitehelpers.h"
|
||||
#include "qt-models/cylindermodel.h"
|
||||
#include "qt-models/weightmodel.h"
|
||||
#include "qt-models/divecomputerextradatamodel.h"
|
||||
#include "qt-models/divelocationmodel.h"
|
||||
#include "qt-models/filtermodels.h"
|
||||
|
@ -29,6 +26,7 @@
|
|||
#include "desktop-widgets/command.h"
|
||||
#include "desktop-widgets/simplewidgets.h"
|
||||
|
||||
#include "TabDiveEquipment.h"
|
||||
#include "TabDiveExtraInfo.h"
|
||||
#include "TabDiveInformation.h"
|
||||
#include "TabDivePhotos.h"
|
||||
|
@ -44,8 +42,6 @@
|
|||
#include <QStringList>
|
||||
|
||||
MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
|
||||
weightModel(new WeightModel(this)),
|
||||
cylindersModel(new CylindersModel(this)),
|
||||
editMode(NONE),
|
||||
lastSelectedDive(true),
|
||||
lastTabSelectedDive(0),
|
||||
|
@ -54,6 +50,8 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
|
|||
{
|
||||
ui.setupUi(this);
|
||||
|
||||
extraWidgets << new TabDiveEquipment();
|
||||
ui.tabWidget->addTab(extraWidgets.last(), tr("Equipment"));
|
||||
extraWidgets << new TabDiveInformation();
|
||||
ui.tabWidget->addTab(extraWidgets.last(), tr("Information"));
|
||||
extraWidgets << new TabDiveStatistics();
|
||||
|
@ -70,14 +68,6 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
|
|||
|
||||
memset(&displayed_dive, 0, sizeof(displayed_dive));
|
||||
|
||||
// This makes sure we only delete the models
|
||||
// after the destructor of the tables,
|
||||
// this is needed to save the column sizes.
|
||||
cylindersModel->setParent(ui.cylinders);
|
||||
weightModel->setParent(ui.weights);
|
||||
|
||||
ui.cylinders->setModel(cylindersModel);
|
||||
ui.weights->setModel(weightModel);
|
||||
closeMessage();
|
||||
|
||||
connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &MainTab::divesChanged);
|
||||
|
@ -108,14 +98,6 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
|
|||
// filled from a dive, they are made writeable
|
||||
setEnabled(false);
|
||||
|
||||
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()));
|
||||
|
||||
// This needs to be the same order as enum dive_comp_type in dive.h!
|
||||
QStringList types = QStringList();
|
||||
for (int i = 0; i < NUM_DIVEMODE; i++)
|
||||
|
@ -123,13 +105,6 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
|
|||
ui.DiveType->insertItems(0, types);
|
||||
connect(ui.DiveType, SIGNAL(currentIndexChanged(int)), this, SLOT(divetype_Changed(int)));
|
||||
|
||||
connect(ui.cylinders->view(), SIGNAL(clicked(QModelIndex)), this, SLOT(editCylinderWidget(QModelIndex)));
|
||||
connect(ui.weights->view(), SIGNAL(clicked(QModelIndex)), this, SLOT(editWeightWidget(QModelIndex)));
|
||||
|
||||
ui.cylinders->view()->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate(this));
|
||||
ui.cylinders->view()->setItemDelegateForColumn(CylindersModel::USE, new TankUseDelegate(this));
|
||||
ui.weights->view()->setItemDelegateForColumn(WeightModel::TYPE, new WSInfoDelegate(this));
|
||||
ui.cylinders->view()->setColumnHidden(CylindersModel::DEPTH, true);
|
||||
completers.buddy = new QCompleter(&buddyModel, ui.buddy);
|
||||
completers.divemaster = new QCompleter(&diveMasterModel, ui.divemaster);
|
||||
completers.suit = new QCompleter(&suitModel, ui.suit);
|
||||
|
@ -151,12 +126,11 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
|
|||
setMinimumWidth(0);
|
||||
|
||||
// Current display of things on Gnome3 looks like shit, so
|
||||
// let`s fix that.
|
||||
// let's fix that.
|
||||
if (isGnome3Session()) {
|
||||
QPalette p;
|
||||
p.setColor(QPalette::Window, QColor(Qt::white));
|
||||
ui.scrollArea->viewport()->setPalette(p);
|
||||
ui.scrollArea_2->viewport()->setPalette(p);
|
||||
|
||||
// GroupBoxes in Gnome3 looks like I'v drawn them...
|
||||
static const QString gnomeCss = QStringLiteral(
|
||||
|
@ -183,23 +157,6 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
|
|||
Q_FOREACH (QLabel *label, findChildren<QLabel *>()) {
|
||||
label->setContentsMargins(margins);
|
||||
}
|
||||
ui.cylinders->view()->horizontalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
ui.weights->view()->horizontalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup("cylinders_dialog");
|
||||
for (int i = 0; i < CylindersModel::COLUMNS; i++) {
|
||||
if ((i == CylindersModel::REMOVE) || (i == CylindersModel::TYPE))
|
||||
continue;
|
||||
bool checked = s.value(QString("column%1_hidden").arg(i)).toBool();
|
||||
action = new QAction(cylindersModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), ui.cylinders->view());
|
||||
action->setCheckable(true);
|
||||
action->setData(i);
|
||||
action->setChecked(!checked);
|
||||
connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleTriggeredColumn()));
|
||||
ui.cylinders->view()->setColumnHidden(i, checked);
|
||||
ui.cylinders->view()->horizontalHeader()->addAction(action);
|
||||
}
|
||||
|
||||
connect(ui.diveNotesMessage, &KMessageWidget::showAnimationFinished,
|
||||
ui.location, &DiveLocationLineEdit::fixPopupPosition);
|
||||
|
@ -212,27 +169,6 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
|
|||
|
||||
MainTab::~MainTab()
|
||||
{
|
||||
QSettings s;
|
||||
s.beginGroup("cylinders_dialog");
|
||||
for (int i = 0; i < CylindersModel::COLUMNS; i++) {
|
||||
if ((i == CylindersModel::REMOVE) || (i == CylindersModel::TYPE))
|
||||
continue;
|
||||
s.setValue(QString("column%1_hidden").arg(i), ui.cylinders->view()->isColumnHidden(i));
|
||||
}
|
||||
}
|
||||
|
||||
void MainTab::toggleTriggeredColumn()
|
||||
{
|
||||
QAction *action = qobject_cast<QAction *>(sender());
|
||||
int col = action->data().toInt();
|
||||
QTableView *view = ui.cylinders->view();
|
||||
|
||||
if (action->isChecked()) {
|
||||
view->showColumn(col);
|
||||
if (view->columnWidth(col) <= 15)
|
||||
view->setColumnWidth(col, 80);
|
||||
} else
|
||||
view->hideColumn(col);
|
||||
}
|
||||
|
||||
void MainTab::addMessageAction(QAction *action)
|
||||
|
@ -412,12 +348,6 @@ void MainTab::tripChanged(dive_trip *trip, TripField field)
|
|||
}
|
||||
}
|
||||
|
||||
void MainTab::clearEquipment()
|
||||
{
|
||||
cylindersModel->clear();
|
||||
weightModel->clear();
|
||||
}
|
||||
|
||||
void MainTab::nextInputField(QKeyEvent *event)
|
||||
{
|
||||
keyPressEvent(event);
|
||||
|
@ -495,16 +425,14 @@ void MainTab::updateDiveInfo()
|
|||
// If there is no current dive, disable all widgets except the last, which is the dive site tab.
|
||||
// TODO: Conceptually, the dive site tab shouldn't even be a tab here!
|
||||
bool enabled = current_dive != nullptr;
|
||||
ui.equipmentTab->setEnabled(enabled);
|
||||
ui.notesTab->setEnabled(enabled);
|
||||
for (int i = 0; i < extraWidgets.size() - 1; ++i)
|
||||
extraWidgets[i]->setEnabled(enabled);
|
||||
|
||||
editMode = IGNORE; // don't trigger on changes to the widgets
|
||||
|
||||
for (auto widget : extraWidgets) {
|
||||
for (TabBase *widget: extraWidgets)
|
||||
widget->updateData();
|
||||
}
|
||||
|
||||
UPDATE_TEXT(suit);
|
||||
UPDATE_TEXT(divemaster);
|
||||
|
@ -563,8 +491,6 @@ void MainTab::updateDiveInfo()
|
|||
//ui.location->setText(currentTrip->location);
|
||||
ui.NotesLabel->setText(tr("Trip notes"));
|
||||
ui.notes->setText(currentTrip->notes);
|
||||
clearEquipment();
|
||||
ui.equipmentTab->setEnabled(false);
|
||||
ui.depth->setVisible(false);
|
||||
ui.depthLabel->setVisible(false);
|
||||
ui.duration->setVisible(false);
|
||||
|
@ -615,9 +541,6 @@ void MainTab::updateDiveInfo()
|
|||
// reset labels in case we last displayed trip notes
|
||||
ui.LocationLabel->setText(tr("Location"));
|
||||
ui.NotesLabel->setText(tr("Notes"));
|
||||
ui.equipmentTab->setEnabled(true);
|
||||
cylindersModel->updateDive();
|
||||
weightModel->updateDive();
|
||||
ui.tagWidget->setText(get_taglist_string(current_dive->tag_list));
|
||||
bool isManual = same_string(current_dive->dc.model, "manually added dive");
|
||||
ui.depth->setVisible(isManual);
|
||||
|
@ -660,31 +583,12 @@ void MainTab::updateDiveInfo()
|
|||
ui.tagWidget->clear();
|
||||
}
|
||||
editMode = rememberEM;
|
||||
ui.cylinders->view()->hideColumn(CylindersModel::DEPTH);
|
||||
if (get_dive_dc(&displayed_dive, dc_number)->divemode == CCR)
|
||||
ui.cylinders->view()->showColumn(CylindersModel::USE);
|
||||
else
|
||||
ui.cylinders->view()->hideColumn(CylindersModel::USE);
|
||||
|
||||
if (verbose && current_dive && current_dive->dive_site)
|
||||
qDebug() << "Set the current dive site:" << current_dive->dive_site->uuid;
|
||||
emit diveSiteChanged();
|
||||
}
|
||||
|
||||
void MainTab::addCylinder_clicked()
|
||||
{
|
||||
if (editMode == NONE)
|
||||
enableEdition();
|
||||
cylindersModel->add();
|
||||
}
|
||||
|
||||
void MainTab::addWeight_clicked()
|
||||
{
|
||||
if (editMode == NONE)
|
||||
enableEdition();
|
||||
weightModel->add();
|
||||
}
|
||||
|
||||
void MainTab::reload()
|
||||
{
|
||||
suitModel.updateModel();
|
||||
|
@ -694,16 +598,6 @@ void MainTab::reload()
|
|||
LocationInformationModel::instance()->update();
|
||||
}
|
||||
|
||||
// tricky little macro to edit all the selected dives
|
||||
// loop ove all DIVES and do WHAT.
|
||||
#define MODIFY_DIVES(DIVES, WHAT) \
|
||||
do { \
|
||||
for (dive *mydive: DIVES) { \
|
||||
WHAT; \
|
||||
} \
|
||||
mark_divelist_changed(true); \
|
||||
} while (0)
|
||||
|
||||
void MainTab::refreshDisplayedDiveSite()
|
||||
{
|
||||
struct dive_site *ds = get_dive_site_for_dive(current_dive);
|
||||
|
@ -711,24 +605,9 @@ void MainTab::refreshDisplayedDiveSite()
|
|||
ui.location->setCurrentDiveSite(ds);
|
||||
}
|
||||
|
||||
// Get the list of selected dives, but put the current dive at the last position of the vector
|
||||
static QVector<dive *> getSelectedDivesCurrentLast()
|
||||
{
|
||||
QVector<dive *> res;
|
||||
struct dive *d;
|
||||
int i;
|
||||
for_each_dive (i, d) {
|
||||
if (d->selected && d != current_dive)
|
||||
res.append(d);
|
||||
}
|
||||
res.append(current_dive);
|
||||
return res;
|
||||
}
|
||||
|
||||
void MainTab::acceptChanges()
|
||||
{
|
||||
int addedId = -1;
|
||||
bool do_replot = false;
|
||||
|
||||
if (ui.location->hasFocus())
|
||||
stealFocus();
|
||||
|
@ -739,81 +618,18 @@ void MainTab::acceptChanges()
|
|||
tabBar()->setTabIcon(1, QIcon()); // Equipment
|
||||
ui.dateEdit->setEnabled(true);
|
||||
hideMessage();
|
||||
ui.equipmentTab->setEnabled(true);
|
||||
|
||||
// Get list of selected dives, but put the current dive last;
|
||||
// this is required in case the invocation wants to compare things
|
||||
// to the original value in current_dive like it should
|
||||
QVector<dive *> selectedDives = getSelectedDivesCurrentLast();
|
||||
if (lastMode == MANUALLY_ADDED_DIVE) {
|
||||
// preserve any changes to the profile
|
||||
free(current_dive->dc.sample);
|
||||
copy_samples(&displayed_dive.dc, ¤t_dive->dc);
|
||||
addedId = displayed_dive.id;
|
||||
}
|
||||
// now check if something has changed and if yes, edit the selected dives that
|
||||
// were identical with the master dive shown (and mark the divelist as changed)
|
||||
struct dive *cd = current_dive;
|
||||
|
||||
if (cylindersModel->changed) {
|
||||
mark_divelist_changed(true);
|
||||
MODIFY_DIVES(selectedDives,
|
||||
for (int i = 0; i < MAX_CYLINDERS; i++) {
|
||||
if (mydive != cd) {
|
||||
if (same_string(mydive->cylinder[i].type.description, cd->cylinder[i].type.description)) {
|
||||
// if we started out with the same cylinder description (for multi-edit) or if we do copt & paste
|
||||
// make sure that we have the same cylinder type and copy the gasmix, but DON'T copy the start
|
||||
// and end pressures (those are per dive after all)
|
||||
if (!same_string(mydive->cylinder[i].type.description, displayed_dive.cylinder[i].type.description)) {
|
||||
free((void*)mydive->cylinder[i].type.description);
|
||||
mydive->cylinder[i].type.description = copy_string(displayed_dive.cylinder[i].type.description);
|
||||
}
|
||||
mydive->cylinder[i].type.size = displayed_dive.cylinder[i].type.size;
|
||||
mydive->cylinder[i].type.workingpressure = displayed_dive.cylinder[i].type.workingpressure;
|
||||
mydive->cylinder[i].gasmix = displayed_dive.cylinder[i].gasmix;
|
||||
mydive->cylinder[i].cylinder_use = displayed_dive.cylinder[i].cylinder_use;
|
||||
mydive->cylinder[i].depth = displayed_dive.cylinder[i].depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
for (int i = 0; i < MAX_CYLINDERS; i++) {
|
||||
// copy the cylinder but make sure we have our own copy of the strings
|
||||
free((void*)cd->cylinder[i].type.description);
|
||||
cd->cylinder[i] = displayed_dive.cylinder[i];
|
||||
cd->cylinder[i].type.description = copy_string(displayed_dive.cylinder[i].type.description);
|
||||
}
|
||||
/* if cylinders changed we may have changed gas change events
|
||||
* and sensor idx in samples as well
|
||||
* - so far this is ONLY supported for a single selected dive */
|
||||
struct divecomputer *tdc = ¤t_dive->dc;
|
||||
struct divecomputer *sdc = &displayed_dive.dc;
|
||||
while(tdc && sdc) {
|
||||
free_events(tdc->events);
|
||||
copy_events(sdc, tdc);
|
||||
free(tdc->sample);
|
||||
copy_samples(sdc, tdc);
|
||||
tdc = tdc->next;
|
||||
sdc = sdc->next;
|
||||
}
|
||||
do_replot = true;
|
||||
}
|
||||
// TODO: This is a temporary hack until the equipment tab is included in the undo system:
|
||||
// The equipment tab is hardcoded at the first place of the "extra widgets".
|
||||
((TabDiveEquipment *)extraWidgets[0])->acceptChanges();
|
||||
|
||||
if (weightModel->changed) {
|
||||
mark_divelist_changed(true);
|
||||
MODIFY_DIVES(selectedDives,
|
||||
for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
|
||||
if (mydive != cd && (same_string(mydive->weightsystem[i].description, cd->weightsystem[i].description))) {
|
||||
mydive->weightsystem[i] = displayed_dive.weightsystem[i];
|
||||
mydive->weightsystem[i].description = copy_string(displayed_dive.weightsystem[i].description);
|
||||
}
|
||||
}
|
||||
);
|
||||
for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
|
||||
cd->weightsystem[i] = displayed_dive.weightsystem[i];
|
||||
cd->weightsystem[i].description = copy_string(displayed_dive.weightsystem[i].description);
|
||||
}
|
||||
}
|
||||
if (lastMode == MANUALLY_ADDED_DIVE) {
|
||||
// we just added or edited the dive, let fixup_dive() make
|
||||
// sure we get the max. depth right
|
||||
|
@ -833,8 +649,6 @@ void MainTab::acceptChanges()
|
|||
MainWindow::instance()->refreshDisplay();
|
||||
MainWindow::instance()->graphics->replot();
|
||||
} else {
|
||||
if (do_replot)
|
||||
MainWindow::instance()->graphics->replot();
|
||||
MainWindow::instance()->diveList->rememberSelection();
|
||||
MainWindow::instance()->refreshDisplay();
|
||||
MainWindow::instance()->diveList->restoreSelection();
|
||||
|
@ -843,8 +657,6 @@ void MainTab::acceptChanges()
|
|||
MainWindow::instance()->diveList->verticalScrollBar()->setSliderPosition(scrolledBy);
|
||||
MainWindow::instance()->diveList->setFocus();
|
||||
MainWindow::instance()->exitEditState();
|
||||
cylindersModel->changed = false;
|
||||
weightModel->changed = false;
|
||||
MainWindow::instance()->setEnabledToolbar(true);
|
||||
ui.editDiveSiteButton->setEnabled(!ui.location->text().isEmpty());
|
||||
editMode = NONE;
|
||||
|
@ -879,9 +691,14 @@ void MainTab::rejectChanges()
|
|||
clear_dive(&displayed_dive);
|
||||
updateDiveInfo();
|
||||
|
||||
for (auto widget : extraWidgets) {
|
||||
for (auto widget: extraWidgets) {
|
||||
widget->updateData();
|
||||
}
|
||||
|
||||
// TODO: This is a temporary hack until the equipment tab is included in the undo system:
|
||||
// The equipment tab is hardcoded at the first place of the "extra widgets".
|
||||
((TabDiveEquipment *)extraWidgets[0])->rejectChanges();
|
||||
|
||||
// the user could have edited the location and then canceled the edit
|
||||
// let's get the correct location back in view
|
||||
MapWidget::instance()->centerOnDiveSite(current_dive ? current_dive->dive_site : nullptr);
|
||||
|
@ -889,10 +706,6 @@ void MainTab::rejectChanges()
|
|||
MainWindow::instance()->graphics->replot();
|
||||
MainWindow::instance()->setEnabledToolbar(true);
|
||||
MainWindow::instance()->exitEditState();
|
||||
cylindersModel->changed = false;
|
||||
weightModel->changed = false;
|
||||
cylindersModel->updateDive();
|
||||
weightModel->updateDive();
|
||||
ui.editDiveSiteButton->setEnabled(!ui.location->text().isEmpty());
|
||||
}
|
||||
|
||||
|
@ -963,10 +776,18 @@ void MainTab::on_watertemp_editingFinished()
|
|||
// all dives are shifted by an offset.
|
||||
static void shiftTime(QDateTime &dateTime)
|
||||
{
|
||||
QVector<dive *> dives;
|
||||
struct dive *d;
|
||||
int i;
|
||||
for_each_dive (i, d) {
|
||||
if (d->selected)
|
||||
dives.append(d);
|
||||
}
|
||||
|
||||
timestamp_t when = dateTime.toTime_t();
|
||||
if (current_dive && current_dive->when != when) {
|
||||
timestamp_t offset = current_dive->when - when;
|
||||
Command::shiftTime(getSelectedDivesCurrentLast(), (int)offset);
|
||||
Command::shiftTime(dives, (int)offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1055,32 +876,6 @@ void MainTab::on_visibility_valueChanged(int value)
|
|||
Command::editVisibility(value, false);
|
||||
}
|
||||
|
||||
#undef MODIFY_DIVES
|
||||
|
||||
void MainTab::editCylinderWidget(const QModelIndex &index)
|
||||
{
|
||||
// we need a local copy or bad things happen when enableEdition() is called
|
||||
QModelIndex editIndex = index;
|
||||
if (cylindersModel->changed && editMode == NONE) {
|
||||
enableEdition();
|
||||
return;
|
||||
}
|
||||
if (editIndex.isValid() && editIndex.column() != CylindersModel::REMOVE) {
|
||||
if (editMode == NONE)
|
||||
enableEdition();
|
||||
ui.cylinders->edit(editIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void MainTab::editWeightWidget(const QModelIndex &index)
|
||||
{
|
||||
if (editMode == NONE)
|
||||
enableEdition();
|
||||
|
||||
if (index.isValid() && index.column() != WeightModel::REMOVE)
|
||||
ui.weights->edit(index);
|
||||
}
|
||||
|
||||
// Remove focus from any active field to update the corresponding value in the dive.
|
||||
// Do this by setting the focus to ourself
|
||||
void MainTab::stealFocus()
|
||||
|
@ -1102,5 +897,4 @@ void MainTab::clearTabs()
|
|||
{
|
||||
for (auto widget: extraWidgets)
|
||||
widget->clear();
|
||||
clearEquipment();
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
#include "core/dive.h"
|
||||
#include "core/subsurface-qt/DiveListNotifier.h"
|
||||
|
||||
class WeightModel;
|
||||
class CylindersModel;
|
||||
class ExtraDataModel;
|
||||
class DivePictureModel;
|
||||
class QCompleter;
|
||||
|
@ -46,7 +44,6 @@ public:
|
|||
MainTab(QWidget *parent = 0);
|
||||
~MainTab();
|
||||
void clearTabs();
|
||||
void clearEquipment();
|
||||
void reload();
|
||||
void initialUiSetup();
|
||||
bool isEditing();
|
||||
|
@ -62,8 +59,6 @@ slots:
|
|||
void divesChanged(dive_trip *trip, const QVector<dive *> &dives, DiveField field);
|
||||
void diveSiteEdited(dive_site *ds, int field);
|
||||
void tripChanged(dive_trip *trip, TripField field);
|
||||
void addCylinder_clicked();
|
||||
void addWeight_clicked();
|
||||
void updateDiveInfo();
|
||||
void updateNotes(const struct dive *d);
|
||||
void updateMode(struct dive *d);
|
||||
|
@ -87,20 +82,15 @@ slots:
|
|||
void on_rating_valueChanged(int value);
|
||||
void on_visibility_valueChanged(int value);
|
||||
void on_tagWidget_editingFinished();
|
||||
void editCylinderWidget(const QModelIndex &index);
|
||||
void editWeightWidget(const QModelIndex &index);
|
||||
void addMessageAction(QAction *action);
|
||||
void hideMessage();
|
||||
void closeMessage();
|
||||
void displayMessage(QString str);
|
||||
void enableEdition(EditMode newEditMode = NONE);
|
||||
void toggleTriggeredColumn();
|
||||
void updateTextLabels(bool showUnits = true);
|
||||
void escDetected(void);
|
||||
private:
|
||||
Ui::MainTab ui;
|
||||
WeightModel *weightModel;
|
||||
CylindersModel *cylindersModel;
|
||||
EditMode editMode;
|
||||
BuddyCompletionModel buddyModel;
|
||||
DiveMasterCompletionModel diveMasterModel;
|
||||
|
|
|
@ -491,83 +491,6 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="equipmentTab">
|
||||
<attribute name="title">
|
||||
<string>Equipment</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea_2">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>523</width>
|
||||
<height>739</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="equipmentTabScrollAreaLayout">
|
||||
<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>
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QVBoxLayout" name="cylinderWeightsLayout">
|
||||
<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="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>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -585,12 +508,6 @@
|
|||
<header>desktop-widgets/starwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>TableView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>desktop-widgets/tableview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>TagWidget</class>
|
||||
<extends>QPlainTextEdit</extends>
|
||||
|
|
Loading…
Reference in a new issue