File indexing completed on 2024-05-19 04:23:11

0001 /*
0002    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
0003    Copyright (c) 2011      Martin Koller <kollix@aon.at>
0004    All rights reserved.
0005 
0006    Redistribution and use in source and binary forms, with or without
0007    modification, are permitted provided that the following conditions
0008    are met:
0009 
0010    1. Redistributions of source code must retain the above copyright
0011       notice, this list of conditions and the following disclaimer.
0012    2. Redistributions in binary form must reproduce the above copyright
0013       notice, this list of conditions and the following disclaimer in the
0014       documentation and/or other materials provided with the distribution.
0015 
0016    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0017    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0018    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0019    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0020    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0021    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0022    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0023    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0024    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0025    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0026 */
0027 
0028 
0029 #define DEBUG_KP_TOOL_TOOL_BAR 0
0030 
0031 
0032 #include "widgets/toolbars/kpToolToolBar.h"
0033 
0034 #include <QBoxLayout>
0035 #include <QGridLayout>
0036 #include <QButtonGroup>
0037 #include <QKeyEvent>
0038 #include <QToolButton>
0039 #include <QStyleOption>
0040 #include <QStylePainter>
0041 
0042 #include <KToggleAction>
0043 
0044 #include "kpLogCategories.h"
0045 
0046 #include "kpDefs.h"
0047 #include "tools/kpTool.h"
0048 #include "widgets/toolbars/options/kpToolWidgetBrush.h"
0049 #include "widgets/toolbars/options/kpToolWidgetEraserSize.h"
0050 #include "widgets/toolbars/options/kpToolWidgetFillStyle.h"
0051 #include "widgets/toolbars/options/kpToolWidgetLineWidth.h"
0052 #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h"
0053 #include "widgets/toolbars/options/kpToolWidgetSpraycanSize.h"
0054 
0055 //---------------------------------------------------------------------
0056 
0057 class kpToolButton : public QToolButton
0058 {
0059 public:
0060     kpToolButton (kpTool *tool, QWidget *parent)
0061         : QToolButton (parent),
0062           m_tool (tool)
0063     {
0064     }
0065 
0066     kpTool *tool() const { return m_tool; }
0067 
0068 protected:
0069     void mouseDoubleClickEvent(QMouseEvent *e) override
0070     {
0071         if (e->button () == Qt::LeftButton && m_tool) {
0072             m_tool->globalDraw ();
0073         }
0074     }
0075 
0076     kpTool *m_tool;
0077 };
0078 
0079 //---------------------------------------------------------------------
0080 
0081 kpToolToolBar::kpToolToolBar(const QString &name, int colsOrRows, QMainWindow *parent)
0082     : KToolBar(name, parent, Qt::LeftToolBarArea),
0083       m_vertCols (colsOrRows),
0084       m_buttonGroup (nullptr),
0085       m_baseWidget (nullptr),
0086       m_baseLayout (nullptr),
0087       m_toolLayout (nullptr),
0088       m_previousTool (nullptr), m_currentTool (nullptr)
0089 {
0090     m_baseWidget = new QWidget(this);
0091 
0092     m_toolWidgets.append (m_toolWidgetBrush =
0093         new kpToolWidgetBrush (m_baseWidget, QStringLiteral("Tool Widget Brush")));
0094     m_toolWidgets.append (m_toolWidgetEraserSize =
0095         new kpToolWidgetEraserSize (m_baseWidget, QStringLiteral("Tool Widget Eraser Size")));
0096     m_toolWidgets.append (m_toolWidgetFillStyle =
0097         new kpToolWidgetFillStyle (m_baseWidget, QStringLiteral("Tool Widget Fill Style")));
0098     m_toolWidgets.append (m_toolWidgetLineWidth =
0099         new kpToolWidgetLineWidth (m_baseWidget, QStringLiteral("Tool Widget Line Width")));
0100     m_toolWidgets.append (m_toolWidgetOpaqueOrTransparent =
0101         new kpToolWidgetOpaqueOrTransparent (m_baseWidget, QStringLiteral("Tool Widget Opaque/Transparent")));
0102     m_toolWidgets.append (m_toolWidgetSpraycanSize =
0103         new kpToolWidgetSpraycanSize (m_baseWidget, QStringLiteral("Tool Widget Spraycan Size")));
0104 
0105     for (auto *w : m_toolWidgets)
0106     {
0107       connect (w, &kpToolWidgetBase::optionSelected,
0108                this, &kpToolToolBar::toolWidgetOptionSelected);
0109     }
0110 
0111     adjustToOrientation(orientation());
0112     connect (this, &kpToolToolBar::orientationChanged,
0113              this, &kpToolToolBar::adjustToOrientation);
0114 
0115     m_buttonGroup = new QButtonGroup (this);
0116     connect (m_buttonGroup, &QButtonGroup::idClicked,
0117              this, &kpToolToolBar::slotToolButtonClicked);
0118 
0119     hideAllToolWidgets ();
0120 
0121     addWidget(m_baseWidget);
0122 
0123     connect (this, &kpToolToolBar::iconSizeChanged,
0124              this, &kpToolToolBar::slotIconSizeChanged);
0125 
0126     connect (this, &kpToolToolBar::toolButtonStyleChanged,
0127              this, &kpToolToolBar::slotToolButtonStyleChanged);
0128 }
0129 
0130 //---------------------------------------------------------------------
0131 
0132 kpToolToolBar::~kpToolToolBar()
0133 {
0134     while ( !m_toolButtons.isEmpty() ) {
0135         delete m_toolButtons.takeFirst();
0136     }
0137 }
0138 
0139 //---------------------------------------------------------------------
0140 
0141 // public
0142 void kpToolToolBar::registerTool (kpTool *tool)
0143 {
0144     for (const auto *b : m_toolButtons)
0145     {
0146         if ( b->tool() == tool ) {  // already given
0147             return;
0148         }
0149     }
0150 
0151     auto *b = new kpToolButton(tool, m_baseWidget);
0152 
0153     b->setToolButtonStyle(toolButtonStyle());
0154     b->setIconSize(iconSize());
0155     b->setAutoRaise(true);
0156 
0157     // tell layout to make all with equal width (much better when text-below-icon)
0158     b->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
0159 
0160     b->setDefaultAction(tool->action());
0161 
0162     m_buttonGroup->addButton(b);
0163     addButton(b, orientation(), m_toolButtons.count());
0164 
0165     m_toolButtons.append(b);
0166 
0167     connect (tool, &kpTool::actionActivated,
0168              this, &kpToolToolBar::slotToolActionActivated);
0169 
0170     adjustSizeConstraint();
0171 }
0172 
0173 //---------------------------------------------------------------------
0174 // public
0175 
0176 void kpToolToolBar::unregisterTool(kpTool *tool)
0177 {
0178     for (int i = 0; i < m_toolButtons.count(); i++)
0179     {
0180       if ( m_toolButtons[i]->tool() == tool )
0181       {
0182         delete m_toolButtons.takeAt(i);
0183         disconnect (tool, &kpTool::actionActivated,
0184                  this, &kpToolToolBar::slotToolActionActivated);
0185         return;
0186       }
0187     }
0188 }
0189 
0190 //---------------------------------------------------------------------
0191 // public
0192 
0193 kpTool *kpToolToolBar::tool () const
0194 {
0195     return m_currentTool;
0196 }
0197 
0198 //---------------------------------------------------------------------
0199 
0200 // public
0201 void kpToolToolBar::selectTool (const kpTool *tool, bool reselectIfSameTool)
0202 {
0203 #if DEBUG_KP_TOOL_TOOL_BAR
0204     qCDebug(kpLogWidgets) << "kpToolToolBar::selectTool (tool=" << tool
0205                << ") currentTool=" << m_currentTool
0206                << endl;
0207 #endif
0208 
0209     if (!reselectIfSameTool && tool == m_currentTool) {
0210         return;
0211     }
0212 
0213     if (tool)
0214     {
0215       tool->action()->setChecked(true);
0216       slotToolButtonClicked();
0217     }
0218     else
0219     {
0220         QAbstractButton *b = m_buttonGroup->checkedButton();
0221         if (b)
0222         {
0223             // HACK: qbuttongroup.html says the following about exclusive
0224             //       button groups:
0225             //
0226             //           "to untoggle a button you must click on another button
0227             //            in the group"
0228             //
0229             //       But we don't want any button to be selected.
0230             //       So don't be an exclusive button group temporarily.
0231             m_buttonGroup->setExclusive (false);
0232             b->setChecked (false);
0233             m_buttonGroup->setExclusive (true);
0234 
0235             slotToolButtonClicked ();
0236         }
0237     }
0238 }
0239 
0240 //---------------------------------------------------------------------
0241 
0242 // public
0243 kpTool *kpToolToolBar::previousTool () const
0244 {
0245     return m_previousTool;
0246 }
0247 
0248 //---------------------------------------------------------------------
0249 
0250 // public
0251 void kpToolToolBar::selectPreviousTool ()
0252 {
0253     selectTool (m_previousTool);
0254 }
0255 
0256 //---------------------------------------------------------------------
0257 
0258 // public
0259 void kpToolToolBar::hideAllToolWidgets ()
0260 {
0261     for (auto *w : m_toolWidgets) {
0262       w->hide ();
0263     }
0264 }
0265 
0266 //---------------------------------------------------------------------
0267 
0268 // public
0269 kpToolWidgetBase *kpToolToolBar::shownToolWidget (int which) const
0270 {
0271     int uptoVisibleWidget = 0;
0272 
0273     for(auto *w : m_toolWidgets)
0274     {
0275         if ( !w->isHidden() )
0276         {
0277             if (which == uptoVisibleWidget) {
0278                 return w;
0279             }
0280 
0281             uptoVisibleWidget++;
0282         }
0283     }
0284 
0285     return nullptr;
0286 }
0287 
0288 //---------------------------------------------------------------------
0289 
0290 // private slot
0291 void kpToolToolBar::slotToolButtonClicked ()
0292 {
0293     QAbstractButton *b = m_buttonGroup->checkedButton();
0294 
0295 #if DEBUG_KP_TOOL_TOOL_BAR
0296     qCDebug(kpLogWidgets) << "kpToolToolBar::slotToolButtonClicked() button=" << b;
0297 #endif
0298 
0299     kpTool *tool = nullptr;
0300     for (const auto *button : m_toolButtons)
0301     {
0302       if ( button == b )
0303       {
0304         tool = button->tool();
0305         break;
0306       }
0307     }
0308 
0309 #if DEBUG_KP_TOOL_TOOL_BAR
0310     qCDebug(kpLogWidgets) << "\ttool=" << tool
0311                << " currentTool=" << m_currentTool
0312                << endl;
0313 #endif
0314 
0315     if (tool == m_currentTool)
0316     {
0317         if (m_currentTool) {
0318             m_currentTool->reselect ();
0319         }
0320 
0321         return;
0322     }
0323 
0324     if (m_currentTool) {
0325         m_currentTool->endInternal ();
0326     }
0327 
0328     m_previousTool = m_currentTool;
0329     m_currentTool = tool;
0330 
0331     if (m_currentTool)
0332     {
0333         KToggleAction *action = m_currentTool->action ();
0334         if (action)
0335         {
0336             action->setChecked (true);
0337         }
0338 
0339         m_currentTool->beginInternal ();
0340     }
0341 
0342     Q_EMIT sigToolSelected (m_currentTool);
0343     m_baseLayout->activate();
0344     adjustSizeConstraint();
0345 }
0346 
0347 //---------------------------------------------------------------------
0348 
0349 // private slot
0350 void kpToolToolBar::slotToolActionActivated ()
0351 {
0352     const auto *tool = dynamic_cast<const kpTool *>(sender());
0353 
0354 #if DEBUG_KP_TOOL_TOOL_BAR
0355     qCDebug(kpLogWidgets) << "kpToolToolBar::slotToolActionActivated() tool="
0356                << (tool ? tool->objectName () : "null")
0357                << endl;
0358 #endif
0359 
0360     selectTool (tool, true/*reselect if same tool*/);
0361 }
0362 
0363 //---------------------------------------------------------------------
0364 
0365 // public
0366 void kpToolToolBar::adjustToOrientation(Qt::Orientation o)
0367 {
0368 #if DEBUG_KP_TOOL_TOOL_BAR
0369     qCDebug(kpLogWidgets) << "kpToolToolBar::adjustToOrientation("
0370                << (o == Qt::Vertical ? "vertical" : "horizontal")
0371                << ") called!" << endl;
0372 #endif
0373 
0374     delete m_baseLayout;
0375     if (o == Qt::Vertical)
0376     {
0377         m_baseLayout = new QBoxLayout (QBoxLayout::TopToBottom, m_baseWidget);
0378     }
0379     else // if (o == Qt::Horizontal)
0380     {
0381         m_baseLayout = new QBoxLayout (QBoxLayout::LeftToRight, m_baseWidget);
0382     }
0383     m_baseLayout->setSizeConstraint(QLayout::SetFixedSize);
0384     m_baseLayout->setContentsMargins(0, 0, 0, 0);
0385 
0386     m_toolLayout = new QGridLayout();
0387     m_toolLayout->setContentsMargins(0, 0, 0, 0);
0388 
0389     // (ownership is transferred to m_baseLayout)
0390     m_baseLayout->addItem (m_toolLayout);
0391 
0392     auto num = 0;
0393 
0394     for (auto *b : m_toolButtons)
0395     {
0396       addButton(b, o, num);
0397       num++;
0398     }
0399 
0400     for (auto *w : m_toolWidgets)
0401     {
0402       m_baseLayout->addWidget(w,
0403           0/*stretch*/,
0404           o == Qt::Vertical ? Qt::AlignHCenter : Qt::AlignVCenter);
0405     }
0406 
0407     adjustSizeConstraint();
0408 }
0409 
0410 
0411 bool kpToolToolBar::event(QEvent *ev)
0412 {
0413     if (ev->type() == QEvent::LayoutRequest) {
0414         adjustSizeConstraint();
0415     }
0416     return KToolBar::event(ev);
0417 }
0418 
0419 void kpToolToolBar::paintEvent(class QPaintEvent *)
0420 {
0421     QStyleOption opt;
0422     opt.initFrom(this);
0423     if (opt.rect.height() <= contentsMargins().top() + contentsMargins().bottom() || opt.rect.width() <= contentsMargins().left() + contentsMargins().right()) {
0424         return;
0425     }
0426 
0427     QStylePainter painter(this);
0428 
0429     opt.rect.setX(opt.rect.width() - style()->pixelMetric(QStyle::PM_SplitterWidth));
0430     opt.rect.setWidth(style()->pixelMetric(QStyle::PM_SplitterWidth));
0431     opt.state = QStyle::State_Horizontal;
0432 
0433     painter.drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, opt);
0434 }
0435 
0436 //---------------------------------------------------------------------
0437 // this makes the size handled correctly during dragging/undocking the toolbar
0438 
0439 void kpToolToolBar::adjustSizeConstraint()
0440 {
0441     // remove constraints
0442     setFixedSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
0443 
0444     if ( orientation() == Qt::Vertical )
0445     {
0446       setFixedWidth(m_baseLayout->sizeHint().width() +
0447                     layout()->contentsMargins().left() +
0448                     layout()->contentsMargins().right());
0449     }
0450     else
0451     {
0452       setFixedHeight(m_baseLayout->sizeHint().height() +
0453                      layout()->contentsMargins().top() +
0454                      layout()->contentsMargins().bottom());
0455     }
0456 }
0457 
0458 //---------------------------------------------------------------------
0459 
0460 // private
0461 void kpToolToolBar::addButton(QAbstractButton *button, Qt::Orientation o, int num)
0462 {
0463     if (o == Qt::Vertical) {
0464         m_toolLayout->addWidget (button, num / m_vertCols, num % m_vertCols);
0465     }
0466     else
0467     {
0468         // maps Left (o = vertical) to Bottom (o = horizontal)
0469         int row = (m_vertCols - 1) - (num % m_vertCols);
0470         m_toolLayout->addWidget (button, row, num / m_vertCols);
0471     }
0472 }
0473 
0474 //---------------------------------------------------------------------
0475 
0476 void kpToolToolBar::slotIconSizeChanged(const QSize &size)
0477 {
0478     for (auto *b : m_toolButtons) {
0479         b->setIconSize(size);
0480     }
0481 
0482     m_baseLayout->activate();
0483     adjustSizeConstraint();
0484 }
0485 
0486 //---------------------------------------------------------------------
0487 
0488 void kpToolToolBar::slotToolButtonStyleChanged(Qt::ToolButtonStyle style)
0489 {
0490     for (auto *b : m_toolButtons) {
0491         b->setToolButtonStyle(style);
0492     }
0493 
0494     m_baseLayout->activate();
0495     adjustSizeConstraint();
0496 }
0497 
0498 //---------------------------------------------------------------------
0499 
0500 #include "moc_kpToolToolBar.cpp"