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