diff --git a/core/utilities/searchwindow/searchfolderview.cpp b/core/utilities/searchwindow/searchfolderview.cpp index 0bee905551..fc8139eb7e 100644 --- a/core/utilities/searchwindow/searchfolderview.cpp +++ b/core/utilities/searchwindow/searchfolderview.cpp @@ -1,108 +1,108 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-21 * Description : Searches folder view * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2008-2012 by Marcel Wiesweg * Copyright (C) 2006-2020 by Gilles Caulier * Copyright (C) 2009 by Andi Clemens * Copyright (C) 2009 by Johannes Wienke * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #include "searchfolderview.h" // Qt includes #include #include // KDE includes #include // Local includes #include "contextmenuhelper.h" namespace Digikam { class Q_DECL_HIDDEN NormalSearchTreeView::Private { public: explicit Private() : newAction(nullptr), editAction(nullptr) { } QAction* newAction; QAction* editAction; }; NormalSearchTreeView::NormalSearchTreeView(QWidget* const parent, SearchModel* const searchModel, SearchModificationHelper* const searchModificationHelper) : EditableSearchTreeView(parent, searchModel, searchModificationHelper), d(new Private) { d->newAction = new QAction(QIcon::fromTheme(QLatin1String("document-new")), i18nc("Create new search", "New..."), this); d->editAction = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18nc("Edit selected search", "Edit..."), this); } NormalSearchTreeView::~NormalSearchTreeView() { delete d; } void NormalSearchTreeView::addCustomContextMenuActions(ContextMenuHelper& cmh, Album* album) { cmh.addAction(d->newAction); cmh.addSeparator(); EditableSearchTreeView::addCustomContextMenuActions(cmh, album); SAlbum* const salbum = dynamic_cast(album); d->editAction->setEnabled(salbum); cmh.addAction(d->editAction); } void NormalSearchTreeView::handleCustomContextMenuAction(QAction* action, AlbumPointer album) { Album* a = album; SAlbum* const salbum = dynamic_cast(a); - if (action == d->newAction && salbum) + if ((action == d->newAction) && salbum) { emit newSearch(); } - else if (action == d->editAction && salbum) + else if ((action == d->editAction) && salbum) { emit editSearch(salbum); } else { EditableSearchTreeView::handleCustomContextMenuAction(action, album); } } } // namespace Digikam diff --git a/core/utilities/searchwindow/searchfolderview.h b/core/utilities/searchwindow/searchfolderview.h index 390ce925a1..0a7690fd76 100644 --- a/core/utilities/searchwindow/searchfolderview.h +++ b/core/utilities/searchwindow/searchfolderview.h @@ -1,92 +1,92 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-21 * Description : Searches folder view * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2008-2012 by Marcel Wiesweg * Copyright (C) 2006-2020 by Gilles Caulier * Copyright (C) 2009 by Johannes Wienke * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_SEARCH_FOLDER_VIEW_H #define DIGIKAM_SEARCH_FOLDER_VIEW_H // Local includes #include "editablesearchtreeview.h" namespace Digikam { /** * Tree view for all saved "normal" searches. Allows editing and creating * searches in the context menu. * * @author jwienke */ class NormalSearchTreeView : public EditableSearchTreeView { Q_OBJECT public: /** * Constructor. * * @param parent qt parent * @param searchModel the model this view should act on * @param searchModificationHelper the modification helper object used to * perform operations on the displayed * searches */ NormalSearchTreeView(QWidget* const parent, SearchModel* const searchModel, SearchModificationHelper* const searchModificationHelper); /** * Destructor. */ virtual ~NormalSearchTreeView(); Q_SIGNALS: /** * Emitted of a new search shall be created. */ void newSearch(); /** * Emitted if the given search shall be edited. * * @param album search to edit */ void editSearch(SAlbum* album); protected: - virtual void addCustomContextMenuActions(ContextMenuHelper& cmh, Album* album) override; - virtual void handleCustomContextMenuAction(QAction* action, AlbumPointer album) override; + virtual void addCustomContextMenuActions(ContextMenuHelper& cmh, Album* album) override; + virtual void handleCustomContextMenuAction(QAction* action, AlbumPointer album) override; private: class Private; Private* d; }; } // namespace Digikam #endif // DIGIKAM_SEARCH_FOLDER_VIEW_H diff --git a/core/utilities/searchwindow/searchgroup.cpp b/core/utilities/searchwindow/searchgroup.cpp index c8f7663fa0..40118def05 100644 --- a/core/utilities/searchwindow/searchgroup.cpp +++ b/core/utilities/searchwindow/searchgroup.cpp @@ -1,761 +1,787 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-01-20 * Description : User interface for searches * * Copyright (C) 2008-2012 by Marcel Wiesweg * Copyright (C) 2011-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #include "searchgroup.h" // Qt includes #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "searchfieldgroup.h" #include "searchfields.h" #include "searchutilities.h" #include "searchview.h" namespace Digikam { SearchGroup::SearchGroup(SearchView* const parent) : AbstractSearchGroupContainer(parent), m_view(parent), m_layout(nullptr), m_label(nullptr), m_subgroupLayout(nullptr), m_groupType(FirstGroup) { } void SearchGroup::setup(Type type) { m_groupType = type; m_layout = new QVBoxLayout; m_layout->setContentsMargins(QMargins()); m_layout->setSpacing(0); m_label = new SearchGroupLabel(m_view, m_groupType, this); m_layout->addWidget(m_label); connect(m_label, SIGNAL(removeClicked()), this, SIGNAL(removeRequested())); SearchFieldGroup* group = nullptr; SearchFieldGroupLabel* label = nullptr; + // To prevent Cppcheck warnings. (void)group; (void)label; // ----- // group = new SearchFieldGroup(this); group->addField(SearchField::createField(QLatin1String("keyword"), group)); m_fieldGroups << group; m_layout->addWidget(group); // this group has no label. Need to show, else it is hidden forever + group->setFieldsVisible(true); // ----- // label = new SearchFieldGroupLabel(this); label->setTitle(i18n("File, Album, Tags")); group = new SearchFieldGroup(this); group->setLabel(label); group->addField(SearchField::createField(QLatin1String("albumid"), group)); group->addField(SearchField::createField(QLatin1String("albumname"), group)); group->addField(SearchField::createField(QLatin1String("albumcollection"), group)); group->addField(SearchField::createField(QLatin1String("tagid"), group)); group->addField(SearchField::createField(QLatin1String("tagname"), group)); group->addField(SearchField::createField(QLatin1String("nottagged"), group)); group->addField(SearchField::createField(QLatin1String("filename"), group)); m_fieldLabels << label; m_fieldGroups << group; m_layout->addWidget(label); m_layout->addWidget(group); // ----- // label = new SearchFieldGroupLabel(this); label->setTitle(i18n("Picture Properties")); group = new SearchFieldGroup(this); group->setLabel(label); group->addField(SearchField::createField(QLatin1String("creationdate"), group)); group->addField(SearchField::createField(QLatin1String("rating"), group)); group->addField(SearchField::createField(QLatin1String("labels"), group)); group->addField(SearchField::createField(QLatin1String("dimension"), group)); group->addField(SearchField::createField(QLatin1String("pageorientation"), group)); group->addField(SearchField::createField(QLatin1String("width"), group)); group->addField(SearchField::createField(QLatin1String("height"), group)); group->addField(SearchField::createField(QLatin1String("aspectratioimg"), group)); group->addField(SearchField::createField(QLatin1String("pixelsize"), group)); group->addField(SearchField::createField(QLatin1String("format"), group)); group->addField(SearchField::createField(QLatin1String("colordepth"), group)); group->addField(SearchField::createField(QLatin1String("colormodel"), group)); group->addField(SearchField::createField(QLatin1String("modificationdate"), group)); group->addField(SearchField::createField(QLatin1String("digitizationdate"), group)); group->addField(SearchField::createField(QLatin1String("filesize"), group)); m_fieldLabels << label; m_fieldGroups << group; m_layout->addWidget(label); m_layout->addWidget(group); // ----- // label = new SearchFieldGroupLabel(this); label->setTitle(i18n("Audio/Video Properties")); group = new SearchFieldGroup(this); group->setLabel(label); group->addField(SearchField::createField(QLatin1String("videoaspectratio"), group)); group->addField(SearchField::createField(QLatin1String("videoduration"), group)); group->addField(SearchField::createField(QLatin1String("videoframerate"), group)); group->addField(SearchField::createField(QLatin1String("videocodec"), group)); group->addField(SearchField::createField(QLatin1String("videoaudiobitrate"), group)); group->addField(SearchField::createField(QLatin1String("videoaudiochanneltype"), group)); group->addField(SearchField::createField(QLatin1String("videoaudioCodec"), group)); m_fieldLabels << label; m_fieldGroups << group; m_layout->addWidget(label); m_layout->addWidget(group); // ----- // label = new SearchFieldGroupLabel(this); label->setTitle(i18n("Caption, Comment, Title")); group = new SearchFieldGroup(this); group->setLabel(label); group->addField(SearchField::createField(QLatin1String("creator"), group)); group->addField(SearchField::createField(QLatin1String("comment"), group)); group->addField(SearchField::createField(QLatin1String("commentauthor"), group)); group->addField(SearchField::createField(QLatin1String("headline"), group)); group->addField(SearchField::createField(QLatin1String("title"), group)); m_fieldLabels << label; m_fieldGroups << group; m_layout->addWidget(label); m_layout->addWidget(group); // ----- // label = new SearchFieldGroupLabel(this); label->setTitle(i18n("Photograph Information")); group = new SearchFieldGroup(this); group->setLabel(label); group->addField(SearchField::createField(QLatin1String("make"), group)); group->addField(SearchField::createField(QLatin1String("model"), group)); group->addField(SearchField::createField(QLatin1String("lenses"), group)); group->addField(SearchField::createField(QLatin1String("aperture"), group)); group->addField(SearchField::createField(QLatin1String("focallength"), group)); group->addField(SearchField::createField(QLatin1String("focallength35"), group)); group->addField(SearchField::createField(QLatin1String("exposuretime"), group)); group->addField(SearchField::createField(QLatin1String("exposureprogram"), group)); group->addField(SearchField::createField(QLatin1String("exposuremode"), group)); group->addField(SearchField::createField(QLatin1String("sensitivity"), group)); group->addField(SearchField::createField(QLatin1String("orientation"), group)); group->addField(SearchField::createField(QLatin1String("flashmode"), group)); group->addField(SearchField::createField(QLatin1String("whitebalance"), group)); group->addField(SearchField::createField(QLatin1String("whitebalancecolortemperature"), group)); group->addField(SearchField::createField(QLatin1String("meteringmode"), group)); group->addField(SearchField::createField(QLatin1String("subjectdistance"), group)); group->addField(SearchField::createField(QLatin1String("subjectdistancecategory"), group)); m_fieldLabels << label; m_fieldGroups << group; m_layout->addWidget(label); m_layout->addWidget(group); // ----- // label = new SearchFieldGroupLabel(this); label->setTitle(i18n("Geographic position")); group = new SearchFieldGroup(this); group->setLabel(label); - //group->addField(SearchField::createField("latitude", group)); - //group->addField(SearchField::createField("longitude", group)); +/* + group->addField(SearchField::createField("latitude", group)); + group->addField(SearchField::createField("longitude", group)); +*/ group->addField(SearchField::createField(QLatin1String("altitude"), group)); group->addField(SearchField::createField(QLatin1String("nogps"), group)); m_fieldLabels << label; m_fieldGroups << group; m_layout->addWidget(label); m_layout->addWidget(group); // ----- // // prepare subgroup layout + QHBoxLayout* const indentLayout = new QHBoxLayout; indentLayout->setContentsMargins(QMargins()); indentLayout->setSpacing(0); QStyleOption option; option.initFrom(this); int indent = 5 * style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &option, this); indent = qMax(indent, 20); indentLayout->addSpacing(indent); m_subgroupLayout = new QVBoxLayout; m_subgroupLayout->setContentsMargins(QMargins()); m_subgroupLayout->setSpacing(0); indentLayout->addLayout(m_subgroupLayout); m_layout->addLayout(indentLayout); // ----- // m_layout->addStretch(1); setLayout(m_layout); // ----- // // initialization as empty group + reset(); } void SearchGroup::read(SearchXmlCachingReader& reader) { reset(); m_label->setGroupOperator(reader.groupOperator()); m_label->setDefaultFieldOperator(reader.defaultFieldOperator()); startReadingGroups(reader); while (!reader.atEnd()) { reader.readNext(); if (reader.isEndElement()) { break; } // subgroup + if (reader.isGroupElement()) { readGroup(reader); } if (reader.isFieldElement()) { QString name = reader.fieldName(); - SearchField* field = nullptr; + SearchField* field = nullptr; SearchFieldGroup* fieldGroup = nullptr; + foreach (fieldGroup, m_fieldGroups) { if ((field = fieldGroup->fieldForName(name))) { break; } } if (field) { field->read(reader); fieldGroup->markField(field); fieldGroup->setFieldsVisible(true); } else { qCWarning(DIGIKAM_GENERAL_LOG) << "Unhandled search field in XML with field name" << name; reader.readToEndOfElement(); } } } finishReadingGroups(); } SearchGroup* SearchGroup::createSearchGroup() { // create a sub group - view is the same + SearchGroup* const group = new SearchGroup(m_view); group->setup(SearchGroup::ChainGroup); + return group; } void SearchGroup::addGroupToLayout(SearchGroup* group) { // insert in front of the stretch + m_subgroupLayout->addWidget(group); } void SearchGroup::write(SearchXmlWriter& writer) { writer.writeGroup(); writer.setGroupOperator(m_label->groupOperator()); writer.setDefaultFieldOperator(m_label->defaultFieldOperator()); - foreach (SearchFieldGroup* fieldGroup, m_fieldGroups) + foreach (SearchFieldGroup* const fieldGroup, m_fieldGroups) { fieldGroup->write(writer); } // take care for subgroups + writeGroups(writer); writer.finishGroup(); } void SearchGroup::reset() { - foreach (SearchFieldGroup* fieldGroup, m_fieldGroups) + foreach (SearchFieldGroup* const fieldGroup, m_fieldGroups) { fieldGroup->reset(); } m_label->setGroupOperator(SearchXml::standardGroupOperator()); m_label->setDefaultFieldOperator(SearchXml::standardFieldOperator()); } SearchGroup::Type SearchGroup::groupType() const { return m_groupType; } QList SearchGroup::startupAnimationArea() const { QList rects; // from subgroups; + rects += startupAnimationAreaOfGroups(); // field groups + foreach (SearchFieldGroup* fieldGroup, m_fieldGroups) { rects += fieldGroup->areaOfMarkedFields(); } // adjust position relative to parent - for (QList::iterator it = rects.begin(); it != rects.end(); ++it) + + for (QList::iterator it = rects.begin() ; it != rects.end() ; ++it) { (*it).translate(pos()); } return rects; } // ------------------------------------------------------------------------- class Q_DECL_HIDDEN RadioButtonHBox : public QHBoxLayout { public: RadioButtonHBox(QWidget* const left, QWidget* const right, Qt::LayoutDirection dir) : QHBoxLayout() { if (dir == Qt::RightToLeft) { addWidget(right, Qt::AlignRight); addWidget(left); } else { addWidget(left); addWidget(right, Qt::AlignLeft); } setSpacing(0); } }; class Q_DECL_HIDDEN SearchGroupLabel::Private { public: explicit Private() : extended(false), groupOp(SearchXml::And), fieldOp(SearchXml::And), layout(nullptr), groupOpLabel(nullptr), allBox(nullptr), anyBox(nullptr), noneBox(nullptr), oneNotBox(nullptr), optionsLabel(nullptr), removeLabel(nullptr), stackedLayout(nullptr), themeCache(nullptr) { } bool extended; SearchXml::Operator groupOp; SearchXml::Operator fieldOp; QGridLayout* layout; - //QComboBox* groupOpBox; +/* + QComboBox* groupOpBox; +*/ DClickLabel* groupOpLabel; QRadioButton* allBox; QRadioButton* anyBox; QRadioButton* noneBox; QRadioButton* oneNotBox; DClickLabel* optionsLabel; DClickLabel* removeLabel; QStackedLayout* stackedLayout; SearchViewThemedPartsCache* themeCache; }; SearchGroupLabel::SearchGroupLabel(SearchViewThemedPartsCache* const cache, SearchGroup::Type type, QWidget* const parent) : QWidget(parent), d(new Private) { d->themeCache = cache; d->layout = new QGridLayout; // leave styling to style sheet (by object name) QLabel* const mainLabel = new QLabel(i18n("Find Items")); mainLabel->setObjectName(QLatin1String("SearchGroupLabel_MainLabel")); // Use radio button with a separate label to fix styling problem, see bug 195809 + d->allBox = new QRadioButton; QLabel* const allBoxLabel = new QLabel(i18n("Meet All of the following conditions")); allBoxLabel->setObjectName(QLatin1String("SearchGroupLabel_CheckBox")); - d->anyBox = new QRadioButton; + d->anyBox = new QRadioButton; QLabel* const anyBoxLabel = new QLabel(i18n("Meet Any of the following conditions")); anyBoxLabel->setObjectName(QLatin1String("SearchGroupLabel_CheckBox")); d->noneBox = new QRadioButton; QLabel* const noneBoxLabel = new QLabel(i18n("None of these conditions are met")); noneBoxLabel->setObjectName(QLatin1String("SearchGroupLabel_CheckBox")); d->oneNotBox = new QRadioButton; QLabel* const oneNotBoxLabel = new QLabel(i18n("At least one of these conditions is not met")); oneNotBoxLabel->setObjectName(QLatin1String("SearchGroupLabel_CheckBox")); connect(d->allBox, SIGNAL(toggled(bool)), this, SLOT(boxesToggled())); connect(d->anyBox, SIGNAL(toggled(bool)), this, SLOT(boxesToggled())); connect(d->noneBox, SIGNAL(toggled(bool)), this, SLOT(boxesToggled())); connect(d->oneNotBox, SIGNAL(toggled(bool)), this, SLOT(boxesToggled())); if (type == SearchGroup::FirstGroup) { QLabel* const logo = new QLabel; logo->setPixmap(QIcon::fromTheme(QLatin1String("digikam")).pixmap(QSize(48,48))); d->optionsLabel = new DClickLabel; d->optionsLabel->setObjectName(QLatin1String("SearchGroupLabel_OptionsLabel")); connect(d->optionsLabel, SIGNAL(activated()), this, SLOT(toggleShowOptions())); QWidget* const simpleHeader = new QWidget; QVBoxLayout* const headerLayout = new QVBoxLayout; QLabel* const simpleLabel1 = new QLabel; - //simpleLabel->setText(i18n("Find Pictures meeting all of these conditions")); - //simpleLabel->setPixmap(QIcon::fromTheme(QLatin1String("edit-find")).pixmap(128)); +/* + simpleLabel->setText(i18n("Find Pictures meeting all of these conditions")); + simpleLabel->setPixmap(QIcon::fromTheme(QLatin1String("edit-find")).pixmap(128)); +*/ simpleLabel1->setText(i18n("

Search your collection
for Items meeting the following conditions

")); simpleLabel1->setObjectName(QLatin1String("SearchGroupLabel_SimpleLabel")); headerLayout->addStretch(3); headerLayout->addWidget(simpleLabel1); headerLayout->addStretch(1); headerLayout->setContentsMargins(QMargins()); simpleHeader->setLayout(headerLayout); QWidget* const optionsBox = new QWidget; QGridLayout* const optionsLayout = new QGridLayout; optionsLayout->addLayout(new RadioButtonHBox(d->allBox, allBoxLabel, layoutDirection()), 0, 0); optionsLayout->addLayout(new RadioButtonHBox(d->anyBox, anyBoxLabel, layoutDirection()), 1, 0); optionsLayout->addLayout(new RadioButtonHBox(d->noneBox, noneBoxLabel, layoutDirection()), 0, 1); optionsLayout->addLayout(new RadioButtonHBox(d->oneNotBox, oneNotBoxLabel, layoutDirection()), 1, 1); optionsLayout->setContentsMargins(QMargins()); optionsBox->setLayout(optionsLayout); d->stackedLayout = new QStackedLayout; d->stackedLayout->addWidget(simpleHeader); d->stackedLayout->addWidget(optionsBox); d->stackedLayout->setContentsMargins(QMargins()); d->layout->addWidget(mainLabel, 0, 0, 1, 1); d->layout->addLayout(d->stackedLayout, 1, 0, 1, 1); d->layout->addWidget(d->optionsLabel, 1, 1, 1, 1, Qt::AlignRight | Qt::AlignBottom); d->layout->addWidget(logo, 0, 2, 2, 1, Qt::AlignTop); d->layout->setColumnStretch(1, 10); setExtended(false); } else { d->groupOpLabel = new DClickLabel; d->groupOpLabel->setObjectName(QLatin1String("SearchGroupLabel_GroupOpLabel")); connect(d->groupOpLabel, SIGNAL(activated()), this, SLOT(toggleGroupOperator())); d->removeLabel = new DClickLabel(i18n("Remove Group")); d->removeLabel->setObjectName(QLatin1String("SearchGroupLabel_RemoveLabel")); connect(d->removeLabel, SIGNAL(activated()), this, SIGNAL(removeClicked())); d->layout->addWidget(d->groupOpLabel, 0, 0, 1, 1); d->layout->addLayout(new RadioButtonHBox(d->allBox, allBoxLabel, layoutDirection()), 1, 0, 1, 1); d->layout->addLayout(new RadioButtonHBox(d->anyBox, anyBoxLabel, layoutDirection()), 2, 0, 1, 1); d->layout->addLayout(new RadioButtonHBox(d->noneBox, noneBoxLabel, layoutDirection()), 3, 0, 1, 1); d->layout->addLayout(new RadioButtonHBox(d->oneNotBox, oneNotBoxLabel, layoutDirection()), 4, 0, 1, 1); d->layout->addWidget(d->removeLabel, 0, 2, 1, 1); d->layout->setColumnStretch(1, 10); } setLayout(d->layout); // Default values + setGroupOperator(SearchXml::standardGroupOperator()); setDefaultFieldOperator(SearchXml::standardFieldOperator()); } SearchGroupLabel::~SearchGroupLabel() { delete d; } void SearchGroupLabel::setExtended(bool extended) { d->extended = extended; if (!d->stackedLayout) { return; } if (d->extended) { d->stackedLayout->setCurrentIndex(1); d->allBox->setVisible(true); d->anyBox->setVisible(true); d->noneBox->setVisible(true); d->oneNotBox->setVisible(true); d->optionsLabel->setText(i18n("Hide Options <<")); } else { d->stackedLayout->setCurrentIndex(0); + // hide to reduce reserved space in stacked layout + d->allBox->setVisible(false); d->anyBox->setVisible(false); d->noneBox->setVisible(false); d->oneNotBox->setVisible(false); d->optionsLabel->setText(i18n("Options >>")); } } void SearchGroupLabel::toggleShowOptions() { setExtended(!d->extended); } void SearchGroupLabel::toggleGroupOperator() { - if (d->groupOp == SearchXml::And) + if (d->groupOp == SearchXml::And) { d->groupOp = SearchXml::Or; } else if (d->groupOp == SearchXml::Or) { d->groupOp = SearchXml::And; } else if (d->groupOp == SearchXml::AndNot) { d->groupOp = SearchXml::OrNot; } else if (d->groupOp == SearchXml::OrNot) { d->groupOp = SearchXml::AndNot; } updateGroupLabel(); } void SearchGroupLabel::boxesToggled() { // set field op + if (d->allBox->isChecked() || d->oneNotBox->isChecked()) { d->fieldOp = SearchXml::And; } else { d->fieldOp = SearchXml::Or; } // negate group op + if (d->allBox->isChecked() || d->anyBox->isChecked()) { - if (d->groupOp == SearchXml::AndNot) + if (d->groupOp == SearchXml::AndNot) { d->groupOp = SearchXml::And; } else if (d->groupOp == SearchXml::OrNot) { d->groupOp = SearchXml::Or; } } else { - if (d->groupOp == SearchXml::And) + if (d->groupOp == SearchXml::And) { d->groupOp = SearchXml::AndNot; } else if (d->groupOp == SearchXml::Or) { d->groupOp = SearchXml::OrNot; } } } void SearchGroupLabel::setGroupOperator(SearchXml::Operator op) { d->groupOp = op; adjustOperatorOptions(); updateGroupLabel(); } void SearchGroupLabel::updateGroupLabel() { if (d->groupOpLabel) { - if (d->groupOp == SearchXml::And || d->groupOp == SearchXml::AndNot) + if ((d->groupOp == SearchXml::And) || (d->groupOp == SearchXml::AndNot)) { d->groupOpLabel->setText(i18n("AND")); } else { d->groupOpLabel->setText(i18n("OR")); } } } void SearchGroupLabel::setDefaultFieldOperator(SearchXml::Operator op) { d->fieldOp = op; adjustOperatorOptions(); } void SearchGroupLabel::adjustOperatorOptions() { // In the UI, the NOT is done at the level of the field operator, // but in fact we put a NOT in front of the whole group, so it is the group operator! // 1. allBox, All of these conditions are met: (A && B && C), Group And/Or, Field And // 2. anyBox, Any of these conditions are met: (A || B || C), Group And/Or, Field Or // 3. oneNotBox, At least one of these conditions is not met: !(A && B && C) = (!A || !B || !C), // Group AndNot/OrNot, Field And // 4. noneBox, None of these conditions are met: !(A || B || C) = (!A && !B && !C), // Group AndNot/OrNot, Field Or switch (d->groupOp) { case SearchXml::And: case SearchXml::Or: if (d->fieldOp == SearchXml::And) { d->allBox->setChecked(true); } else { d->anyBox->setChecked(true); } break; case SearchXml::AndNot: case SearchXml::OrNot: if (d->fieldOp == SearchXml::And) { d->oneNotBox->setChecked(true); } else { d->noneBox->setChecked(true); } break; } if (!d->allBox->isChecked()) { setExtended(true); } } SearchXml::Operator SearchGroupLabel::groupOperator() const { return d->groupOp; } SearchXml::Operator SearchGroupLabel::defaultFieldOperator() const { if (d->anyBox->isChecked() || d->noneBox->isChecked()) { return SearchXml::Or; } else { return SearchXml::And; } } void SearchGroupLabel::paintEvent(QPaintEvent*) { // paint themed background + QPainter p(this); p.drawPixmap(0, 0, d->themeCache->groupLabelPixmap(width(), height())); } } // namespace Digikam diff --git a/core/utilities/searchwindow/searchmodificationhelper.cpp b/core/utilities/searchwindow/searchmodificationhelper.cpp index 4f2afe822d..e2fa8e3ed6 100644 --- a/core/utilities/searchwindow/searchmodificationhelper.cpp +++ b/core/utilities/searchwindow/searchmodificationhelper.cpp @@ -1,406 +1,414 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2000-12-05 * Description : helper class used to modify search albums in views * * Copyright (C) 2009-2010 by Johannes Wienke * Copyright (C) 2011-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #include "searchmodificationhelper.h" // Qt includes #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "album.h" #include "albummanager.h" #include "similaritydbaccess.h" #include "similaritydb.h" #include "haariface.h" #include "iteminfo.h" #include "coredbsearchxml.h" #include "sketchwidget.h" namespace Digikam { class Q_DECL_HIDDEN SearchModificationHelper::Private { public: explicit Private() : dialogParent(nullptr) { } QWidget* dialogParent; }; SearchModificationHelper::SearchModificationHelper(QObject* const parent, QWidget* const dialogParent) : QObject(parent), d(new Private) { d->dialogParent = dialogParent; } SearchModificationHelper::~SearchModificationHelper() { delete d; } void SearchModificationHelper::slotSearchDelete(SAlbum* searchAlbum) { if (!searchAlbum) { return; } // Make sure that a complicated search is not deleted accidentally + int result = QMessageBox::warning(d->dialogParent, i18n("Delete Search?"), i18n("Are you sure you want to " "delete the selected search " "\"%1\"?", searchAlbum->title()), QMessageBox::Yes | QMessageBox::Cancel); if (result != QMessageBox::Yes) { return; } AlbumManager::instance()->deleteSAlbum(searchAlbum); } bool SearchModificationHelper::checkAlbum(const QString& name) const { const AlbumList list = AlbumManager::instance()->allSAlbums(); for (AlbumList::ConstIterator it = list.constBegin() ; it != list.constEnd() ; ++it) { SAlbum* const album = (SAlbum*)(*it); if (album->title() == name) { return false; } } return true; } bool SearchModificationHelper::checkName(QString& name) { bool checked = checkAlbum(name); while (!checked) { QString label = i18n( "Search name already exists.\n" "Please enter a new name:" ); bool ok; QString newTitle = QInputDialog::getText(d->dialogParent, i18n("Name exists"), label, QLineEdit::Normal, name, &ok); if (!ok) { return false; } name = newTitle; checked = checkAlbum(name); } return true; } void SearchModificationHelper::slotSearchRename(SAlbum* searchAlbum) { if (!searchAlbum) { return; } QString oldName(searchAlbum->title()); bool ok; QString name = QInputDialog::getText(d->dialogParent, i18n("Rename Album (%1)", oldName), i18n("Enter new album name:"), QLineEdit::Normal, oldName, &ok); - if (!ok || name == oldName || name.isEmpty()) + if (!ok || (name == oldName) || (name.isEmpty())) { return; } if (!checkName(name)) { return; } AlbumManager::instance()->updateSAlbum(searchAlbum, searchAlbum->query(), name); } SAlbum* SearchModificationHelper::slotCreateTimeLineSearch(const QString& desiredName, const DateRangeList& dateRanges, bool overwriteIfExisting) { QString name = desiredName; if (!overwriteIfExisting) { if (!checkName(name)) { return nullptr; } } if (dateRanges.isEmpty()) { AlbumManager::instance()->clearCurrentAlbums(); return nullptr; } // Create an XML search query for the list of date ranges + SearchXmlWriter writer; // for each range, write a group with two fields - for (int i = 0; i < dateRanges.size(); ++i) + + for (int i = 0 ; i < dateRanges.size() ; ++i) { writer.writeGroup(); writer.writeField(QLatin1String("creationdate"), SearchXml::GreaterThanOrEqual); writer.writeValue(dateRanges.at(i).first); writer.finishField(); writer.writeField(QLatin1String("creationdate"), SearchXml::LessThan); writer.writeValue(dateRanges.at(i).second); writer.finishField(); writer.finishGroup(); } writer.finish(); qCDebug(DIGIKAM_GENERAL_LOG) << "Date search XML:\n" << writer.xml(); SAlbum* const album = AlbumManager::instance()->createSAlbum(name, DatabaseSearch::TimeLineSearch, writer.xml()); AlbumManager::instance()->setCurrentAlbums(QList() << album); + return album; } SAlbum* SearchModificationHelper::createFuzzySearchFromSketch(const QString& proposedName, SketchWidget* sketchWidget, unsigned int numberOfResults, QList& targetAlbums, bool overwriteIfExisting) { if (sketchWidget->isClear()) { return nullptr; } QString name = proposedName; if (!overwriteIfExisting) { if (!checkName(name)) { return nullptr; } } // We query database here HaarIface haarIface; SearchXmlWriter writer; writer.writeGroup(); writer.writeField(QLatin1String("similarity"), SearchXml::Like); writer.writeAttribute(QLatin1String("type"), QLatin1String("signature")); // we pass a signature writer.writeAttribute(QLatin1String("numberofresults"), QString::number(numberOfResults)); writer.writeAttribute(QLatin1String("sketchtype"), QLatin1String("handdrawn")); writer.writeValue(haarIface.signatureAsText(sketchWidget->sketchImage())); sketchWidget->sketchImageToXML(writer); writer.finishField(); // Add the target albums, i.e. define that the found similar images // must be located in one of the target albums. + writer.writeField(QLatin1String("noeffect_targetAlbums"), SearchXml::OneOf); writer.writeValue(targetAlbums); writer.finishField(); writer.finishGroup(); writer.finish(); SAlbum* const salbum = AlbumManager::instance()->createSAlbum(name, DatabaseSearch::HaarSearch, writer.xml()); AlbumManager::instance()->setCurrentAlbums(QList() << salbum); return salbum; } void SearchModificationHelper::slotCreateFuzzySearchFromSketch(const QString& proposedName, SketchWidget* sketchWidget, unsigned int numberOfResults, QList& targetAlbums, bool overwriteIfExisting) { createFuzzySearchFromSketch(proposedName, sketchWidget, numberOfResults, targetAlbums, overwriteIfExisting); } SAlbum* SearchModificationHelper::createFuzzySearchFromDropped(const QString& proposedName, const QString& filePath, float threshold, float maxThreshold, QList& targetAlbums, bool overwriteIfExisting) { QString name = proposedName; if (!overwriteIfExisting) { if (!checkName(name)) { return nullptr; } } // We query database here HaarIface haarIface; SearchXmlWriter writer; writer.writeGroup(); writer.writeField(QLatin1String("similarity"), SearchXml::Like); writer.writeAttribute(QLatin1String("type"), QLatin1String("image")); // we pass an image path writer.writeAttribute(QLatin1String("threshold"), QString::number(threshold)); writer.writeAttribute(QLatin1String("maxthreshold"), QString::number(maxThreshold)); writer.writeAttribute(QLatin1String("sketchtype"), QLatin1String("scanned")); writer.writeValue(filePath); writer.finishField(); // Add the target albums, i.e. define that the found similar images // must be located in one of the target albums. + writer.writeField(QLatin1String("noeffect_targetAlbums"), SearchXml::OneOf); writer.writeValue(targetAlbums); writer.finishField(); writer.finishGroup(); writer.finish(); SAlbum* const salbum = AlbumManager::instance()->createSAlbum(name, DatabaseSearch::HaarSearch, writer.xml()); AlbumManager::instance()->setCurrentAlbums(QList() << salbum); return salbum; } void SearchModificationHelper::slotCreateFuzzySearchFromDropped(const QString& proposedName, const QString& filePath, float threshold, float maxThreshold, QList& targetAlbums, bool overwriteIfExisting) { createFuzzySearchFromDropped(proposedName, filePath, threshold, maxThreshold, targetAlbums, overwriteIfExisting); } SAlbum* SearchModificationHelper::createFuzzySearchFromImage(const QString& proposedName, const ItemInfo& image, float threshold, float maxThreshold, QList& targetAlbums, bool overwriteIfExisting) { if (image.isNull()) { return nullptr; } QString name = proposedName; if (!overwriteIfExisting) { if (!checkName(name)) { return nullptr; } } // If the image has no fingerprint, generate it. + if (SimilarityDbAccess().db()->hasDirtyOrMissingFingerprint(image)) { QString path = image.filePath(); if (!path.isEmpty()) { qCDebug(DIGIKAM_GENERAL_LOG) << "(Re-)generating fingerprint for file:" << path; HaarIface haarIface; haarIface.indexImage(path); } } // We query database here SearchXmlWriter writer; writer.writeGroup(); writer.writeField(QLatin1String("similarity"), SearchXml::Like); writer.writeAttribute(QLatin1String("type"), QLatin1String("imageid")); writer.writeAttribute(QLatin1String("threshold"), QString::number(threshold)); writer.writeAttribute(QLatin1String("maxthreshold"), QString::number(maxThreshold)); writer.writeAttribute(QLatin1String("sketchtype"), QLatin1String("scanned")); writer.writeValue(image.id()); writer.finishField(); // Add the target albums, i.e. define that the found similar images // must be located in one of the target albums. + writer.writeField(QLatin1String("noeffect_targetAlbums"), SearchXml::OneOf); writer.writeValue(targetAlbums); writer.finishField(); writer.finishGroup(); writer.finish(); SAlbum* const salbum = AlbumManager::instance()->createSAlbum(name, DatabaseSearch::HaarSearch, writer.xml()); AlbumManager::instance()->setCurrentAlbums(QList() << salbum); return salbum; } void SearchModificationHelper::slotCreateFuzzySearchFromImage(const QString& proposedName, const ItemInfo& image, float threshold, float maxThreshold, QList& targetAlbums, bool overwriteIfExisting) { createFuzzySearchFromImage(proposedName, image, threshold, maxThreshold, targetAlbums, overwriteIfExisting); } } // namespace Digikam diff --git a/core/utilities/searchwindow/searchtabheader.cpp b/core/utilities/searchwindow/searchtabheader.cpp index 95d6ae036e..720bb586ec 100644 --- a/core/utilities/searchwindow/searchtabheader.cpp +++ b/core/utilities/searchwindow/searchtabheader.cpp @@ -1,660 +1,672 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-02-26 * Description : Upper widget in the search sidebar * * Copyright (C) 2008-2012 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #include "searchtabheader.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include // Local includes #include "digikam_debug.h" #include "album.h" #include "albummanager.h" #include "searchfolderview.h" #include "searchwindow.h" #include "coredbsearchxml.h" #include "dexpanderbox.h" namespace Digikam { class Q_DECL_HIDDEN KeywordLineEdit : public QLineEdit { public: explicit KeywordLineEdit(QWidget* const parent = nullptr) - : QLineEdit(parent) + : QLineEdit(parent), + m_hasAdvanced(false) { - m_hasAdvanced = false; - KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(QLatin1String("KeywordSearchEdit Settings")); m_autoSearch = group.readEntry(QLatin1String("Autostart Search"), true); } void showAdvancedSearch(bool hasAdvanced) { if (m_hasAdvanced == hasAdvanced) { return; } m_hasAdvanced = hasAdvanced; adjustStatus(m_hasAdvanced); } void focusInEvent(QFocusEvent* e) { if (m_hasAdvanced) { adjustStatus(false); } QLineEdit::focusInEvent(e); } void focusOutEvent(QFocusEvent* e) { QLineEdit::focusOutEvent(e); if (m_hasAdvanced) { adjustStatus(true); } } void contextMenuEvent(QContextMenuEvent* e) { QAction* const action = new QAction(i18nc("@action:inmenu", "Autostart Search"), this); action->setCheckable(true); action->setChecked(m_autoSearch); connect(action, &QAction::triggered, this, &KeywordLineEdit::toggleAutoSearch); QMenu* const menu = createStandardContextMenu(); menu->addSeparator(); menu->addAction(action); menu->exec(e->globalPos()); delete menu; } bool autoSearchEnabled() const { return m_autoSearch; } void adjustStatus(bool adv) { if (adv) { QPalette p = palette(); p.setColor(QPalette::Text, p.color(QPalette::Disabled, QPalette::Text)); setPalette(p); setText(i18n("(Advanced Search)")); } else { setPalette(QPalette()); if (text() == i18n("(Advanced Search)")) { setText(QString()); } } } public Q_SLOTS: void toggleAutoSearch() { m_autoSearch = !m_autoSearch; KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(QLatin1String("KeywordSearchEdit Settings")); group.writeEntry(QLatin1String("Autostart Search"), m_autoSearch); } protected: bool m_hasAdvanced; bool m_autoSearch; }; // ------------------------------------------------------------------------- class Q_DECL_HIDDEN SearchTabHeader::Private { public: explicit Private() : newSearchWidget(nullptr), saveAsWidget(nullptr), editSimpleWidget(nullptr), editAdvancedWidget(nullptr), lowerArea(nullptr), keywordEdit(nullptr), advancedEditLabel(nullptr), saveNameEdit(nullptr), saveButton(nullptr), storedKeywordEditName(nullptr), storedKeywordEdit(nullptr), storedAdvancedEditName(nullptr), storedAdvancedEditLabel(nullptr), keywordEditTimer(nullptr), storedKeywordEditTimer(nullptr), searchWindow(nullptr), currentAlbum(nullptr) { } QGroupBox* newSearchWidget; QGroupBox* saveAsWidget; QGroupBox* editSimpleWidget; QGroupBox* editAdvancedWidget; QStackedLayout* lowerArea; KeywordLineEdit* keywordEdit; QPushButton* advancedEditLabel; QLineEdit* saveNameEdit; QToolButton* saveButton; DAdjustableLabel* storedKeywordEditName; QLineEdit* storedKeywordEdit; DAdjustableLabel* storedAdvancedEditName; QPushButton* storedAdvancedEditLabel; QTimer* keywordEditTimer; QTimer* storedKeywordEditTimer; SearchWindow* searchWindow; SAlbum* currentAlbum; QString oldKeywordContent; QString oldStoredKeywordContent; }; SearchTabHeader::SearchTabHeader(QWidget* const parent) : QWidget(parent), d(new Private) { const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QVBoxLayout* const mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(QMargins()); setLayout(mainLayout); // upper part + d->newSearchWidget = new QGroupBox(this); mainLayout->addWidget(d->newSearchWidget); // lower part + d->lowerArea = new QStackedLayout; mainLayout->addLayout(d->lowerArea); d->saveAsWidget = new QGroupBox(this); d->editSimpleWidget = new QGroupBox(this); d->editAdvancedWidget = new QGroupBox(this); d->lowerArea->addWidget(d->saveAsWidget); d->lowerArea->addWidget(d->editSimpleWidget); d->lowerArea->addWidget(d->editAdvancedWidget); // ------------------- // // upper part d->newSearchWidget->setTitle(i18n("New Search")); QGridLayout* const grid1 = new QGridLayout; QLabel* const searchLabel = new QLabel(i18n("Search:"), this); d->keywordEdit = new KeywordLineEdit(this); d->keywordEdit->setClearButtonEnabled(true); d->keywordEdit->setPlaceholderText(i18n("Enter keywords here...")); d->advancedEditLabel = new QPushButton(i18n("Advanced Search..."), this); grid1->addWidget(searchLabel, 0, 0, 1, 1); grid1->addWidget(d->keywordEdit, 0, 1, 1, 1); grid1->addWidget(d->advancedEditLabel, 1, 0, 1, 2); grid1->setContentsMargins(spacing, spacing, spacing, spacing); grid1->setSpacing(spacing); d->newSearchWidget->setLayout(grid1); // ------------------- // // lower part, variant 1 d->saveAsWidget->setTitle(i18n("Save Current Search")); QHBoxLayout* const hbox1 = new QHBoxLayout; d->saveNameEdit = new QLineEdit(this); d->saveNameEdit->setWhatsThis(i18n("Enter a name for the current search to save it in the " "\"Searches\" view")); d->saveButton = new QToolButton(this); d->saveButton->setIcon(QIcon::fromTheme(QLatin1String("document-save"))); d->saveButton->setToolTip(i18n("Save current search to a new virtual Album")); d->saveButton->setWhatsThis(i18n("If you press this button, the current search " "will be saved to a new virtual Search Album using the name " "set on the left side.")); hbox1->addWidget(d->saveNameEdit); hbox1->addWidget(d->saveButton); hbox1->setContentsMargins(spacing, spacing, spacing, spacing); hbox1->setSpacing(spacing); d->saveAsWidget->setLayout(hbox1); // ------------------- // // lower part, variant 2 + d->editSimpleWidget->setTitle(i18n("Edit Stored Search")); QVBoxLayout* const vbox1 = new QVBoxLayout; d->storedKeywordEditName = new DAdjustableLabel(this); d->storedKeywordEditName->setElideMode(Qt::ElideRight); d->storedKeywordEdit = new QLineEdit(this); vbox1->addWidget(d->storedKeywordEditName); vbox1->addWidget(d->storedKeywordEdit); vbox1->setContentsMargins(spacing, spacing, spacing, spacing); vbox1->setSpacing(spacing); d->editSimpleWidget->setLayout(vbox1); // ------------------- // // lower part, variant 3 + d->editAdvancedWidget->setTitle(i18n("Edit Stored Search")); QVBoxLayout* const vbox2 = new QVBoxLayout; d->storedAdvancedEditName = new DAdjustableLabel(this); d->storedAdvancedEditName->setElideMode(Qt::ElideRight); d->storedAdvancedEditLabel = new QPushButton(i18n("Edit..."), this); vbox2->addWidget(d->storedAdvancedEditName); vbox2->addWidget(d->storedAdvancedEditLabel); d->editAdvancedWidget->setLayout(vbox2); // ------------------- // // timers + d->keywordEditTimer = new QTimer(this); d->keywordEditTimer->setSingleShot(true); d->keywordEditTimer->setInterval(800); d->storedKeywordEditTimer = new QTimer(this); d->storedKeywordEditTimer->setSingleShot(true); d->storedKeywordEditTimer->setInterval(800); // ------------------- // connect(d->keywordEdit, SIGNAL(textEdited(QString)), d->keywordEditTimer, SLOT(start())); connect(d->keywordEditTimer, SIGNAL(timeout()), this, SLOT(keywordChangedTimer())); connect(d->keywordEdit, SIGNAL(editingFinished()), this, SLOT(keywordChanged())); connect(d->advancedEditLabel, SIGNAL(clicked()), this, SLOT(editCurrentAdvancedSearch())); connect(d->saveNameEdit, SIGNAL(returnPressed()), this, SLOT(saveSearch())); connect(d->saveButton, SIGNAL(clicked()), this, SLOT(saveSearch())); connect(d->storedKeywordEditTimer, SIGNAL(timeout()), this, SLOT(storedKeywordChanged())); connect(d->storedKeywordEdit, SIGNAL(editingFinished()), this, SLOT(storedKeywordChanged())); connect(d->storedAdvancedEditLabel, SIGNAL(clicked()), this, SLOT(editStoredAdvancedSearch())); } SearchTabHeader::~SearchTabHeader() { delete d->searchWindow; delete d; } SearchWindow* SearchTabHeader::searchWindow() const { if (!d->searchWindow) { qCDebug(DIGIKAM_GENERAL_LOG) << "Creating search window"; + // Create the advanced search edit window, deferred from constructor + d->searchWindow = new SearchWindow; connect(d->searchWindow, SIGNAL(searchEdited(int,QString)), this, SLOT(advancedSearchEdited(int,QString)), Qt::QueuedConnection); } return d->searchWindow; } void SearchTabHeader::selectedSearchChanged(Album* a) { SAlbum* album = dynamic_cast(a); // Signal from SearchFolderView that a search has been selected. - // Don't check on d->currentAlbum == album, rather update status (which may have changed on same album) d->currentAlbum = album; qCDebug(DIGIKAM_GENERAL_LOG) << "changing to SAlbum " << album; if (!album) { d->lowerArea->setCurrentWidget(d->saveAsWidget); d->lowerArea->setEnabled(false); } else { d->lowerArea->setEnabled(true); - if (album->title() == SAlbum::getTemporaryTitle(DatabaseSearch::AdvancedSearch)) + if (album->title() == SAlbum::getTemporaryTitle(DatabaseSearch::AdvancedSearch)) { d->lowerArea->setCurrentWidget(d->saveAsWidget); if (album->isKeywordSearch()) { d->keywordEdit->setText(keywordsFromQuery(album->query())); d->keywordEdit->showAdvancedSearch(false); } else { d->keywordEdit->showAdvancedSearch(true); } } else if (album->isKeywordSearch()) { d->lowerArea->setCurrentWidget(d->editSimpleWidget); d->storedKeywordEditName->setAdjustedText(album->title()); d->storedKeywordEdit->setText(keywordsFromQuery(album->query())); d->keywordEdit->showAdvancedSearch(false); } else { d->lowerArea->setCurrentWidget(d->editAdvancedWidget); d->storedAdvancedEditName->setAdjustedText(album->title()); d->keywordEdit->showAdvancedSearch(false); } } } void SearchTabHeader::editSearch(SAlbum* album) { if (!album) { return; } - if (album->isAdvancedSearch()) + if (album->isAdvancedSearch()) { SearchWindow* window = searchWindow(); window->readSearch(album->id(), album->query()); window->show(); window->raise(); } else if (album->isKeywordSearch()) { d->storedKeywordEdit->selectAll(); } } void SearchTabHeader::newKeywordSearch() { d->keywordEdit->clear(); QString keywords = d->keywordEdit->text(); setCurrentSearch(DatabaseSearch::KeywordSearch, queryFromKeywords(keywords)); d->keywordEdit->setFocus(); } void SearchTabHeader::newAdvancedSearch() { - SearchWindow* window = searchWindow(); + SearchWindow* const window = searchWindow(); window->reset(); window->show(); window->raise(); } void SearchTabHeader::keywordChanged() { QString keywords = d->keywordEdit->text(); qCDebug(DIGIKAM_GENERAL_LOG) << "keywords changed to '" << keywords << "'"; - if (d->oldKeywordContent == keywords || keywords.trimmed().isEmpty()) + if ((d->oldKeywordContent == keywords) || (keywords.trimmed().isEmpty())) { qCDebug(DIGIKAM_GENERAL_LOG) << "same keywords as before, ignoring..."; return; } else { d->oldKeywordContent = keywords; } setCurrentSearch(DatabaseSearch::KeywordSearch, queryFromKeywords(keywords)); d->keywordEdit->setFocus(); } void SearchTabHeader::keywordChangedTimer() { if (d->keywordEdit->autoSearchEnabled()) { keywordChanged(); } } void SearchTabHeader::editCurrentAdvancedSearch() { - SAlbum* album = AlbumManager::instance()->findSAlbum(SAlbum::getTemporaryTitle(DatabaseSearch::AdvancedSearch)); - SearchWindow* window = searchWindow(); + SAlbum* const album = AlbumManager::instance()->findSAlbum(SAlbum::getTemporaryTitle(DatabaseSearch::AdvancedSearch)); + SearchWindow* const window = searchWindow(); if (album) { window->readSearch(album->id(), album->query()); } else { window->reset(); } window->show(); window->raise(); } void SearchTabHeader::saveSearch() { // Only applicable if: // 1. current album is Search View Current Album Save this album as a user names search album. // 2. user as processed a search before to save it. QString name = d->saveNameEdit->text(); qCDebug(DIGIKAM_GENERAL_LOG) << "name = " << name; if (name.isEmpty() || !d->currentAlbum) { qCDebug(DIGIKAM_GENERAL_LOG) << "no current album, returning"; + // passive popup + return; } SAlbum* oldAlbum = AlbumManager::instance()->findSAlbum(name); while (oldAlbum) { QString label = i18n("Search name already exists.\n" "Please enter a new name:"); bool ok; QString newTitle = QInputDialog::getText(this, i18n("Name exists"), label, QLineEdit::Normal, name, &ok); if (!ok) { return; } name = newTitle; oldAlbum = AlbumManager::instance()->findSAlbum(name); } SAlbum* newAlbum = AlbumManager::instance()->createSAlbum(name, d->currentAlbum->searchType(), d->currentAlbum->query()); emit searchShallBeSelected(QList() << newAlbum); } void SearchTabHeader::storedKeywordChanged() { QString keywords = d->storedKeywordEdit->text(); if (d->oldStoredKeywordContent == keywords) { return; } else { d->oldStoredKeywordContent = keywords; } if (d->currentAlbum) { AlbumManager::instance()->updateSAlbum(d->currentAlbum, queryFromKeywords(keywords)); emit searchShallBeSelected(QList() << d->currentAlbum); } } void SearchTabHeader::editStoredAdvancedSearch() { if (d->currentAlbum) { SearchWindow* window = searchWindow(); window->readSearch(d->currentAlbum->id(), d->currentAlbum->query()); window->show(); window->raise(); } } void SearchTabHeader::advancedSearchEdited(int id, const QString& query) { // if the user just pressed the button, but did not change anything in the window, // the search is effectively still a keyword search. // We go the hard way and check this case. + KeywordSearchReader check(query); DatabaseSearch::Type type = check.isSimpleKeywordSearch() ? DatabaseSearch::KeywordSearch : DatabaseSearch::AdvancedSearch; if (id == -1) { setCurrentSearch(type, query); } else { - SAlbum* album = AlbumManager::instance()->findSAlbum(id); + SAlbum* const album = AlbumManager::instance()->findSAlbum(id); if (album) { AlbumManager::instance()->updateSAlbum(album, query, album->title(), type); emit searchShallBeSelected(QList() << album); } } } void SearchTabHeader::setCurrentSearch(DatabaseSearch::Type type, const QString& query, bool selectCurrentAlbum) { SAlbum* album = AlbumManager::instance()->findSAlbum(SAlbum::getTemporaryTitle(DatabaseSearch::KeywordSearch)); if (album) { AlbumManager::instance()->updateSAlbum(album, query, SAlbum::getTemporaryTitle(DatabaseSearch::KeywordSearch), type); } else { album = AlbumManager::instance()->createSAlbum(SAlbum::getTemporaryTitle(DatabaseSearch::KeywordSearch), type, query); } if (selectCurrentAlbum) { emit searchShallBeSelected(QList() << album); } } QString SearchTabHeader::queryFromKeywords(const QString& keywords) const { QStringList keywordList = KeywordSearch::split(keywords); + // create xml + KeywordSearchWriter writer; + return writer.xml(keywordList); } QString SearchTabHeader::keywordsFromQuery(const QString& query) const { KeywordSearchReader reader(query); QStringList keywordList = reader.keywords(); + return KeywordSearch::merge(keywordList); } } // namespace Digikam diff --git a/core/utilities/searchwindow/searchutilities.cpp b/core/utilities/searchwindow/searchutilities.cpp index f47225c024..18da5b8a86 100644 --- a/core/utilities/searchwindow/searchutilities.cpp +++ b/core/utilities/searchwindow/searchutilities.cpp @@ -1,649 +1,661 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-03-14 * Description : User interface for searches * * Copyright (C) 2007 by Aaron Seigo * Copyright (C) 2008-2012 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #include "searchutilities.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "albummodel.h" #include "ratingwidget.h" #include "thememanager.h" #include "itemvisibilitycontroller.h" namespace Digikam { class Q_DECL_HIDDEN AnimatedClearButton::Private : public AnimatedVisibility { public: explicit Private(QObject* const parent) - : AnimatedVisibility(parent) + : AnimatedVisibility(parent), + stayAlwaysVisible(false) { - stayAlwaysVisible = false; } bool stayAlwaysVisible; QPixmap pixmap; }; AnimatedClearButton::AnimatedClearButton(QWidget* const parent) : QWidget(parent), d(new Private(this)) { setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(d, SIGNAL(opacityChanged()), this, SLOT(update())); connect(d, SIGNAL(visibleChanged()), this, SLOT(visibleChanged())); } QSize AnimatedClearButton::sizeHint() const { QFontMetrics fm(font()); + return QSize(d->pixmap.width(), fm.lineSpacing()); } void AnimatedClearButton::stayVisibleWhenAnimatedOut(bool stayVisible) { d->stayAlwaysVisible = stayVisible; visibleChanged(); } void AnimatedClearButton::setShallBeShown(bool shown) { d->controller()->setShallBeShownDirectly(shown); visibleChanged(); } void AnimatedClearButton::animateVisible(bool visible) { // skip animation if parent widget is not visible + if (!parentWidget() || !parentWidget()->isVisible()) { d->controller()->setDirectlyVisible(visible); } d->controller()->setAnimationDuration(visible ? 150 : 250); d->controller()->setVisible(visible); } void AnimatedClearButton::setDirectlyVisible(bool visible) { d->controller()->setDirectlyVisible(visible); } void AnimatedClearButton::setPixmap(const QPixmap& p) { d->pixmap = p; } QPixmap AnimatedClearButton::pixmap() const { return d->pixmap; } void AnimatedClearButton::updateAnimationSettings() { } void AnimatedClearButton::paintEvent(QPaintEvent* event) { Q_UNUSED(event) QPainter p(this); p.setOpacity(1); // make sure p.drawPixmap((width() - d->pixmap.width()) / 2, (height() - d->pixmap.height()) / 2, d->pixmap); } void AnimatedClearButton::visibleChanged() { - if (d->isVisible()) + if (d->isVisible()) { show(); } else if (!d->controller()->shallBeShown() || !d->stayAlwaysVisible) { hide(); } } void AnimatedClearButton::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { emit clicked(); } } // ------------------------------------------------------------------------ class Q_DECL_HIDDEN CustomStepsDoubleSpinBox::Private { public: explicit Private() : beforeInitialValue(true), initialValue(0), smallerStep(0), largerStep(0), invertStepping(false) { } bool beforeInitialValue; QList values; double initialValue; double smallerStep; double largerStep; bool invertStepping; }; CustomStepsDoubleSpinBox::CustomStepsDoubleSpinBox(QWidget* const parent) : QDoubleSpinBox(parent), d(new Private) { } CustomStepsDoubleSpinBox::~CustomStepsDoubleSpinBox() { delete d; } void CustomStepsDoubleSpinBox::stepBy(int steps) { if (d->invertStepping) { steps = -steps; } if (d->values.isEmpty()) { QDoubleSpinBox::stepBy(steps); return; } - if (d->beforeInitialValue && d->initialValue > minimum()) + if (d->beforeInitialValue && (d->initialValue > minimum())) { setValue(d->initialValue); return; } double v = value(); - if (v >= d->values.first() && v <= d->values.last()) + if ((v >= d->values.first()) && (v <= d->values.last())) { int nextStep = 0; if (steps > 0) { // find the next value in d->values after current value - for (nextStep = 0; nextStep < d->values.count(); ++nextStep) + + for (nextStep = 0 ; nextStep < d->values.count() ; ++nextStep) { if (v <= d->values.at(nextStep)) { ++nextStep; break; } } // go as many steps in d->values as we need + int stepsToGo = steps; - for (; stepsToGo > 0 && nextStep < d->values.count(); --stepsToGo) + for ( ; (stepsToGo > 0) && (nextStep < d->values.count()) ; --stepsToGo) { v = d->values.at(nextStep++); } // set the new value + setValue(v); // if anything is left, use Qt code + if (stepsToGo) { QDoubleSpinBox::stepBy(stepsToGo); } } else { - for (nextStep = d->values.count() - 1; nextStep >= 0; --nextStep) + for (nextStep = d->values.count() - 1 ; nextStep >= 0 ; --nextStep) { if (v >= d->values.at(nextStep)) { --nextStep; break; } } int stepsToGo = -steps; - for (; stepsToGo > 0 && nextStep >= 0; --stepsToGo) + for ( ; (stepsToGo > 0) && (nextStep >= 0) ; --stepsToGo) { v = d->values.at(nextStep--); } setValue(v); if (stepsToGo) { QDoubleSpinBox::stepBy(-stepsToGo); } } } else { QDoubleSpinBox::stepBy(steps); } } void CustomStepsDoubleSpinBox::setSuggestedValues(const QList& values) { connect(this, SIGNAL(valueChanged(double)), this, SLOT(slotValueChanged(double))); d->values = values; std::sort(d->values.begin(), d->values.end()); } void CustomStepsDoubleSpinBox::setSuggestedInitialValue(double initialValue) { d->initialValue = initialValue; } void CustomStepsDoubleSpinBox::setSingleSteps(double smaller, double larger) { d->smallerStep = smaller; d->largerStep = larger; } void CustomStepsDoubleSpinBox::setInvertStepping(bool invert) { d->invertStepping = invert; } void CustomStepsDoubleSpinBox::reset() { setValue(minimum()); d->beforeInitialValue = true; } void CustomStepsDoubleSpinBox::slotValueChanged(double val) { if (val != minimum()) { d->beforeInitialValue = false; } if (!d->values.isEmpty()) { - if (d->largerStep && val >= d->values.last()) + if (d->largerStep && val >= d->values.last()) { setSingleStep(d->largerStep); } else if (d->smallerStep) { setSingleStep(d->smallerStep); } } } // ------------------------------------------------------------------------ class Q_DECL_HIDDEN CustomStepsIntSpinBox::Private { public: explicit Private() : beforeInitialValue(true), initialValue(0), smallerStep(0), largerStep(0), invertStepping(false) { } bool beforeInitialValue; QList values; int initialValue; int smallerStep; int largerStep; bool invertStepping; QString fractionPrefix; QString fractionSpecialValueText; }; CustomStepsIntSpinBox::CustomStepsIntSpinBox(QWidget* const parent) : QSpinBox(parent), d(new Private) { } CustomStepsIntSpinBox::~CustomStepsIntSpinBox() { delete d; } void CustomStepsIntSpinBox::stepBy(int steps) { if (d->invertStepping) { steps = -steps; } if (d->values.isEmpty()) { QSpinBox::stepBy(steps); return; } if (d->beforeInitialValue && d->initialValue > minimum()) { setValue(d->initialValue); return; } int v = value(); if (v >= d->values.first() && v <= d->values.last()) { int nextStep = 0; if (steps > 0) { // find the next value in d->values after current value - for (nextStep = 0; nextStep < d->values.count(); ++nextStep) + + for (nextStep = 0 ; nextStep < d->values.count() ; ++nextStep) { if (v <= d->values.at(nextStep)) { ++nextStep; break; } } // go as many steps in d->values as we need + int stepsToGo = steps; - for (; stepsToGo > 0 && nextStep < d->values.count(); --stepsToGo) + for ( ; (stepsToGo > 0) && (nextStep < d->values.count()) ; --stepsToGo) { v = d->values.at(nextStep++); } // set the new value + setValue(v); // if anything is left, use Qt code + if (stepsToGo) { QSpinBox::stepBy(stepsToGo); } } else { - for (nextStep = d->values.count() - 1; nextStep >= 0; --nextStep) + for (nextStep = d->values.count() - 1 ; nextStep >= 0 ; --nextStep) { if (v >= d->values.at(nextStep)) { --nextStep; break; } } int stepsToGo = -steps; - for (; stepsToGo > 0 && nextStep >= 0; --stepsToGo) + for ( ; (stepsToGo > 0) && (nextStep >= 0) ; --stepsToGo) { v = d->values.at(nextStep--); } setValue(v); if (stepsToGo) { QSpinBox::stepBy(-stepsToGo); } } } else { QSpinBox::stepBy(steps); } } void CustomStepsIntSpinBox::setSuggestedValues(const QList& values) { connect(this, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int))); d->values = values; std::sort(d->values.begin(), d->values.end()); } void CustomStepsIntSpinBox::setSuggestedInitialValue(int initialValue) { d->initialValue = initialValue; } void CustomStepsIntSpinBox::setSingleSteps(int smaller, int larger) { d->smallerStep = smaller; d->largerStep = larger; } void CustomStepsIntSpinBox::setInvertStepping(bool invert) { d->invertStepping = invert; } void CustomStepsIntSpinBox::enableFractionMagic(const QString& prefix) { d->fractionPrefix = prefix; std::sort(d->values.begin(), d->values.end(), std::greater()); } void CustomStepsIntSpinBox::reset() { setValue(minimum()); d->beforeInitialValue = true; } QString CustomStepsIntSpinBox::textFromValue(int value) const { // reimplemented for fraction magic handling + if (d->fractionPrefix.isNull()) { return QSpinBox::textFromValue(value); } if (value < 0) { return d->fractionPrefix + QSpinBox::textFromValue(- value); } else { return QSpinBox::textFromValue(value); } } int CustomStepsIntSpinBox::valueFromText(const QString& text) const { // reimplemented for fraction magic handling + if (d->fractionPrefix.isNull()) { return QSpinBox::valueFromText(text); } if (text.startsWith(d->fractionPrefix)) { - return - QSpinBox::valueFromText(text.mid(d->fractionPrefix.length())); + return (- QSpinBox::valueFromText(text.mid(d->fractionPrefix.length()))); } else { return QSpinBox::valueFromText(text); } } QAbstractSpinBox::StepEnabled CustomStepsIntSpinBox::stepEnabled() const { if (d->fractionPrefix.isNull()) { return QSpinBox::stepEnabled(); } QAbstractSpinBox::StepEnabled s; - if (value() > minimum() || value() == minimum()) + if ((value() > minimum()) || (value() == minimum())) { s |= QAbstractSpinBox::StepUpEnabled; } if (value() < maximum()) { s |= QAbstractSpinBox::StepDownEnabled; } return s; } double CustomStepsIntSpinBox::fractionMagicValue() const { if (d->fractionPrefix.isNull()) { return value(); } int v = QSpinBox::value(); if (v < 0) { return - 1.0 / v; } else { return v; } } void CustomStepsIntSpinBox::setFractionMagicValue(double value) { if (d->fractionPrefix.isNull()) { setValue((int)value); return; } if (value < 1.0) { setValue(- lround(1.0 / value)); } else { setValue((int)value); } } void CustomStepsIntSpinBox::slotValueChanged(int val) { if (val != minimum()) { d->beforeInitialValue = false; } if (!d->values.isEmpty()) { - if (d->largerStep && val >= d->values.last()) + if (d->largerStep && (val >= d->values.last())) { setSingleStep(d->largerStep); } else if (d->smallerStep) { setSingleStep(d->smallerStep); } } } // ------------------------------------------------------------------------ StyleSheetDebugger::StyleSheetDebugger(QWidget* const object) : QWidget(nullptr), m_widget(object) { setAttribute(Qt::WA_DeleteOnClose); QVBoxLayout* const vbox = new QVBoxLayout; m_edit = new QTextEdit; m_okButton = new QPushButton(i18n("OK")); m_okButton->setIcon(QIcon::fromTheme(QLatin1String("dialog-ok-apply"))); vbox->addWidget(m_edit, 1); vbox->addWidget(m_okButton, 0, Qt::AlignRight); setLayout(vbox); connect(m_okButton, SIGNAL(clicked()), this, SLOT(buttonClicked())); m_edit->setPlainText(m_widget->styleSheet()); resize(400, 300); show(); } void StyleSheetDebugger::buttonClicked() { m_widget->setStyleSheet(m_edit->toPlainText()); } } // namespace Digikam diff --git a/core/utilities/searchwindow/searchutilities.h b/core/utilities/searchwindow/searchutilities.h index 5c6ca22f9f..1b3052720f 100644 --- a/core/utilities/searchwindow/searchutilities.h +++ b/core/utilities/searchwindow/searchutilities.h @@ -1,254 +1,267 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-03-14 * Description : User interface for searches * * Copyright (C) 2008-2012 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_SEARCH_UTILITIES_H #define DIGIKAM_SEARCH_UTILITIES_H // Qt includes #include #include #include #include #include #include #include #include // Local includes #include "comboboxutilities.h" class QTextEdit; class QPushButton; namespace Digikam { class AnimatedClearButton : public QWidget { Q_OBJECT public: explicit AnimatedClearButton(QWidget* const parent = nullptr); QSize sizeHint() const override; void setPixmap(const QPixmap& p); QPixmap pixmap() const; /** * Sets a primary condition for the button to be shown. * If false, animateVisible() will have no effect. */ void setShallBeShown(bool show); /** This parameter determines the behavior when the animation * to hide the widget has finished: * If stayVisible is true, the widget remains visible, * but paints nothing. * If stayVisible is false, setVisible(false) is called, * which removes the widget for layouting etc. * Default: false */ void stayVisibleWhenAnimatedOut(bool stayVisible); public Q_SLOTS: /// Set visible, possibly with animation void animateVisible(bool visible); /// Set visible without animation void setDirectlyVisible(bool visible); Q_SIGNALS: void clicked(); protected: - virtual void paintEvent(QPaintEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; virtual void mouseReleaseEvent(QMouseEvent* event) override; protected Q_SLOTS: void visibleChanged(); void updateAnimationSettings(); private: class Private; Private* const d; }; // ------------------------------------------------------------------------- class CustomStepsDoubleSpinBox : public QDoubleSpinBox { Q_OBJECT public: - /** This is a normal QDoubleSpinBox which allows to - * customize the stepping behavior, for cases where - * linear steps are not applicable + /** + * This is a normal QDoubleSpinBox which allows to + * customize the stepping behavior, for cases where + * linear steps are not applicable */ explicit CustomStepsDoubleSpinBox(QWidget* const parent = nullptr); ~CustomStepsDoubleSpinBox(); - /** Set a list of values that are usually applicable for the - * type of data of the combo box. The user can still type in - * any other value. Boundaries are not touched. - * Up or below the min and max values of the list given, - * default stepping is used. + /** + * Set a list of values that are usually applicable for the + * type of data of the combo box. The user can still type in + * any other value. Boundaries are not touched. + * Up or below the min and max values of the list given, + * default stepping is used. */ void setSuggestedValues(const QList& values); - /** Sets the value that should be set as first value - * when first moving away from the minimum value. + /** + * Sets the value that should be set as first value + * when first moving away from the minimum value. */ void setSuggestedInitialValue(double initialValue); - /** Allows to set to different default single steps, - * for the range below m_values, the other for above. + /** + * Allows to set to different default single steps, + * for the range below m_values, the other for above. */ void setSingleSteps(double smaller, double larger); void setInvertStepping(bool invert); - /** Resets to minimum value. + /** + * Resets to minimum value. */ void reset(); virtual void stepBy(int steps); private Q_SLOTS: void slotValueChanged(double val); private: class Private; Private* const d; }; // ------------------------------------------------------------------------- class CustomStepsIntSpinBox : public QSpinBox { Q_OBJECT public: - /** This is a normal QIntSpinBox which allows to - * customize the stepping behavior, for cases where - * linear steps are not applicable + /** + * This is a normal QIntSpinBox which allows to + * customize the stepping behavior, for cases where + * linear steps are not applicable */ explicit CustomStepsIntSpinBox(QWidget* const parent = nullptr); ~CustomStepsIntSpinBox(); - /** Set a list of values that are usually applicable for the - * type of data of the combo box. The user can still type in - * any other value. Boundaries are not touched. - * Up or below the min and max values of the list given, - * default stepping is used. + /** + * Set a list of values that are usually applicable for the + * type of data of the combo box. The user can still type in + * any other value. Boundaries are not touched. + * Up or below the min and max values of the list given, + * default stepping is used. */ void setSuggestedValues(const QList& values); - /** Sets the value that should be set as first value - * when first moving away from the minimum value. + /** + * Sets the value that should be set as first value + * when first moving away from the minimum value. */ void setSuggestedInitialValue(int initialValue); - /** Allows to set to different default single steps, - * for the range below m_values, the other for above. + /** + * Allows to set to different default single steps, + * for the range below m_values, the other for above. */ void setSingleSteps(int smaller, int larger); void setInvertStepping(bool invert); - /** Call this with a fraction prefix (like "1/") to enable - * magic handling of the value as fraction denominator. + /** + * Call this with a fraction prefix (like "1/") to enable + * magic handling of the value as fraction denominator. */ void enableFractionMagic(const QString& prefix); - /** Resets to minimum value + /** + * Resets to minimum value */ void reset(); - /** value() and setValue() for fraction magic value. + /** + * value() and setValue() for fraction magic value. */ double fractionMagicValue() const; void setFractionMagicValue(double value); virtual void stepBy(int steps); protected: virtual QString textFromValue(int value) const; virtual int valueFromText(const QString& text) const; virtual StepEnabled stepEnabled() const; private Q_SLOTS: void slotValueChanged(int val); private: class Private; Private* const d; }; // ------------------------------------------------------------------------- class StyleSheetDebugger : public QWidget { Q_OBJECT public: - /** This widget is for development purpose only: - * It allows the developer to change the style sheet - * on a widget dynamically. - * If you want to develop or debug the stylesheet on your widget, - * add temporary code: - * new StyleSheetDebugger(myWidget); - * That's all. Change the style sheet by editing it and pressing Ok. + /** + * This widget is for development purpose only: + * It allows the developer to change the style sheet + * on a widget dynamically. + * If you want to develop or debug the stylesheet on your widget, + * add temporary code: + * new StyleSheetDebugger(myWidget); + * That's all. Change the style sheet by editing it and pressing Ok. */ explicit StyleSheetDebugger(QWidget* const object); protected Q_SLOTS: void buttonClicked(); protected: QTextEdit* m_edit; QPushButton* m_okButton; QWidget* m_widget; }; } // namespace Digikam #endif // DIGIKAM_SEARCH_UTILITIES_H diff --git a/core/utilities/searchwindow/searchview.cpp b/core/utilities/searchwindow/searchview.cpp index 4d60660c56..2483e4dd49 100644 --- a/core/utilities/searchwindow/searchview.cpp +++ b/core/utilities/searchwindow/searchview.cpp @@ -1,542 +1,556 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-01-20 * Description : User interface for searches * * Copyright (C) 2008-2012 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #include "searchview.h" // Qt includes #include #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "searchgroup.h" #include "searchutilities.h" #include "searchwindow.h" #include "coredbsearchxml.h" #include "thememanager.h" namespace Digikam { AbstractSearchGroupContainer::AbstractSearchGroupContainer(QWidget* const parent) : QWidget(parent), m_groupIndex(0) { } SearchGroup* AbstractSearchGroupContainer::addSearchGroup() { SearchGroup* const group = createSearchGroup(); m_groups << group; addGroupToLayout(group); connect(group, SIGNAL(removeRequested()), this, SLOT(removeSendingSearchGroup())); return group; } void AbstractSearchGroupContainer::removeSearchGroup(SearchGroup* group) { if (group->groupType() == SearchGroup::FirstGroup) { qCWarning(DIGIKAM_GENERAL_LOG) << "Attempt to delete the primary search group"; return; } m_groups.removeAll(group); + // This method call may arise from an event handler of a widget within group. Defer deletion. + group->deleteLater(); } void AbstractSearchGroupContainer::startReadingGroups(SearchXmlCachingReader&) { m_groupIndex = 0; } void AbstractSearchGroupContainer::readGroup(SearchXmlCachingReader& reader) { SearchGroup* group = nullptr; if (m_groupIndex >= m_groups.size()) { group = addSearchGroup(); } else { group = m_groups.at(m_groupIndex); } group->read(reader); ++m_groupIndex; } void AbstractSearchGroupContainer::finishReadingGroups() { // remove superfluous groups + while (m_groups.size() > (m_groupIndex + 1)) { delete m_groups.takeLast(); } // for empty searches, and we have an initial search group, reset the remaining search group + if (!m_groupIndex && !m_groups.isEmpty()) { m_groups.first()->reset(); } } void AbstractSearchGroupContainer::writeGroups(SearchXmlWriter& writer) const { foreach (SearchGroup* const group, m_groups) { group->write(writer); } } void AbstractSearchGroupContainer::removeSendingSearchGroup() { removeSearchGroup(static_cast(sender())); } QList AbstractSearchGroupContainer::startupAnimationAreaOfGroups() const { QList list; foreach (SearchGroup* const group, m_groups) { list += group->startupAnimationArea(); } return list; } // ------------------------------------------------------------------------- class Q_DECL_HIDDEN SearchView::Private { public: explicit Private() : needAnimationForReadIn(false), layout(nullptr), timeline(nullptr), bar(nullptr) { } bool needAnimationForReadIn; QVBoxLayout* layout; QCache pixmapCache; QTimeLine* timeline; SearchViewBottomBar* bar; }; SearchView::SearchView() : d(new Private) { d->pixmapCache.setMaxCost(4); } SearchView::~SearchView() { delete d; } void SearchView::setup() { connect(ThemeManager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(setTheme())); setTheme(); d->layout = new QVBoxLayout; d->layout->setContentsMargins(QMargins()); d->layout->setSpacing(0); // add stretch at bottom + d->layout->addStretch(1); // create initial group + addSearchGroup(); setLayout(d->layout); // prepare animation + d->timeline = new QTimeLine(500, this); d->timeline->setFrameRange(0, 100); connect(d->timeline, SIGNAL(finished()), this, SLOT(timeLineFinished())); connect(d->timeline, SIGNAL(frameChanged(int)), this, SLOT(animationFrame(int))); } void SearchView::setBottomBar(SearchViewBottomBar* const bar) { d->bar = bar; connect(d->bar, SIGNAL(okPressed()), this, SIGNAL(searchOk())); connect(d->bar, SIGNAL(cancelPressed()), this, SIGNAL(searchCancel())); connect(d->bar, SIGNAL(tryoutPressed()), this, SIGNAL(searchTryout())); connect(d->bar, SIGNAL(addGroupPressed()), this, SLOT(slotAddGroupButton())); connect(d->bar, SIGNAL(resetPressed()), this, SLOT(slotResetButton())); } void SearchView::read(const QString& xml) { SearchXmlCachingReader reader(xml); startReadingGroups(reader); SearchXml::Element element; while (!reader.atEnd()) { element = reader.readNext(); if (element == SearchXml::Group) { readGroup(reader); } } finishReadingGroups(); if (isVisible()) { startAnimation(); } else { d->needAnimationForReadIn = true; } } void SearchView::addGroupToLayout(SearchGroup* group) { // insert at last-but-one position; leave stretch at the bottom + d->layout->insertWidget(d->layout->count() - 1, group); } SearchGroup* SearchView::createSearchGroup() { SearchGroup* const group = new SearchGroup(this); group->setup(m_groups.isEmpty() ? SearchGroup::FirstGroup : SearchGroup::ChainGroup); + return group; } void SearchView::slotAddGroupButton() { addSearchGroup(); } void SearchView::slotResetButton() { while (m_groups.size() > 1) { delete m_groups.takeLast(); } if (!m_groups.isEmpty()) { if (m_groups.first()) { m_groups.first()->reset(); } } } QString SearchView::write() const { SearchXmlWriter writer; writeGroups(writer); writer.finish(); qCDebug(DIGIKAM_GENERAL_LOG) << writer.xml(); + return writer.xml(); } void SearchView::startAnimation() { d->timeline->setCurveShape(QTimeLine::EaseInCurve); d->timeline->setDuration(500); d->timeline->setDirection(QTimeLine::Forward); d->timeline->start(); } void SearchView::animationFrame(int) { update(); } void SearchView::timeLineFinished() { if (d->timeline->direction() == QTimeLine::Forward) { d->timeline->setDirection(QTimeLine::Backward); d->timeline->start(); } else { update(); } } void SearchView::showEvent(QShowEvent*) { if (d->needAnimationForReadIn) { d->needAnimationForReadIn = false; startAnimation(); } } void SearchView::paintEvent(QPaintEvent*) { if (d->timeline->state() == QTimeLine::Running) { QList rects = startupAnimationAreaOfGroups(); if (rects.isEmpty()) { return; } int animationStep = d->timeline->currentFrame(); const int margin = 2; QRadialGradient grad(0.5, 0.5, 1, 0.5, 0.3); grad.setCoordinateMode(QGradient::ObjectBoundingMode); QColor color = qApp->palette().color(QPalette::Link); QColor colorStart(color), colorEnd(color); colorStart.setAlphaF(0); colorEnd.setAlphaF(color.alphaF() * animationStep / 100.0); grad.setColorAt(0, colorEnd); grad.setColorAt(1, colorStart); QPainter p(this); p.setRenderHint(QPainter::Antialiasing, true); p.setPen(QPen(Qt::NoPen)); p.setBrush(grad); foreach (QRect rect, rects) // krazy:exclude=foreach { rect.adjust(-margin, -margin, margin, margin); p.drawRoundedRect(rect, 4, 4); } } } void SearchView::setTheme() { // settings with style sheet results in extremely slow painting + setBackgroundRole(QPalette::Base); QFont f = font(); QString fontSizeLarger; QString fontSizeSmaller; if (f.pointSizeF() == -1) { // set pixel size + fontSizeLarger = QString::number(f.pixelSize() + 2) + QLatin1String("px"); fontSizeSmaller = QString::number(f.pixelSize() - 2) + QLatin1String("px"); } else { fontSizeLarger = QString::number(f.pointSizeF() + 2) + QLatin1String("pt"); fontSizeSmaller = QString::number(f.pointSizeF() - 2) + QLatin1String("pt"); } QString sheet = QLatin1String("#SearchGroupLabel_MainLabel " " { font-weight: bold; font-size: ") + fontSizeLarger + QLatin1Char(';') + QLatin1String(" color: ") + qApp->palette().color(QPalette::HighlightedText).name() + QLatin1Char(';') + QLatin1String(" } " "#SearchGroupLabel_SimpleLabel " " { font-size: ") + fontSizeLarger + QLatin1Char(';') + QLatin1String(" color: ") + qApp->palette().color(QPalette::HighlightedText).name() + QLatin1Char(';') + QLatin1String(" } " "#SearchGroupLabel_GroupOpLabel " " { font-weight: bold; font-size: ") + fontSizeLarger + QLatin1Char(';') + QLatin1String(" color: ") + qApp->palette().color(QPalette::HighlightedText).name() + QLatin1Char(';') + QLatin1String(" text-decoration: underline; " " } " "#SearchGroupLabel_CheckBox " " { color: ") + qApp->palette().color(QPalette::HighlightedText).name() + QLatin1Char(';') + QLatin1String(" } " "#SearchGroupLabel_RemoveLabel " " { color: ") + qApp->palette().color(QPalette::HighlightedText).name() + QLatin1Char(';') + QLatin1String(" font-style: italic; " " text-decoration: underline; " " } " "#SearchGroupLabel_OptionsLabel " " { color: ") + qApp->palette().color(QPalette::HighlightedText).name() + QLatin1Char(';') + QLatin1String(" font-style: italic; " " text-decoration: underline; font-size: ") + fontSizeSmaller + QLatin1Char(';') + QLatin1String(" } " "#SearchFieldGroupLabel_Label " " { color: ") + qApp->palette().color(QPalette::Link).name() + QLatin1Char(';') + QLatin1String(" font-weight: bold; " " } " "#SearchField_MainLabel " " { font-weight: bold; } " "#SearchFieldChoice_ClickLabel " " { color: ") + qApp->palette().color(QPalette::Link).name() + QLatin1Char(';') + QLatin1String(" font-style: italic; " " text-decoration: underline; " " } " "QComboBox#SearchFieldChoice_ComboBox" " { border-width: 0px; border-style: solid; padding-left: 5px; " " } " "QComboBox::drop-down#SearchFieldChoice_ComboBox" " { subcontrol-origin: padding; subcontrol-position: right top; " " border: 0px; background: rgba(0,0,0,0); width: 0px; height: 0px; " " } "); QWidget::setStyleSheet(sheet); d->pixmapCache.clear(); } QPixmap SearchView::cachedBannerPixmap(int w, int h) const { QString key = QLatin1String("BannerPixmap-") + QString::number(w) + QLatin1Char('-') + QString::number(h); QPixmap* pix = d->pixmapCache.object(key); if (!pix) { QPixmap pixmap(w, h); pixmap.fill(qApp->palette().color(QPalette::Highlight)); d->pixmapCache.insert(key, new QPixmap(pixmap)); + return pixmap; } else { return *pix; } } QPixmap SearchView::groupLabelPixmap(int w, int h) { return cachedBannerPixmap(w, h); } QPixmap SearchView::bottomBarPixmap(int w, int h) { return cachedBannerPixmap(w, h); } // ------------------------------------------------------------------------- SearchViewBottomBar::SearchViewBottomBar(SearchViewThemedPartsCache* const cache, QWidget* const parent) : QWidget(parent), m_themeCache(cache) { m_mainLayout = new QHBoxLayout; m_addGroupsButton = new QPushButton(i18n("Add Search Group")); m_addGroupsButton->setIcon(QIcon::fromTheme(QLatin1String("list-add"))); connect(m_addGroupsButton, SIGNAL(clicked()), this, SIGNAL(addGroupPressed())); m_mainLayout->addWidget(m_addGroupsButton); m_resetButton = new QPushButton(i18n("Reset")); m_resetButton->setIcon(QIcon::fromTheme(QLatin1String("edit-undo"))); connect(m_resetButton, SIGNAL(clicked()), this, SIGNAL(resetPressed())); m_mainLayout->addWidget(m_resetButton); m_mainLayout->addStretch(1); m_buttonBox = new QDialogButtonBox(this); QPushButton* const ok = m_buttonBox->addButton(QDialogButtonBox::Ok); connect(ok, SIGNAL(clicked()), this, SIGNAL(okPressed())); QPushButton* const cancel = m_buttonBox->addButton(QDialogButtonBox::Cancel); connect(cancel, SIGNAL(clicked()), this, SIGNAL(cancelPressed())); QPushButton* const aBtn = m_buttonBox->addButton(i18n("Try"), QDialogButtonBox::ApplyRole); connect(aBtn, SIGNAL(clicked()), this, SIGNAL(tryoutPressed())); m_mainLayout->addWidget(m_buttonBox); setLayout(m_mainLayout); } void SearchViewBottomBar::paintEvent(QPaintEvent*) { // paint themed background + QPainter p(this); p.drawPixmap(0, 0, m_themeCache->bottomBarPixmap(width(), height())); } } // namespace Digikam diff --git a/core/utilities/searchwindow/searchview.h b/core/utilities/searchwindow/searchview.h index 066bf87cc4..f301b406ad 100644 --- a/core/utilities/searchwindow/searchview.h +++ b/core/utilities/searchwindow/searchview.h @@ -1,198 +1,199 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-01-20 * Description : User interface for searches * * Copyright (C) 2008-2012 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_SEARCH_VIEW_H #define DIGIKAM_SEARCH_VIEW_H // Qt includes #include #include #include #include class QHBoxLayout; class QDialogButtonBox; class QPushButton; namespace Digikam { class SearchGroup; class SearchViewBottomBar; class SearchXmlCachingReader; class SearchXmlWriter; class SearchViewThemedPartsCache { public: virtual ~SearchViewThemedPartsCache() { } virtual QPixmap groupLabelPixmap(int w, int h) = 0; - virtual QPixmap bottomBarPixmap(int w, int h) = 0; + virtual QPixmap bottomBarPixmap(int w, int h) = 0; }; class AbstractSearchGroupContainer : public QWidget { Q_OBJECT public: - /// Abstract base class for classes that contain SearchGroups - // To contain common code of SearchView and SearchGroup, - // as SearchGroups can have subgroups. - + /** + * Abstract base class for classes that contain SearchGroups + * To contain common code of SearchView and SearchGroup, + * as SearchGroups can have subgroups. + */ explicit AbstractSearchGroupContainer(QWidget* const parent = nullptr); public Q_SLOTS: SearchGroup* addSearchGroup(); void removeSearchGroup(SearchGroup* group); protected: /// Call before reading the XML part that could contain group elements void startReadingGroups(SearchXmlCachingReader& reader); /// Call when a group element is the current element void readGroup(SearchXmlCachingReader& reader); /// Call when the XML part is finished void finishReadingGroups(); /// Write contained groups to writer void writeGroups(SearchXmlWriter& writer) const; /// Collects the data from the same method of all contained groups (position relative to this widget) QList startupAnimationAreaOfGroups() const; /// Re-implement: create and setup a search group virtual SearchGroup* createSearchGroup() = 0; /// Re-implement: Adds a newly created group to the layout structures virtual void addGroupToLayout(SearchGroup* group) = 0; protected Q_SLOTS: void removeSendingSearchGroup(); protected: int m_groupIndex; QList m_groups; }; // ------------------------------------------------------------------------- class SearchView : public AbstractSearchGroupContainer, public SearchViewThemedPartsCache { Q_OBJECT public: SearchView(); ~SearchView(); void setup(); void setBottomBar(SearchViewBottomBar* const bar); void read(const QString& search); QString write() const; QPixmap groupLabelPixmap(int w, int h) override; QPixmap bottomBarPixmap(int w, int h) override; Q_SIGNALS: void searchOk(); void searchTryout(); void searchCancel(); protected Q_SLOTS: void setTheme(); void slotAddGroupButton(); void slotResetButton(); void startAnimation(); void animationFrame(int); void timeLineFinished(); protected: QPixmap cachedBannerPixmap(int w, int h) const; virtual void paintEvent(QPaintEvent* e) override; virtual void showEvent(QShowEvent* event) override; virtual SearchGroup* createSearchGroup() override; virtual void addGroupToLayout(SearchGroup* group) override; private: // Hidden copy constructor and assignment operator. SearchView(const SearchView&); SearchView& operator=(const SearchView&); class Private; Private* const d; }; // ------------------------------------------------------------------------- class SearchViewBottomBar : public QWidget { Q_OBJECT public: explicit SearchViewBottomBar(SearchViewThemedPartsCache* const cache, QWidget* const parent = nullptr); Q_SIGNALS: void okPressed(); void cancelPressed(); void tryoutPressed(); void addGroupPressed(); void resetPressed(); protected: virtual void paintEvent(QPaintEvent*) override; protected: QHBoxLayout* m_mainLayout; QDialogButtonBox* m_buttonBox; QPushButton* m_addGroupsButton; QPushButton* m_resetButton; SearchViewThemedPartsCache* m_themeCache; }; } // namespace Digikam #endif // DIGIKAM_SEARCH_VIEW_H diff --git a/core/utilities/searchwindow/searchwindow.cpp b/core/utilities/searchwindow/searchwindow.cpp index 002d16eccb..5f04830dc2 100644 --- a/core/utilities/searchwindow/searchwindow.cpp +++ b/core/utilities/searchwindow/searchwindow.cpp @@ -1,181 +1,183 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-01-20 * Description : User interface for searches * * Copyright (C) 2008-2012 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #include "searchwindow.h" // Qt includes #include #include #include // KDE includes #include // Local includes #include "searchview.h" #include "coredbsearchxml.h" #include "thememanager.h" namespace Digikam { class Q_DECL_HIDDEN SearchWindow::Private { public: explicit Private() : scrollArea(nullptr), searchView(nullptr), bottomBar(nullptr), currentId(-1), hasTouchedXml(false) { } QScrollArea* scrollArea; SearchView* searchView; SearchViewBottomBar* bottomBar; int currentId; bool hasTouchedXml; QString oldXml; }; SearchWindow::SearchWindow() : QWidget(nullptr), d(new Private) { QVBoxLayout* const layout = new QVBoxLayout; d->scrollArea = new QScrollArea(this); d->scrollArea->setWidgetResizable(true); d->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->searchView = new SearchView; d->searchView->setup(); d->bottomBar = new SearchViewBottomBar(d->searchView); d->searchView->setBottomBar(d->bottomBar); d->scrollArea->setWidget(d->searchView); d->scrollArea->setFrameStyle(QFrame::NoFrame); layout->addWidget(d->scrollArea); layout->addWidget(d->bottomBar); layout->setContentsMargins(QMargins()); layout->setSpacing(0); setLayout(layout); setVisible(false); setWindowTitle(i18n("Advanced Search")); resize(800, 600); connect(d->searchView, SIGNAL(searchOk()), this, SLOT(searchOk())); connect(d->searchView, SIGNAL(searchCancel()), this, SLOT(searchCancel())); connect(d->searchView, SIGNAL(searchTryout()), this, SLOT(searchTryout())); } SearchWindow::~SearchWindow() { delete d; } void SearchWindow::readSearch(int id, const QString& xml) { d->currentId = id; d->hasTouchedXml = false; d->oldXml = xml; d->searchView->read(xml); } void SearchWindow::reset() { d->currentId = -1; d->hasTouchedXml = false; d->oldXml.clear(); d->searchView->read(QString()); } QString SearchWindow::search() const { return d->searchView->write(); } void SearchWindow::searchOk() { d->hasTouchedXml = true; emit searchEdited(d->currentId, search()); hide(); } void SearchWindow::searchCancel() { // redo changes by tryout + if (d->hasTouchedXml) { emit searchEdited(d->currentId, d->oldXml); d->hasTouchedXml = false; } hide(); } void SearchWindow::searchTryout() { d->hasTouchedXml = true; emit searchEdited(d->currentId, search()); } void SearchWindow::keyPressEvent(QKeyEvent* e) { // Implement keys like in a dialog - if (!e->modifiers() || ((e->modifiers() & Qt::KeypadModifier) && e->key() == Qt::Key_Enter)) + + if (!e->modifiers() || ((e->modifiers() & Qt::KeypadModifier) && (e->key() == Qt::Key_Enter))) { switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Select: searchOk(); break; case Qt::Key_F4: case Qt::Key_Escape: case Qt::Key_Back: searchCancel(); break; default: break; } } } } // namespace Digikam diff --git a/core/utilities/searchwindow/visibilitycontroller.cpp b/core/utilities/searchwindow/visibilitycontroller.cpp index 71ab92f8a5..0cf79d8d6a 100644 --- a/core/utilities/searchwindow/visibilitycontroller.cpp +++ b/core/utilities/searchwindow/visibilitycontroller.cpp @@ -1,245 +1,247 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-03-14 * Description : User interface for searches * * Copyright (C) 2008-2011 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #include "visibilitycontroller.h" namespace Digikam { class Q_DECL_HIDDEN VisibilityController::Private { public: explicit Private() : status(VisibilityController::Unknown), containerWidget(nullptr) { } VisibilityController::Status status; QList objects; QWidget* containerWidget; }; class Q_DECL_HIDDEN VisibilityWidgetWrapper : public QObject, public VisibilityObject { public: VisibilityWidgetWrapper(VisibilityController* const parent, QWidget* const widget) : QObject(parent), m_widget(widget) { } virtual void setVisible(bool visible) override { return m_widget->setVisible(visible); } virtual bool isVisible() override { return m_widget->isVisible(); } QWidget* m_widget; }; // ------------------------------------------------------------------------------------- VisibilityController::VisibilityController(QObject* const parent) : QObject(parent), d(new Private) { } VisibilityController::~VisibilityController() { delete d; } void VisibilityController::addObject(VisibilityObject* const object) { d->objects << object; // create clean state + if (d->status == Unknown) { if (object->isVisible()) { d->status = Shown; } else { d->status = Hidden; } } // set state on object - if (d->status == Shown || d->status == Showing) + + if ((d->status == Shown) || (d->status == Showing)) { object->setVisible(true); } else { object->setVisible(false); } } void VisibilityController::addWidget(QWidget* const widget) { addObject(new VisibilityWidgetWrapper(this, widget)); } void VisibilityController::setContainerWidget(QWidget* const widget) { d->containerWidget = widget; } void VisibilityController::setVisible(bool shallBeVisible) { if (shallBeVisible) { - if (d->status == Shown || d->status == Showing) + if ((d->status == Shown) || (d->status == Showing)) { return; } d->status = Showing; beginStatusChange(); } else { - if (d->status == Hidden || d->status == Hiding) + if ((d->status == Hidden) || (d->status == Hiding)) { return; } d->status = Hiding; beginStatusChange(); } } void VisibilityController::show() { setVisible(true); } void VisibilityController::hide() { setVisible(false); } void VisibilityController::triggerVisibility() { - if (d->status == Shown || d->status == Showing || d->status == Unknown) + if ((d->status == Shown) || (d->status == Showing) || (d->status == Unknown)) { setVisible(false); } else { setVisible(true); } } bool VisibilityController::isVisible() const { - if (d->status == Shown || d->status == Showing) + if ((d->status == Shown) || (d->status == Showing)) { return true; } else { return false; } } void VisibilityController::beginStatusChange() { allSteps(); } void VisibilityController::step() { - if (d->status == Showing) + if (d->status == Showing) { foreach (VisibilityObject* const o, d->objects) { if (!o->isVisible()) { o->setVisible(true); return; } } } else if (d->status == Hiding) { foreach (VisibilityObject* const o, d->objects) { if (o->isVisible()) { o->setVisible(false); return; } } } } void VisibilityController::allSteps() { - if (d->status == Showing) + if (d->status == Showing) { if (d->containerWidget) { d->containerWidget->setUpdatesEnabled(false); } foreach (VisibilityObject* const o, d->objects) { o->setVisible(true); } if (d->containerWidget) { d->containerWidget->setUpdatesEnabled(true); } } else if (d->status == Hiding) { if (d->containerWidget) { d->containerWidget->setUpdatesEnabled(false); } foreach (VisibilityObject* const o, d->objects) { o->setVisible(false); } if (d->containerWidget) { d->containerWidget->setUpdatesEnabled(true); } } } } // namespace Digikam diff --git a/core/utilities/searchwindow/visibilitycontroller.h b/core/utilities/searchwindow/visibilitycontroller.h index 48cff0f2c0..d00e5177d7 100644 --- a/core/utilities/searchwindow/visibilitycontroller.h +++ b/core/utilities/searchwindow/visibilitycontroller.h @@ -1,117 +1,119 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-03-14 * Description : User interface for searches * * Copyright (C) 2008-2011 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_VISIBILITY_CONTROLLER_H #define DIGIKAM_VISIBILITY_CONTROLLER_H // Qt includes #include #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT VisibilityObject { public: virtual ~VisibilityObject() { } virtual void setVisible(bool visible) = 0; virtual bool isVisible() = 0; }; // ----------------------------------------------------------------------------------- class DIGIKAM_EXPORT VisibilityController : public QObject { Q_OBJECT public: enum Status { Unknown, Hidden, Showing, Shown, Hiding }; public: explicit VisibilityController(QObject* const parent); ~VisibilityController(); - /** Set the widget containing the widgets added to this controller */ + /** + * Set the widget containing the widgets added to this controller + */ void setContainerWidget(QWidget* const widget); /** * Add a widget to this controller. */ void addWidget(QWidget* const widget); /** * Add an object implementing the VisibilityObject interface. * You can use this if you have your widgets grouped in intermediate objects. */ void addObject(VisibilityObject* const object); /** * Returns true if the contained objects are visible or becoming visible. */ bool isVisible() const; public Q_SLOTS: /// Shows/hides all added objects void setVisible(bool visible); void show(); void hide(); /// Shows if hidden and hides if visible. void triggerVisibility(); protected: void step(); void allSteps(); virtual void beginStatusChange(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_VISIBILITY_CONTROLLER_H