File indexing completed on 2024-12-22 04:13:08

0001 /*
0002  *  SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_categorized_list_view.h"
0008 #include "kis_categorized_list_model.h"
0009 #include <QMouseEvent>
0010 #include <QMenu>
0011 #include <QAction>
0012 #include <QShowEvent>
0013 #include <kconfig.h>
0014 #include <klocalizedstring.h>
0015 #include <kis_icon.h>
0016 #include "kis_debug.h"
0017 #include <KisKineticScroller.h>
0018 
0019 KisCategorizedListView::KisCategorizedListView(QWidget* parent):
0020     QListView(parent)
0021 {
0022     connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(slotIndexChanged(QModelIndex)));
0023 
0024     // Because this widget has a darker background, the checkbox borders get hidden with default palette
0025     // This palette update makes the checkboxes easier to see by starting with the text color
0026     QPalette newPall = palette();
0027     newPall.setColor(QPalette::Active, QPalette::Window, palette().text().color() );
0028     setPalette(newPall);
0029 
0030     {
0031         QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
0032         if (scroller) {
0033             connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChange(QScroller::State)));
0034         }
0035     }
0036 }
0037 
0038 KisCategorizedListView::~KisCategorizedListView()
0039 {
0040 }
0041 
0042 void KisCategorizedListView::setModel(QAbstractItemModel* model)
0043 {
0044     QListView::setModel(model);
0045     updateRows(0, model->rowCount());
0046     model->sort(0);
0047 }
0048 
0049 QSize KisCategorizedListView::sizeHint() const
0050 {
0051     const QSize sh = QListView::sizeHint();
0052     const int width = sizeHintForColumn(0);
0053 
0054     return QSize(width, sh.height());
0055 }
0056 
0057 void KisCategorizedListView::setCompositeBoxControl(bool value)
0058 {
0059     isCompositeBoxControl = value;
0060 }
0061 
0062 void KisCategorizedListView::updateRows(int begin, int end)
0063 {
0064     for(; begin!=end; ++begin) {
0065         QModelIndex index    = model()->index(begin, 0);
0066         bool        isHeader = model()->data(index, __CategorizedListModelBase::IsHeaderRole).toBool();
0067         bool        expanded = model()->data(index, __CategorizedListModelBase::ExpandCategoryRole).toBool();
0068         setRowHidden(begin, !expanded && !isHeader);
0069     }
0070 }
0071 
0072 void KisCategorizedListView::slotIndexChanged(const QModelIndex& index)
0073 {
0074     if(model()->data(index, __CategorizedListModelBase::IsHeaderRole).toBool()) {
0075         bool expanded = model()->data(index, __CategorizedListModelBase::ExpandCategoryRole).toBool();
0076         model()->setData(index, !expanded, __CategorizedListModelBase::ExpandCategoryRole);
0077         emit sigCategoryToggled(index, !expanded);
0078     }
0079 }
0080 
0081 void KisCategorizedListView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int> &roles)
0082 {
0083     QListView::dataChanged(topLeft, bottomRight);
0084     updateRows(topLeft.row(), bottomRight.row()+1);
0085 
0086     // check to see if the data changed was a check box
0087     // if it is a checkbox tell the brush editor that the preset is now "dirty"
0088     int i = 0;
0089     for (QVector<int>::const_iterator iterator = roles.begin(); iterator != roles.end(); ++iterator) {
0090 
0091         if (Qt::CheckStateRole == roles.at(i) ) {
0092             int row = topLeft.row();
0093             int column = topLeft.column();
0094 
0095             emit sigEntryChecked(model()->index(row, column));
0096         } else if (__CategorizedListModelBase::ExpandCategoryRole == roles.at(i)) {
0097            // logic to target the expand/contract menus if needed
0098         }
0099 
0100         i++;
0101     }
0102 }
0103 
0104 void KisCategorizedListView::rowsInserted(const QModelIndex& parent, int start, int end)
0105 {
0106     QListView::rowsInserted(parent, start, end);
0107     updateRows(0, model()->rowCount());
0108     model()->sort(0);
0109 }
0110 
0111 void KisCategorizedListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
0112 {
0113     QListView::rowsAboutToBeRemoved(parent, start, end);
0114     model()->sort(0);
0115 }
0116 
0117 void KisCategorizedListView::mousePressEvent(QMouseEvent* event)
0118 {
0119     QListView::mousePressEvent(event);
0120 
0121     QModelIndex index = QListView::indexAt(event->pos());
0122 
0123 
0124     // hack: the custom compositeop combo box has issues with events being sent
0125     // those widgets will run this extra code to help them know if a checkbox is clicked
0126     if (isCompositeBoxControl) {
0127 
0128         if (index.isValid() && (event->pos().x() < 25) && (model()->flags(index) & Qt::ItemIsUserCheckable)) {
0129             QListView::mousePressEvent(event);
0130             QMouseEvent releaseEvent(QEvent::MouseButtonRelease,
0131                             event->pos(),
0132                             event->globalPos(),
0133                             event->button(),
0134                             event->button() | event->buttons(),
0135                             event->modifiers());
0136 
0137             QListView::mouseReleaseEvent(&releaseEvent);
0138             emit sigEntryChecked(index);
0139 
0140             return; // don't worry about running the 'right' click logic below. that is not relevant with composite ops
0141         }
0142     }
0143 
0144 
0145 
0146     if (event->button() == Qt::RightButton){
0147 
0148         QMenu menu(this);
0149 
0150         if(index.data(__CategorizedListModelBase::isLockableRole).toBool() && index.isValid()) {
0151 
0152             bool locked = index.data(__CategorizedListModelBase::isLockedRole).toBool();
0153 
0154             QIcon icon = locked ? KisIconUtils::loadIcon("unlocked") : KisIconUtils::loadIcon("locked");
0155 
0156             // Add an empty section so there's a bit more space before users accidentally press lock
0157             // See https://bugs.kde.org/show_bug.cgi?id=447367
0158             menu.addSection("     ");
0159 
0160             QAction* action1 = menu.addAction(icon, locked ? i18n("Unlock (restore settings from preset)")
0161                                                            : i18n("Lock"));
0162 
0163             connect(action1, SIGNAL(triggered()), this, SIGNAL(rightClickedMenuDropSettingsTriggered()));
0164 
0165             if (locked){
0166                 QAction* action2 = menu.addAction(icon, i18n("Unlock (keep current settings)"));
0167                 connect(action2, SIGNAL(triggered()), this, SIGNAL(rightClickedMenuSaveSettingsTriggered()));
0168             }
0169             menu.exec(event->globalPos());
0170         }
0171     }
0172 }
0173 
0174 void KisCategorizedListView::mouseReleaseEvent(QMouseEvent* event)
0175 {
0176     QListView::mouseReleaseEvent(event);
0177 }
0178 
0179 void KisCategorizedListView::slotScrollerStateChange(QScroller::State state)
0180 {
0181     KisKineticScroller::updateCursor(this, state);
0182 }
0183 
0184 
0185