6 #include <QApplication>
9 #include <QStringBuilder>
15 #include "gui/models/allmodels.h"
22 using namespace swift::gui::menus;
25 using namespace swift::gui::settings;
33 this->setSortingEnabled(
true);
40 Q_ASSERT_X(m_model, Q_FUNC_INFO,
"Missing model");
41 if (container.isEmpty())
49 this->showLoadIndicator(container.size());
50 const bool reallyResize = resize && isResizeConditionMet(container.size());
51 bool presize = (m_resizeMode == PresizeSubset) && this->isEmpty() &&
54 presize || (this->isEmpty() && resize &&
56 const bool presizeThresholdReached =
57 presize && container.size() > ResizeSubsetThreshold;
60 if (presizeThresholdReached)
62 const int presizeRandomElements = this->getPresizeRandomElementsSize(container.size());
63 if (presizeRandomElements > 0)
65 m_model->update(container.sampleElements(presizeRandomElements),
false);
66 this->fullResizeToContents();
70 const int c = m_model->update(container, sort);
73 if (presizeThresholdReached)
77 else if (reallyResize)
79 this->resizeToContents();
81 else if (presize && !presizeThresholdReached)
84 this->fullResizeToContents();
86 this->updateSortIndicator();
87 this->hideLoadIndicator();
95 if (container.isEmpty())
103 const auto sortColumn = model->getSortColumn();
104 const auto sortOrder = model->getSortOrder();
105 this->showLoadIndicator(container.size());
106 CWorker *worker = CWorker::fromTask(
this,
"ViewSort", [model, container, sortColumn, sortOrder]() {
107 return model->sortContainerByColumn(container, sortColumn, sortOrder);
110 this->updateContainer(sortedContainer,
false, resize);
119 if (container.isEmpty()) { this->clear(); }
120 else if (container.size() > ASyncRowsCountThreshold && sort)
123 this->updateContainerAsync(container, sort, resize);
125 else { this->updateContainer(container, sort, resize); }
132 if (this->rowCount() < 1)
135 this->updateContainerMaybeAsync(
ContainerType({ value }),
true, resize);
139 m_model->insert(value);
140 if (resize) { this->performModeBasedResizeToContent(); }
148 if (this->rowCount() < 1)
151 this->updateContainerMaybeAsync(container,
true, resize);
155 m_model->insert(container);
156 if (resize) { this->performModeBasedResizeToContent(); }
164 if (this->rowCount() < 1)
167 this->updateContainerMaybeAsync(
ContainerType({ value }),
true, resize);
171 m_model->push_back(value);
172 if (resize) { this->performModeBasedResizeToContent(); }
180 if (this->rowCount() < 1)
183 this->updateContainerMaybeAsync(container,
true, resize);
187 m_model->push_back(container);
188 if (resize) { this->performModeBasedResizeToContent(); }
196 return m_model->at(index);
203 return m_model->container();
212 const int i = this->rowOf(o);
213 if (i >= 0) { rows.push_back(i); }
222 const ContainerType objects = m_model->containerOrFilteredContainer();
226 if (o == obj) {
return i; }
236 return m_model->containerOrFilteredContainer(filtered);
244 const QModelIndexList indexes = this->selectedRows();
245 for (
const QModelIndex &i : indexes) { c.push_back(this->at(i)); }
252 if (!this->hasSelection()) {
return this->containerOrFilteredContainer(); }
254 const QModelIndexList indexes = this->unselectedRows();
255 for (
const QModelIndex &i : indexes) { c.push_back(this->at(i)); }
262 if (this->hasSelection()) {
return this->selectedObjects().front(); }
263 if (this->rowCount() < 2) {
return this->containerOrFilteredContainer().frontOrDefault(); }
272 if (vm.
isEmpty()) {
return 0; }
273 if (!hasSelection()) {
return 0; }
276 int lastUpdatedRow = -1;
277 int firstUpdatedRow = -1;
279 const QModelIndexList indexes = this->selectedRows();
281 for (
const QModelIndex &i : indexes)
283 if (i.row() == lastUpdatedRow) {
continue; }
284 lastUpdatedRow = i.row();
285 if (firstUpdatedRow < 0 || lastUpdatedRow < firstUpdatedRow) { firstUpdatedRow = lastUpdatedRow; }
289 for (
const CPropertyIndex &pi : propertyIndexes) { obj.setPropertyByIndex(pi, vm.
value(pi)); }
292 if (this->derivedModel()->setInContainer(i, obj)) { c++; }
295 if (c > 0) { this->derivedModel()->emitDataChanged(firstUpdatedRow, lastUpdatedRow); }
303 return this->updateSelected(vm);
310 return c.frontOrDefault();
316 if (!this->hasSelection()) {
return 0; }
317 if (this->isEmpty()) {
return 0; }
319 const int currentRows = this->rowCount();
321 const CVariant deletedObjsVariant = CVariant::from(selected);
324 if (!this->hasFilter() && currentRows == this->selectedRowCount())
333 unselectedObjects.removeIfInSubset(selected);
334 this->updateContainerMaybeAsync(unselectedObjects);
335 delta = currentRows - unselectedObjects.size();
337 emit this->objectsDeleted(deletedObjsVariant);
344 const int rc = this->rowCount();
345 if (rc > ResizeSubsetThreshold)
347 const int presizeRandomElements = this->getPresizeRandomElementsSize(rc);
348 if (presizeRandomElements > 0)
351 m_model->update(containerBackup.sampleElements(presizeRandomElements),
false);
352 this->fullResizeToContents();
353 m_model->update(containerBackup,
false);
356 else { this->fullResizeToContents(); }
363 return m_model->clearHighlighting();
370 if (!m_model->hasFilter()) {
return; }
371 if (this->isEmpty()) {
return; }
373 this->removeFilter();
374 this->updateContainerMaybeAsync(filtered);
382 this->hideLoadIndicator();
389 return m_model->rowCount();
396 return m_model->columnCount(QModelIndex());
403 return m_model->rowCount() < 1;
410 return m_model->isOrderable();
419 this->viewport()->setAcceptDrops(allowDrop);
420 this->setDragEnabled(allowDrag);
421 this->setDropIndicatorShown(allowDrag || allowDrop);
422 m_model->allowDrop(allowDrop);
423 m_model->allowJsonFileDrop(allowDropJsonFile);
430 return m_model->isDropAllowed();
436 if (m_model && m_model->isJsonFileDropAllowed() &&
440 const CStatusMessage msgs = this->loadJsonFile(fi.absoluteFilePath());
451 const bool a = m_model->acceptDrop(mimeData);
459 return m_model->setSorting(propertyIndex, order);
465 m_model->sortByPropertyIndex(propertyIndex, order);
484 return m_model->toJson(selectedOnly);
491 return m_model->toJsonString(format, selectedOnly);
499 QTableView::setObjectName(name);
500 m_model->setObjectName(name);
506 this->derivedModel()->takeFilterOwnership(filter);
512 this->derivedModel()->removeFilter();
518 return derivedModel()->hasFilter();
524 if (objectType) { m_model->addAcceptedMetaTypeId(qMetaTypeId<ObjectType>()); }
525 if (containerType) { m_model->addAcceptedMetaTypeId(qMetaTypeId<ContainerType>()); }
531 Q_ASSERT_X(isOrderable(), Q_FUNC_INFO,
"Model not orderable");
532 this->allowDragDrop(
true,
true);
533 this->setDragDropMode(InternalMove);
534 this->setDropActions(Qt::MoveAction);
535 this->addContainerTypesAsDropTypes(
true,
true);
542 QString o = tw->tabText(index);
543 const QString f = this->hasFilter() ?
"F" :
"";
545 tw->setTabText(index, o);
551 const int width = this->width() - 25;
552 QList<int> widths = this->getColumns().calculateWidths(width);
553 if (widths.isEmpty()) {
return; }
554 for (
int c = 0; c < this->getColumns().size(); c++)
556 const int w = widths.at(c);
557 this->setColumnWidth(c, w);
564 if (m_model->hasValidSortColumn())
566 Q_ASSERT(this->horizontalHeader());
567 this->horizontalHeader()->setSortIndicator(m_model->getSortColumn(), m_model->getSortOrder());
574 Q_ASSERT_X(model || m_model, Q_FUNC_INFO,
"Missing model");
577 if (model == m_model) {
return; }
578 if (m_model) { m_model->disconnect(); }
581 m_model->setSelectionModel(
this);
583 Q_ASSERT_X(c, Q_FUNC_INFO,
"Connect failed");
585 Q_ASSERT_X(c, Q_FUNC_INFO,
"Connect failed");
587 Q_ASSERT_X(c, Q_FUNC_INFO,
"Connect failed");
589 Q_ASSERT_X(c, Q_FUNC_INFO,
"Connect failed");
591 Q_ASSERT_X(c, Q_FUNC_INFO,
"Connect failed");
595 this->setModel(m_model);
597 this->setSortIndicator();
603 if (containerSize < 0) {
return this->rowCount() > m_skipResizeThreshold; }
604 return containerSize > m_skipResizeThreshold;
612 if (this->isResizeConditionMet()) { this->fullResizeToContents(); }
623 return this->updateContainer(c, sort, resize);
629 if (this->derivedModel()->hasValidSortColumn())
631 const int index = this->derivedModel()->getSortColumn();
632 Qt::SortOrder order = this->derivedModel()->getSortOrder();
633 this->horizontalHeader()->setSortIndicator(index, order);
648 m_dropIndicator = indicator;
654 Q_UNUSED(selectedObjects)
661 static const CStatusMessage e(
this, CStatusMessage::SeverityInfo, u
"no modification",
true);
669 static const CStatusMessage e(
this, CStatusMessage::SeverityInfo, u
"validation passed",
true);
685 if (this->derivedModel()->hasHighlightedRows())
687 menuActions.
addAction(CIcons::refresh16(),
"Clear highlighting", CMenuAction::pathViewClearHighlighting(),
697 if (fileName.isEmpty())
703 const QString json(CFileUtils::readFileToString(fileName));
709 if (!json::looksLikeSwiftJson(json))
717 const bool allowCacheFormat = this->allowCacheFileFormatJson();
719 if (jsonObject.isEmpty())
727 if (jsonObject.contains(
"type") && jsonObject.contains(
"value"))
737 container = containerVariant.
value<ContainerType>();
742 container.convertFromJson(jsonObject);
745 const int countBefore = container.size();
746 m = this->modifyLoadedJsonData(container);
748 if (countBefore > 0 && container.isEmpty()) {
break; }
749 m = this->validateLoadedJsonData(container);
751 this->updateContainerMaybeAsync(container);
752 m =
CStatusMessage(
this, CStatusMessage::SeverityInfo,
"Reading " + fileName +
" completed",
true);
753 this->jsonLoadedAndModelUpdated(container);
754 this->rememberLastJsonDirectory(fileName);
758 m = CStatusMessage::fromJsonException(ex,
this, QString(
"Reading JSON from '%1'").arg(fileName));
764 emit this->jsonLoadCompleted(m);
771 const ContainerType container = selectedOnly ? this->selectedObjects() : this->container();
772 const QString json = container.toJsonString();
773 QTextEdit *te = this->textEditDialog()->textEdit();
774 te->setReadOnly(
true);
776 this->textEditDialog()->show();
782 const QString fileName = QFileDialog::getOpenFileName(
783 nullptr, tr(
"Load data file"), directory.isEmpty() ? this->getFileDialogFileName(
true) : directory,
784 tr(
"swift (*.json *.txt)"));
785 return this->loadJsonFile(fileName);
791 const QString fileName = QFileDialog::getSaveFileName(
792 nullptr, tr(
"Save data file"), directory.isEmpty() ? this->getFileDialogFileName(
false) : directory,
793 tr(
"swift (*.json *.txt)"));
794 if (fileName.isEmpty()) {
return CStatusMessage(
this, CStatusMessage::SeverityDebug, u
"Save canceled",
true); }
795 const QString json(this->toJsonString(QJsonDocument::Indented, selectedOnly));
798 CWorker::fromTask(qApp, Q_FUNC_INFO, [=] { CFileUtils::writeStringToFile(json, fileName); });
799 this->rememberLastJsonDirectory(fileName);
800 return CStatusMessage(
this, CStatusMessage::SeverityInfo, u
"Writing " % fileName % u
" in progress",
true);
806 QClipboard *clipboard = QApplication::clipboard();
807 if (!clipboard) {
return; }
808 if (!this->hasSelection()) {
return; }
810 if (selection.isEmpty()) {
return; }
811 const CVariant copyJson = CVariant::from(selection);
813 clipboard->setText(json);
819 if (!QApplication::clipboard()) {
return; }
821 this->removeSelectedRows();
827 const QClipboard *clipboard = QApplication::clipboard();
828 if (!clipboard) {
return; }
829 const QString json = clipboard->text();
830 if (json.isEmpty()) {
return; }
831 if (!json::looksLikeSwiftJson(json)) {
return; }
835 objects.convertFromJson(json);
836 if (!objects.isEmpty()) { this->insert(objects); }
847 const QDialog::DialogCode statusCode =
static_cast<QDialog::DialogCode
>(status);
848 return filterWidgetChangedFilter(statusCode == QDialog::Accepted);
856 if (!m_filterWidget) { this->removeFilter(); }
862 Q_ASSERT_X(provider, Q_FUNC_INFO,
"Filter widget does not provide interface");
863 if (!provider) {
return false; }
865 if (f->isValid()) { this->takeFilterOwnership(f); }
866 else { this->removeFilter(); }
872 this->removeFilter();
880 if (!m_acceptClickSelection) {
return; }
881 if (!index.isValid()) {
return; }
882 emit this->objectClicked(CVariant::fromValue(at(index)));
888 if (!m_acceptDoubleClickSelection) {
return; }
889 if (!index.isValid()) {
return; }
890 emit this->objectDoubleClicked(CVariant::fromValue(at(index)));
896 if (!m_acceptRowSelection) {
return; }
897 if (!index.isValid()) {
return; }
898 emit this->objectSelected(CVariant::fromValue(at(index)));
static QString replaceTabCountValue(const QString &oldName, int count)
Replace count in name such as "stations (4)".
static bool isMimeRepresentingReadableJsonFile(const QMimeData *mime)
Is representing existing JSON file.
static QFileInfo representedMimeFile(const QMimeData *mime)
Represented file if any.
Model filter interface for those who can generate such a filter (e.g. a widget or dialog)
virtual std::unique_ptr< IModelFilter< ContainerType > > createModelFilter() const =0
Get the filter, this is the filter itself, not its widget or dialog.
void sort()
Sort if columns or order changed.
virtual ContainerType unselectedObjects() const
Unselected objects.
virtual bool filterDialogFinished(int status)
Filter dialog finished.
int rowOf(const ObjectType &obj) const
The row of the given object.
virtual void cut()
Clipboard cut/copy/paste.
virtual int performUpdateContainer(const swift::misc::CVariant &variant, bool sort, bool resize)
Helper method with template free signature.
void standardInit(ModelClass *model=nullptr)
Standard initialization.
virtual swift::misc::CStatusMessage loadJsonFile(const QString &fileName)
Method creating the menu.
void initAsOrderable()
Init so items can be ordered, includes enabling drag and drop.
virtual bool acceptDrop(const QMimeData *mimeData) const
Accept drop data?
QList< int > rowsOf(const ContainerType &container) const
The rows of the given objects.
virtual void removeFilter()
Remove filter.
virtual void onRowSelected(const QModelIndex &index)
Row selected.
void setTabWidgetViewText(QTabWidget *tw, int index)
Set a tab widget text based on row count, filter etc.
virtual swift::misc::CStatusMessage loadJson(const QString &directory={})
Load JSON.
virtual void allowDragDrop(bool allowDrag, bool allowDrop, bool allowDropJsonFile=false)
Allow to drag and/or drop value objects.
virtual void clearHighlighting()
Clear any highlighted objects.
virtual bool isEmpty() const
Empty?
void updateContainerMaybeAsync(const ContainerType &container, bool sort=true, bool resize=true)
Based on size call sync / async update.
void insert(const ObjectType &value, bool resize=true)
Insert.
virtual ContainerType selectedObjects() const
Selected objects.
virtual swift::misc::CStatusMessage saveJson(bool selectedOnly=false, const QString &directory={})
Save JSON.
void setSortIndicator()
Set the search indicator based on model.
virtual int rowCount() const
Elements in container.
const ObjectType & at(const QModelIndex &index) const
Value object at.
virtual void clear()
Clear data.
typename T::ObjectType ObjectType
Model container element type.
virtual void sortByPropertyIndex(const swift::misc::CPropertyIndex &propertyIndex, Qt::SortOrder order=Qt::AscendingOrder)
Sort by index.
void takeFilterOwnership(std::unique_ptr< models::IModelFilter< ContainerType >> &filter)
Set filter and take ownership, any previously set filter will be destroyed.
virtual bool filterWidgetChangedFilter(bool enabled)
Filter changed in filter widget.
virtual void setObjectName(const QString &name)
Set own name and the model's name.
void push_back(const ObjectType &value, bool resize=true)
Push back.
int columnCount() const
Column count.
virtual swift::misc::CStatusMessage validateLoadedJsonData(const ContainerType &data) const
Verify JSON data loaded in swift::gui::views::CViewBaseNonTemplate::loadJson.
virtual bool reachedResizeThreshold(int containrerSize=-1) const
Skip resizing because of size?
QString toJsonString(QJsonDocument::JsonFormat format=QJsonDocument::Indented, bool selectedOnly=false) const
Convert to JSON string.
virtual swift::misc::CStatusMessage modifyLoadedJsonData(ContainerType &data) const
Modify JSON data loaded in swift::gui::views::CViewBaseNonTemplate::loadJson.
virtual void selectObjects(const ContainerType &selectedObjects)
Select.
virtual void onDoubleClicked(const QModelIndex &index)
Index double clicked.
void setPercentageColumnWidths()
Set the widths based on the column percentages.
virtual void displayContainerAsJsonPopup(bool selectedOnly)
Display the container as JSON popup.
virtual void performModeBasedResizeToContent()
Perform resizing (no presizing) / non slot method for template.
bool hasFilter() const
Has filter set?
virtual int removeSelectedRows()
Remove selected rows.
virtual bool isDropAllowed() const
Drop allowed?
swift::misc::CWorker * updateContainerAsync(const ContainerType &container, bool sort=true, bool resize=true)
Update whole container in background.
void resort()
Resort ("forced sorting")
virtual void onClicked(const QModelIndex &index)
Remove filter.
virtual void jsonLoadedAndModelUpdated(const ContainerType &data)
In swift::gui::views::CViewBaseNonTemplate::loadJson the view has been updated because of loaded JSON...
ObjectType selectedObject() const
Selected object (or default)
const ContainerType & container() const
Access to container.
typename T::ContainerType ContainerType
Model container type.
QJsonObject toJson(bool selectedOnly=false) const
Convert to JSON.
virtual void materializeFilter()
Materialize filter.
virtual void presizeOrFullResizeToContents()
Depending on CViewBaseNonTemplate::ResizeSubsetThreshold presize or fully resize.
const ContainerType & containerOrFilteredContainer(bool *filtered=nullptr) const
Full container or cached filtered container as approproiate.
virtual bool setSorting(const swift::misc::CPropertyIndex &propertyIndex, Qt::SortOrder order=Qt::AscendingOrder)
Sorting.
virtual void mouseOverCallback(const QModelIndex &index, bool mouseOver)
From delegate indicating we are in mouse over state.
virtual void drawDropIndicator(bool indicator)
Draw drop indicator.
virtual void copy()
Clipboard cut/copy/paste.
int updateSelected(const swift::misc::CVariant &variant, const swift::misc::CPropertyIndex &index)
Update selected objects.
virtual void updateSortIndicator()
Set the sort indicator to the current sort column.
void addContainerTypesAsDropTypes(bool objectType=true, bool containerType=true)
Add the object and container type as accepted drop types CDropBase::addAcceptedMetaTypeId.
virtual void customMenu(menus::CMenuActions &menuActions)
Method creating the menu.
virtual void dropEvent(QDropEvent *event)
ObjectType firstSelectedOrDefaultObject() const
First selected, the only one, or default.
int updateContainer(const ContainerType &container, bool sort=true, bool resize=true)
Update whole container.
virtual bool isOrderable() const
Is the corresponding model orderable, swift::misc::models::CListModelBaseNonTemplate::isOrderable.
virtual void paste()
Clipboard cut/copy/paste.
Non templated base class, allows Q_OBJECT and signals / slots to be used.
virtual void clearHighlighting()=0
Clear any highlighted objects.
void modelChanged()
Model bas been changed (means data in view have been changed)
void objectChanged(const swift::misc::CVariant &object, const swift::misc::CPropertyIndex &changedIndex)
Single object was changed in model.
void onModelChanged()
Underlying model changed.
virtual void dropEvent(QDropEvent *event)
void modelDataChangedDigest(int count, bool withFilter)
Model data changed,.
virtual void customMenu(menus::CMenuActions &menuActions)
Method creating the menu.
void asyncUpdateFinished()
Asynchronous update finished.
void modelDataChanged(int count, bool withFilter)
Model data changed.
void init()
Init default values.
Thrown when a convertFromJson method encounters an unrecoverable error in JSON data.
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Value object encapsulating a list of property indexes.
Specialized value object compliant map for variants, based on indexes.
CVariant value(const CPropertyIndex &index) const
Value.
bool isEmpty() const
Is empty?
CPropertyIndexList indexes() const
Indexes.
Streamable status message, e.g.
bool isFailure() const
Operation considered unsuccessful.
Wrapper around QVariant which provides transparent access to CValueObject methods of the contained ob...
T to() const
Synonym for value().
T value() const
Return the value converted to the type T.
void convertFromJson(const QJsonObject &json)
Assign from JSON object.
bool canConvert(int typeId) const
True if this variant can be converted to the type with the given metatype ID.
QString toJsonString(QJsonDocument::JsonFormat format=QJsonDocument::Indented) const
Convenience function JSON as string.
void then(T *context, F functor)
Connects to a functor or method which will be called when the task is finished.
Class for doing some arbitrary parcel of work in its own thread.
void thenWithResult(F functor)
Connects to a functor to which will be passed the result when the task is finished.
QJsonObject jsonObjectFromString(const QString &json, bool acceptCacheFormat)
JSON Object from string.
High level reusable GUI components.
Filter to search data sets.
Models to be used with views, mainly QTableView.
Views, mainly QTableView.
Free functions in swift::misc.