File indexing completed on 2025-01-05 04:00:06

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2008-01-20
0007  * Description : User interface for searches
0008  *
0009  * SPDX-FileCopyrightText: 2008-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2011-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "searchgroup_p.h"
0017 
0018 namespace Digikam
0019 {
0020 
0021 class Q_DECL_HIDDEN SearchGroupLabel::Private
0022 {
0023 public:
0024 
0025     explicit Private()
0026       : extended     (false),
0027         groupOp      (SearchXml::And),
0028         fieldOp      (SearchXml::And),
0029         layout       (nullptr),
0030         groupOpLabel (nullptr),
0031         allBox       (nullptr),
0032         anyBox       (nullptr),
0033         noneBox      (nullptr),
0034         oneNotBox    (nullptr),
0035         optionsLabel (nullptr),
0036         removeLabel  (nullptr),
0037         stackedLayout(nullptr),
0038         themeCache   (nullptr)
0039     {
0040     }
0041 
0042     bool                        extended;
0043     SearchXml::Operator         groupOp;
0044     SearchXml::Operator         fieldOp;
0045     QGridLayout*                layout;
0046 /*
0047     QComboBox*                  groupOpBox;
0048 */
0049     DClickLabel*                groupOpLabel;
0050     QRadioButton*               allBox;
0051     QRadioButton*               anyBox;
0052     QRadioButton*               noneBox;
0053     QRadioButton*               oneNotBox;
0054     DClickLabel*                optionsLabel;
0055     DClickLabel*                removeLabel;
0056     QStackedLayout*             stackedLayout;
0057     SearchViewThemedPartsCache* themeCache;
0058 };
0059 
0060 SearchGroupLabel::SearchGroupLabel(SearchViewThemedPartsCache* const cache, SearchGroup::Type type, QWidget* const parent)
0061     : QWidget(parent),
0062       d      (new Private)
0063 {
0064     d->themeCache = cache;
0065     d->layout     = new QGridLayout;
0066 
0067     // leave styling to style sheet (by object name)
0068 
0069     QLabel* const mainLabel = new QLabel(i18n("Find Items"));
0070     mainLabel->setObjectName(QLatin1String("SearchGroupLabel_MainLabel"));
0071 
0072     // Use radio button with a separate label to fix styling problem, see bug 195809
0073 
0074     d->allBox                 = new QRadioButton;
0075     QLabel* const allBoxLabel = new QLabel(i18n("Meet All of the following conditions"));
0076     allBoxLabel->setObjectName(QLatin1String("SearchGroupLabel_CheckBox"));
0077 
0078     d->anyBox                 = new QRadioButton;
0079     QLabel* const anyBoxLabel = new QLabel(i18n("Meet Any of the following conditions"));
0080     anyBoxLabel->setObjectName(QLatin1String("SearchGroupLabel_CheckBox"));
0081 
0082     d->noneBox = new QRadioButton;
0083     QLabel* const noneBoxLabel = new QLabel(i18n("None of these conditions are met"));
0084     noneBoxLabel->setObjectName(QLatin1String("SearchGroupLabel_CheckBox"));
0085 
0086     d->oneNotBox                 = new QRadioButton;
0087     QLabel* const oneNotBoxLabel = new QLabel(i18n("At least one of these conditions is not met"));
0088     oneNotBoxLabel->setObjectName(QLatin1String("SearchGroupLabel_CheckBox"));
0089 
0090     connect(d->allBox, SIGNAL(toggled(bool)),
0091             this, SLOT(boxesToggled()));
0092 
0093     connect(d->anyBox, SIGNAL(toggled(bool)),
0094             this, SLOT(boxesToggled()));
0095 
0096     connect(d->noneBox, SIGNAL(toggled(bool)),
0097             this, SLOT(boxesToggled()));
0098 
0099     connect(d->oneNotBox, SIGNAL(toggled(bool)),
0100             this, SLOT(boxesToggled()));
0101 
0102     if (type == SearchGroup::FirstGroup)
0103     {
0104         QLabel* const logo              = new QLabel;
0105         logo->setPixmap(QIcon::fromTheme(QLatin1String("digikam")).pixmap(QSize(48,48)));
0106 
0107         d->optionsLabel                 = new DClickLabel;
0108         d->optionsLabel->setObjectName(QLatin1String("SearchGroupLabel_OptionsLabel"));
0109 
0110         connect(d->optionsLabel, SIGNAL(activated()),
0111                 this, SLOT(toggleShowOptions()));
0112 
0113         QWidget* const simpleHeader     = new QWidget;
0114         QVBoxLayout* const headerLayout = new QVBoxLayout;
0115         QLabel* const simpleLabel1      = new QLabel;
0116 /*
0117         simpleLabel->setText(i18n("Find Pictures meeting all of these conditions"));
0118         simpleLabel->setPixmap(QIcon::fromTheme(QLatin1String("edit-find")).pixmap(128));
0119 */
0120         simpleLabel1->setText(i18n("<qt><p>Search your collection<br/>for Items meeting the following conditions</p></qt>"));
0121         simpleLabel1->setObjectName(QLatin1String("SearchGroupLabel_SimpleLabel"));
0122         headerLayout->addStretch(3);
0123         headerLayout->addWidget(simpleLabel1);
0124         headerLayout->addStretch(1);
0125         headerLayout->setContentsMargins(QMargins());
0126         simpleHeader->setLayout(headerLayout);
0127 
0128         QWidget* const optionsBox        = new QWidget;
0129         QGridLayout* const optionsLayout = new QGridLayout;
0130         optionsLayout->addLayout(new RadioButtonHBox(d->allBox,    allBoxLabel,    layoutDirection()), 0, 0);
0131         optionsLayout->addLayout(new RadioButtonHBox(d->anyBox,    anyBoxLabel,    layoutDirection()), 1, 0);
0132         optionsLayout->addLayout(new RadioButtonHBox(d->noneBox,   noneBoxLabel,   layoutDirection()), 0, 1);
0133         optionsLayout->addLayout(new RadioButtonHBox(d->oneNotBox, oneNotBoxLabel, layoutDirection()), 1, 1);
0134         optionsLayout->setContentsMargins(QMargins());
0135         optionsBox->setLayout(optionsLayout);
0136 
0137         d->stackedLayout = new QStackedLayout;
0138         d->stackedLayout->addWidget(simpleHeader);
0139         d->stackedLayout->addWidget(optionsBox);
0140         d->stackedLayout->setContentsMargins(QMargins());
0141 
0142         d->layout->addWidget(mainLabel,        0, 0, 1, 1);
0143         d->layout->addLayout(d->stackedLayout, 1, 0, 1, 1);
0144         d->layout->addWidget(d->optionsLabel,  1, 1, 1, 1, Qt::AlignRight | Qt::AlignBottom);
0145         d->layout->addWidget(logo,             0, 2, 2, 1, Qt::AlignTop);
0146         d->layout->setColumnStretch(1, 10);
0147 
0148         setExtended(false);
0149     }
0150     else
0151     {
0152         d->groupOpLabel = new DClickLabel;
0153         d->groupOpLabel->setObjectName(QLatin1String("SearchGroupLabel_GroupOpLabel"));
0154 
0155         connect(d->groupOpLabel, SIGNAL(activated()),
0156                 this, SLOT(toggleGroupOperator()));
0157 
0158         d->removeLabel  = new DClickLabel(i18n("Remove Group"));
0159         d->removeLabel->setObjectName(QLatin1String("SearchGroupLabel_RemoveLabel"));
0160 
0161         connect(d->removeLabel, SIGNAL(activated()),
0162                 this, SIGNAL(removeClicked()));
0163 
0164         d->layout->addWidget(d->groupOpLabel, 0, 0, 1, 1);
0165         d->layout->addLayout(new RadioButtonHBox(d->allBox, allBoxLabel, layoutDirection()),       1, 0, 1, 1);
0166         d->layout->addLayout(new RadioButtonHBox(d->anyBox, anyBoxLabel, layoutDirection()),       2, 0, 1, 1);
0167         d->layout->addLayout(new RadioButtonHBox(d->noneBox, noneBoxLabel, layoutDirection()),     3, 0, 1, 1);
0168         d->layout->addLayout(new RadioButtonHBox(d->oneNotBox, oneNotBoxLabel, layoutDirection()), 4, 0, 1, 1);
0169         d->layout->addWidget(d->removeLabel,  0, 2, 1, 1);
0170         d->layout->setColumnStretch(1, 10);
0171     }
0172 
0173     setLayout(d->layout);
0174 
0175     // Default values
0176 
0177     setGroupOperator(SearchXml::standardGroupOperator());
0178     setDefaultFieldOperator(SearchXml::standardFieldOperator());
0179 }
0180 
0181 SearchGroupLabel::~SearchGroupLabel()
0182 {
0183     delete d;
0184 }
0185 
0186 void SearchGroupLabel::setExtended(bool extended)
0187 {
0188     d->extended = extended;
0189 
0190     if (!d->stackedLayout)
0191     {
0192         return;
0193     }
0194 
0195     if (d->extended)
0196     {
0197         d->stackedLayout->setCurrentIndex(1);
0198         d->allBox->setVisible(true);
0199         d->anyBox->setVisible(true);
0200         d->noneBox->setVisible(true);
0201         d->oneNotBox->setVisible(true);
0202         d->optionsLabel->setText(i18n("Hide Options") + QLatin1String(" <<"));
0203     }
0204     else
0205     {
0206         d->stackedLayout->setCurrentIndex(0);
0207 
0208         // hide to reduce reserved space in stacked layout
0209 
0210         d->allBox->setVisible(false);
0211         d->anyBox->setVisible(false);
0212         d->noneBox->setVisible(false);
0213         d->oneNotBox->setVisible(false);
0214         d->optionsLabel->setText(i18n("Options") + QLatin1String(" >>"));
0215     }
0216 }
0217 
0218 void SearchGroupLabel::toggleShowOptions()
0219 {
0220     setExtended(!d->extended);
0221 }
0222 
0223 void SearchGroupLabel::toggleGroupOperator()
0224 {
0225     if      (d->groupOp == SearchXml::And)
0226     {
0227         d->groupOp = SearchXml::Or;
0228     }
0229     else if (d->groupOp == SearchXml::Or)
0230     {
0231         d->groupOp = SearchXml::And;
0232     }
0233     else if (d->groupOp == SearchXml::AndNot)
0234     {
0235         d->groupOp = SearchXml::OrNot;
0236     }
0237     else if (d->groupOp == SearchXml::OrNot)
0238     {
0239         d->groupOp = SearchXml::AndNot;
0240     }
0241 
0242     updateGroupLabel();
0243 }
0244 
0245 void SearchGroupLabel::boxesToggled()
0246 {
0247     // set field op
0248 
0249     if (d->allBox->isChecked() || d->oneNotBox->isChecked())
0250     {
0251         d->fieldOp = SearchXml::And;
0252     }
0253     else
0254     {
0255         d->fieldOp = SearchXml::Or;
0256     }
0257 
0258     // negate group op
0259 
0260     if (d->allBox->isChecked() || d->anyBox->isChecked())
0261     {
0262         if      (d->groupOp == SearchXml::AndNot)
0263         {
0264             d->groupOp = SearchXml::And;
0265         }
0266         else if (d->groupOp == SearchXml::OrNot)
0267         {
0268             d->groupOp = SearchXml::Or;
0269         }
0270     }
0271     else
0272     {
0273         if      (d->groupOp == SearchXml::And)
0274         {
0275             d->groupOp = SearchXml::AndNot;
0276         }
0277         else if (d->groupOp == SearchXml::Or)
0278         {
0279             d->groupOp = SearchXml::OrNot;
0280         }
0281     }
0282 }
0283 
0284 void SearchGroupLabel::setGroupOperator(SearchXml::Operator op)
0285 {
0286     d->groupOp = op;
0287     adjustOperatorOptions();
0288     updateGroupLabel();
0289 }
0290 
0291 void SearchGroupLabel::updateGroupLabel()
0292 {
0293     if (d->groupOpLabel)
0294     {
0295         if ((d->groupOp == SearchXml::And) || (d->groupOp == SearchXml::AndNot))
0296         {
0297             d->groupOpLabel->setText(i18n("AND"));
0298         }
0299         else
0300         {
0301             d->groupOpLabel->setText(i18n("OR"));
0302         }
0303     }
0304 }
0305 
0306 void SearchGroupLabel::setDefaultFieldOperator(SearchXml::Operator op)
0307 {
0308     d->fieldOp = op;
0309     adjustOperatorOptions();
0310 }
0311 
0312 void SearchGroupLabel::adjustOperatorOptions()
0313 {
0314     // In the UI, the NOT is done at the level of the field operator,
0315     // but in fact we put a NOT in front of the whole group, so it is the group operator!
0316 
0317     // 1. allBox, All of these conditions are met: (A && B && C), Group And/Or, Field And
0318     // 2. anyBox, Any of these conditions are met: (A || B || C), Group And/Or, Field Or
0319     // 3. oneNotBox, At least one of these conditions is not met: !(A && B && C) = (!A || !B || !C),
0320     //    Group AndNot/OrNot, Field And
0321     // 4. noneBox, None of these conditions are met: !(A || B || C) = (!A && !B && !C),
0322     //    Group AndNot/OrNot, Field Or
0323 
0324     switch (d->groupOp)
0325     {
0326         case SearchXml::And:
0327         case SearchXml::Or:
0328 
0329             if (d->fieldOp == SearchXml::And)
0330             {
0331                 d->allBox->setChecked(true);
0332             }
0333             else
0334             {
0335                 d->anyBox->setChecked(true);
0336             }
0337 
0338             break;
0339 
0340         case SearchXml::AndNot:
0341         case SearchXml::OrNot:
0342 
0343             if (d->fieldOp == SearchXml::And)
0344             {
0345                 d->oneNotBox->setChecked(true);
0346             }
0347             else
0348             {
0349                 d->noneBox->setChecked(true);
0350             }
0351 
0352             break;
0353     }
0354 
0355     if (!d->allBox->isChecked())
0356     {
0357         setExtended(true);
0358     }
0359 }
0360 
0361 SearchXml::Operator SearchGroupLabel::groupOperator() const
0362 {
0363     return d->groupOp;
0364 }
0365 
0366 SearchXml::Operator SearchGroupLabel::defaultFieldOperator() const
0367 {
0368     if (d->anyBox->isChecked() || d->noneBox->isChecked())
0369     {
0370         return SearchXml::Or;
0371     }
0372     else
0373     {
0374         return SearchXml::And;
0375     }
0376 }
0377 
0378 void SearchGroupLabel::paintEvent(QPaintEvent*)
0379 {
0380     // paint themed background
0381 
0382     QPainter p(this);
0383     p.drawPixmap(0, 0, d->themeCache->groupLabelPixmap(width(), height()));
0384 }
0385 
0386 } // namespace Digikam