File indexing completed on 2025-03-09 03:57:08

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-05-22
0007  * Description : a control widget for the AdvancedRename utility
0008  *
0009  * SPDX-FileCopyrightText: 2009-2012 by Andi Clemens <andi dot clemens at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "advancedrenamewidget.h"
0016 
0017 // Qt includes
0018 
0019 #include <QAction>
0020 #include <QApplication>
0021 #include <QGridLayout>
0022 #include <QIcon>
0023 #include <QMenu>
0024 #include <QPushButton>
0025 #include <QStyle>
0026 #include <QToolButton>
0027 
0028 // KDE includes
0029 
0030 #include <kconfiggroup.h>
0031 #include <klocalizedstring.h>
0032 #include <ksharedconfig.h>
0033 
0034 // Local includes
0035 
0036 #include "dexpanderbox.h"
0037 #include "advancedrenameinput.h"
0038 #include "defaultrenameparser.h"
0039 #include "dynamiclayout.h"
0040 #include "tooltipcreator.h"
0041 #include "tooltipdialog.h"
0042 
0043 namespace Digikam
0044 {
0045 
0046 class Q_DECL_HIDDEN AdvancedRenameWidget::Private
0047 {
0048     typedef AdvancedRenameWidget::ControlWidgets CWMask;
0049     typedef AdvancedRenameWidget::LayoutStyle    LStyle;
0050 
0051 public:
0052 
0053     explicit Private() :
0054         configExpandedStateDefault  (true),
0055         tooltipToggleButton         (nullptr),
0056         modifiersToolButton         (nullptr),
0057         optionsButton               (nullptr),
0058         modifiersButton             (nullptr),
0059         btnContainer                (nullptr),
0060         tooltipDialog               (nullptr),
0061         renameInput                 (nullptr),
0062         parser                      (nullptr),
0063         optionsLabel                (nullptr),
0064         controlWidgetsMask          (AdvancedRenameWidget::DefaultControls),
0065         layoutStyle                 (AdvancedRenameWidget::LayoutNormal)
0066     {
0067     }
0068 
0069     static const QString configGroupName;
0070     static const QString configExpandedStateEntry;
0071 
0072     bool                 configExpandedStateDefault;
0073 
0074     QToolButton*         tooltipToggleButton;
0075     QToolButton*         modifiersToolButton;
0076 
0077     QPushButton*         optionsButton;
0078     QPushButton*         modifiersButton;
0079 
0080     QWidget*             btnContainer;
0081 
0082     TooltipDialog*       tooltipDialog;
0083     AdvancedRenameInput* renameInput;
0084     Parser*              parser;
0085     DLabelExpander*      optionsLabel;
0086 
0087     CWMask               controlWidgetsMask;
0088     LStyle               layoutStyle;
0089 };
0090 
0091 const QString AdvancedRenameWidget::Private::configGroupName(QLatin1String("AdvancedRename Widget"));
0092 const QString AdvancedRenameWidget::Private::configExpandedStateEntry(QLatin1String("Options are expanded"));
0093 
0094 // --------------------------------------------------------
0095 
0096 AdvancedRenameWidget::AdvancedRenameWidget(QWidget* const parent)
0097     : QWidget(parent),
0098       d      (new Private)
0099 {
0100     setupWidgets();
0101 }
0102 
0103 AdvancedRenameWidget::~AdvancedRenameWidget()
0104 {
0105     writeSettings();
0106     delete d;
0107 }
0108 
0109 QString AdvancedRenameWidget::parseString() const
0110 {
0111     return d->renameInput->text();
0112 }
0113 
0114 void AdvancedRenameWidget::setParseString(const QString& text)
0115 {
0116     d->renameInput->setText(text);
0117 }
0118 
0119 void AdvancedRenameWidget::setParseTimerDuration(int milliseconds)
0120 {
0121     d->renameInput->setParseTimerDuration(milliseconds);
0122 }
0123 
0124 void AdvancedRenameWidget::clearParseString()
0125 {
0126     d->renameInput->slotClearText();
0127 }
0128 
0129 void AdvancedRenameWidget::clear()
0130 {
0131     d->renameInput->slotClearTextAndHistory();
0132 }
0133 
0134 QString AdvancedRenameWidget::parse(ParseSettings& settings) const
0135 {
0136     if (!d->parser)
0137     {
0138         return QString();
0139     }
0140 
0141     settings.parseString = d->renameInput->text();
0142 
0143     QString parsed;
0144     parsed               = d->parser->parse(settings);
0145 
0146     return parsed;
0147 }
0148 
0149 void AdvancedRenameWidget::createToolTip()
0150 {
0151     d->tooltipDialog->clearTooltip();
0152 
0153     if (d->parser)
0154     {
0155         d->tooltipDialog->setTooltip(TooltipCreator::getInstance().tooltip(d->parser));
0156     }
0157 }
0158 
0159 void AdvancedRenameWidget::slotToolTipButtonToggled(bool checked)
0160 {
0161     Q_UNUSED(checked)
0162 
0163     if (!d->tooltipDialog->isVisible())
0164     {
0165         d->tooltipDialog->show();
0166     }
0167 
0168     d->tooltipDialog->raise();
0169 }
0170 
0171 void AdvancedRenameWidget::setLayoutStyle(LayoutStyle style)
0172 {
0173     d->layoutStyle = style;
0174     calculateLayout();
0175 }
0176 
0177 void AdvancedRenameWidget::setControlWidgets(ControlWidgets mask)
0178 {
0179     d->controlWidgetsMask = mask;
0180 
0181     // we need a parser and at least one renaming option to successfully use
0182     // this widget.
0183 
0184     bool enable       = d->parser && !(d->parser->options().isEmpty());
0185 
0186     // enable the modifier toolbutton if environment has been set up correctly
0187 
0188     bool enableModBtn = enable && !(d->parser->modifiers().isEmpty());
0189 
0190     d->renameInput->setEnabled(enable);
0191     d->tooltipToggleButton->setVisible(enable && (mask & ToolTipButton));
0192 
0193     // layout specific
0194 
0195     if (d->layoutStyle == LayoutNormal)
0196     {
0197         d->optionsLabel->setVisible(enable && (mask & TokenButtons));
0198         d->modifiersToolButton->setVisible(enableModBtn && (mask & ModifierToolButton));
0199     }
0200     else
0201     {
0202         d->optionsButton->setVisible(enableModBtn && (mask & TokenButtons));
0203         d->modifiersButton->setVisible(enableModBtn && (mask & ModifierToolButton));
0204     }
0205 }
0206 
0207 QMenu* AdvancedRenameWidget::createControlsMenu(QWidget* const parent, const RulesList& rules)
0208 {
0209     QMenu* const menu = new QMenu(parent);
0210     QAction* action   = nullptr;
0211 
0212     Q_FOREACH (Rule* const rule, rules)
0213     {
0214         action = rule->registerMenu(menu);
0215 
0216         if (!action)
0217         {
0218             continue;
0219         }
0220 
0221         connect(rule, SIGNAL(signalTokenTriggered(QString)),
0222                 d->renameInput, SLOT(slotAddToken(QString)));
0223     }
0224 
0225     return menu;
0226 }
0227 
0228 void AdvancedRenameWidget::registerParserControls()
0229 {
0230     if (d->parser)
0231     {
0232         setupWidgets();
0233 
0234         RulesList optionsList   = d->parser->options();
0235         RulesList modifiersList = d->parser->modifiers();
0236 
0237         if (d->layoutStyle == LayoutNormal)
0238         {
0239             // register options
0240 
0241             QPushButton* btn            = nullptr;
0242             const int cmargin = qMin(style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
0243                                      qMin(style()->pixelMetric(QStyle::PM_LayoutTopMargin),
0244                                           qMin(style()->pixelMetric(QStyle::PM_LayoutRightMargin),
0245                                                style()->pixelMetric(QStyle::PM_LayoutBottomMargin))));
0246 
0247             DynamicLayout* const layout = new DynamicLayout(cmargin, cmargin);
0248 
0249             Q_FOREACH (Rule* const p, d->parser->options())
0250             {
0251                 btn = p->registerButton(this);
0252 
0253                 if (!btn)
0254                 {
0255                     continue;
0256                 }
0257 
0258                 // set button tooltip
0259 
0260                 btn->setToolTip(p->description());
0261 
0262                 layout->addWidget(btn);
0263 
0264                 connect(p, SIGNAL(signalTokenTriggered(QString)),
0265                         d->renameInput, SLOT(slotAddToken(QString)));
0266             }
0267 
0268             d->btnContainer->setLayout(layout);
0269             setMinimumWidth(d->btnContainer->layout()->sizeHint().width());
0270 
0271             // register modifiers
0272 
0273             QMenu* const modifiersMenu = createControlsMenu(d->modifiersToolButton, modifiersList);
0274             d->modifiersToolButton->setMenu(modifiersMenu);
0275         }
0276         else    // LayoutCompact
0277         {
0278             // register options
0279 
0280             QMenu* const optionsMenu = createControlsMenu(d->optionsButton, optionsList);
0281             d->optionsButton->setMenu(optionsMenu);
0282 
0283             // register modifiers
0284 
0285             QMenu* const modifiersMenu = createControlsMenu(d->modifiersButton, modifiersList);
0286             d->modifiersButton->setMenu(modifiersMenu);
0287         }
0288 
0289         // --------------------------------------------------------
0290 
0291         d->renameInput->setParser(d->parser);
0292         createToolTip();
0293     }
0294 }
0295 
0296 Parser* AdvancedRenameWidget::parser() const
0297 {
0298     return d->parser;
0299 }
0300 
0301 void AdvancedRenameWidget::setParser(Parser* parser)
0302 {
0303     if (!parser)
0304     {
0305         return;
0306     }
0307 
0308     d->parser = parser;
0309 
0310     calculateLayout();
0311 }
0312 
0313 void AdvancedRenameWidget::calculateLayout()
0314 {
0315     registerParserControls();
0316     setControlWidgets(d->controlWidgetsMask);
0317 }
0318 
0319 void AdvancedRenameWidget::setupWidgets()
0320 {
0321     /*
0322      * This methods needs to delete all main widgets, do not remove the delete lines!
0323      * If a new parser is set or the layout has changed, we need to call setupWidgets() again.
0324      * So any widget that is created in here needs to be removed first, to avoid memory leaks and
0325      * duplicate signal/slot connections.
0326      */
0327     delete d->tooltipDialog;
0328     delete d->renameInput;
0329     delete d->tooltipToggleButton;
0330     delete d->optionsButton;
0331     delete d->modifiersButton;
0332     delete d->btnContainer;
0333     delete d->optionsLabel;
0334     delete d->modifiersToolButton;
0335 
0336     // --------------------------------------------------------
0337 
0338     d->tooltipDialog = new TooltipDialog(this);
0339     d->tooltipDialog->resize(650, 530);
0340 
0341     d->renameInput = new AdvancedRenameInput;
0342     d->renameInput->setToolTip(i18n("<p>Enter your renaming pattern here. Use the access buttons to quickly add renaming "
0343                                     "options and modifiers. For further explanation, use the information button.</p>"));
0344 
0345     // --------------------------------------------------------
0346 
0347     d->tooltipToggleButton = new QToolButton;
0348     d->tooltipToggleButton->setIcon(QIcon::fromTheme(QLatin1String("dialog-information")));
0349     d->tooltipToggleButton->setToolTip(i18n("Show a list of all available options"));
0350 
0351     // --------------------------------------------------------
0352 
0353     QString modifiersStr     = i18n("Modifiers");
0354     QIcon   modifiersIcon    = QIcon::fromTheme(QLatin1String("document-edit"));
0355     QString modifiersTooltip = i18n("<p>Add a modifier to a renaming option. "
0356                                     "To activate this button, place the cursor behind a renaming option "
0357                                     "or an already assigned modifier.</p>");
0358 
0359     // --------------------------------------------------------
0360 
0361     delete layout();
0362     QGridLayout* const mainLayout = new QGridLayout;
0363 
0364     if (d->layoutStyle == LayoutNormal)
0365     {
0366         d->btnContainer = new QWidget(this);
0367 
0368         d->optionsLabel = new DLabelExpander(this);
0369         d->optionsLabel->setText(i18n("Renaming Options"));
0370         d->optionsLabel->setWidget(d->btnContainer);
0371         d->optionsLabel->setLineVisible(false);
0372 
0373         d->modifiersToolButton = new QToolButton;
0374         d->modifiersToolButton->setPopupMode(QToolButton::InstantPopup);
0375         d->modifiersToolButton->setText(modifiersStr);
0376         d->modifiersToolButton->setIcon(modifiersIcon);
0377         d->modifiersToolButton->setToolTip(modifiersTooltip);
0378 
0379         mainLayout->addWidget(d->renameInput,           0, 0, 1, 1);
0380         mainLayout->addWidget(d->modifiersToolButton,   0, 1, 1, 1);
0381         mainLayout->addWidget(d->tooltipToggleButton,   0, 2, 1, 1);
0382         mainLayout->addWidget(d->optionsLabel,          1, 0, 1, -1);
0383         mainLayout->setColumnStretch(0, 10);
0384     }
0385     else
0386     {
0387         d->optionsButton = new QPushButton;
0388         d->optionsButton->setText(i18n("Options"));
0389         d->optionsButton->setIcon(QIcon::fromTheme(QLatin1String("configure")));
0390         d->optionsButton->setToolTip(i18n("<p>Add renaming options to the parse string.</p>"));
0391 
0392         // --------------------------------------------------------
0393 
0394         d->modifiersButton = new QPushButton;
0395         d->modifiersButton->setText(modifiersStr);
0396         d->modifiersButton->setIcon(modifiersIcon);
0397         d->modifiersButton->setToolTip(modifiersTooltip);
0398 
0399         mainLayout->addWidget(d->renameInput,           0, 0, 1, -1);
0400         mainLayout->addWidget(d->optionsButton,         1, 0, 1, 1);
0401         mainLayout->addWidget(d->modifiersButton,       1, 1, 1, 1);
0402         mainLayout->addWidget(d->tooltipToggleButton,   1, 3, 1, 1);
0403         mainLayout->setColumnStretch(2, 10);
0404     }
0405 
0406     const int cmargin = qMin(style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
0407                              qMin(style()->pixelMetric(QStyle::PM_LayoutTopMargin),
0408                                   qMin(style()->pixelMetric(QStyle::PM_LayoutRightMargin),
0409                                        style()->pixelMetric(QStyle::PM_LayoutBottomMargin))));
0410 
0411     mainLayout->setSpacing(cmargin);
0412     mainLayout->setContentsMargins(QMargins(cmargin, cmargin, cmargin, cmargin));
0413     setLayout(mainLayout);
0414 
0415     // --------------------------------------------------------
0416 
0417     connect(d->tooltipToggleButton, SIGNAL(clicked(bool)),
0418             this, SLOT(slotToolTipButtonToggled(bool)));
0419 
0420     connect(d->renameInput, SIGNAL(signalTextChanged(QString)),
0421             this, SIGNAL(signalTextChanged(QString)));
0422 
0423     connect(d->renameInput, SIGNAL(signalTokenMarked(bool)),
0424             this, SLOT(slotTokenMarked(bool)));
0425 
0426     connect(d->renameInput, SIGNAL(signalReturnPressed()),
0427             this, SIGNAL(signalReturnPressed()));
0428 
0429     slotTokenMarked(false);
0430     readSettings();
0431 }
0432 
0433 void AdvancedRenameWidget::slotTokenMarked(bool marked)
0434 {
0435     bool enable    = marked && d->parser;
0436     bool enableMod = enable && !(d->parser->modifiers().isEmpty());
0437 
0438     if (d->layoutStyle == LayoutNormal)
0439     {
0440         d->modifiersToolButton->setEnabled(enableMod);
0441     }
0442     else
0443     {
0444         d->modifiersButton->setEnabled(enableMod);
0445     }
0446 }
0447 
0448 void AdvancedRenameWidget::focusLineEdit()
0449 {
0450     d->renameInput->slotSetFocus();
0451 }
0452 
0453 void AdvancedRenameWidget::highlightLineEdit()
0454 {
0455     d->renameInput->slotHighlightLineEdit();
0456 }
0457 
0458 void AdvancedRenameWidget::highlightLineEdit(const QString& word)
0459 {
0460     d->renameInput->slotHighlightLineEdit(word);
0461 }
0462 
0463 void AdvancedRenameWidget::readSettings()
0464 {
0465     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0466     KConfigGroup group        = config->group(d->configGroupName);
0467 
0468     if (d->layoutStyle == LayoutNormal)
0469     {
0470         d->optionsLabel->setExpanded(group.readEntry(d->configExpandedStateEntry, d->configExpandedStateDefault));
0471     }
0472 }
0473 
0474 void AdvancedRenameWidget::writeSettings()
0475 {
0476     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0477     KConfigGroup group        = config->group(d->configGroupName);
0478 
0479     if (d->layoutStyle == LayoutNormal)
0480     {
0481         group.writeEntry(d->configExpandedStateEntry, d->optionsLabel ? d->optionsLabel->isExpanded()
0482                                                                       : d->configExpandedStateDefault);
0483     }
0484 }
0485 
0486 } // namespace Digikam
0487 
0488 #include "moc_advancedrenamewidget.cpp"