File indexing completed on 2024-05-12 16:34:42

0001 /* This file is part of the Calligra project
0002  * Copyright (C) 2010-2014 Yue Liu <yue.liu@mail.com>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Library General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 
0020 #include "CollectionTreeWidget.h"
0021 
0022 #include "StencilListView.h"
0023 #include "CollectionItemModel.h"
0024 #include "StencilBoxDebug.h"
0025 
0026 #include <klocalizedstring.h>
0027 #include <ksharedconfig.h>
0028 #include <kconfiggroup.h>
0029 
0030 #include <QTreeWidgetItem>
0031 #include <QApplication>
0032 #include <QContextMenuEvent>
0033 #include <QAction>
0034 #include <QActionGroup>
0035 #include <QMenu>
0036 #include <QHeaderView>
0037 #include <QSortFilterProxyModel>
0038 #include <QPainter>
0039 
0040 SheetDelegate::SheetDelegate(QTreeView* view, QWidget* parent)
0041     : QItemDelegate(parent),
0042       m_view(view)
0043 {
0044 }
0045 
0046 // style comes from qt designer
0047 // https://qt.gitorious.org/qt/qttools/source/src/designer/src/lib/shared/sheet_delegate.cpp
0048 void SheetDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
0049 {
0050     const QAbstractItemModel* model = index.model();
0051     Q_ASSERT(model);
0052 
0053     if (!model->parent(index).isValid()) {
0054         // this is a top-level item.
0055         QStyleOptionButton buttonOption;
0056 
0057         buttonOption.state = option.state;
0058 #ifdef Q_OS_MAC
0059         buttonOption.state |= QStyle::State_Raised;
0060 #endif
0061         buttonOption.state &= ~QStyle::State_HasFocus;
0062 
0063         buttonOption.rect = option.rect;
0064         buttonOption.palette = option.palette;
0065         buttonOption.features = QStyleOptionButton::None;
0066 
0067         painter->save();
0068         QColor buttonColor(230, 230, 230);
0069         QBrush buttonBrush = option.palette.button();
0070         if (!buttonBrush.gradient() && buttonBrush.texture().isNull())
0071             buttonColor = buttonBrush.color();
0072         QColor outlineColor = buttonColor.darker(150);
0073         QColor highlightColor = buttonColor.lighter(130);
0074 
0075         // Only draw topline if the previous item is expanded
0076         QModelIndex previousIndex = model->index(index.row() - 1, index.column());
0077         bool drawTopline = (index.row() > 0 && m_view->isExpanded(previousIndex));
0078         int highlightOffset = drawTopline ? 1 : 0;
0079 
0080         QLinearGradient gradient(option.rect.topLeft(), option.rect.bottomLeft());
0081         gradient.setColorAt(0, buttonColor.lighter(102));
0082         gradient.setColorAt(1, buttonColor.darker(106));
0083 
0084         painter->setPen(Qt::NoPen);
0085         painter->setBrush(gradient);
0086         painter->drawRect(option.rect);
0087         painter->setPen(QPen(highlightColor, 0));
0088         painter->drawLine(option.rect.topLeft() + QPoint(0, highlightOffset),
0089                           option.rect.topRight() + QPoint(0, highlightOffset));
0090         painter->setPen(QPen(outlineColor, 0));
0091         if (drawTopline)
0092             painter->drawLine(option.rect.topLeft(), option.rect.topRight());
0093         painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
0094         painter->restore();
0095 
0096         QStyleOption branchOption;
0097         static const int i = 9; // ### hardcoded in qcommonstyle.cpp
0098         QRect r = option.rect;
0099         branchOption.rect = QRect(r.left() + i / 2, r.top() + (r.height() - i) / 2, i, i);
0100         branchOption.palette = option.palette;
0101         branchOption.state = QStyle::State_Children;
0102 
0103         if (m_view->isExpanded(index))
0104             branchOption.state |= QStyle::State_Open;
0105 
0106         m_view->style()->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, painter, m_view);
0107 
0108         // draw text
0109         QRect textrect = QRect(r.left() + i * 2, r.top(), r.width() - ((5 * i) / 2), r.height());
0110         QString text = elidedText(option.fontMetrics, textrect.width(), Qt::ElideMiddle,
0111                                   model->data(index, Qt::DisplayRole).toString());
0112         m_view->style()->drawItemText(painter, textrect, Qt::AlignCenter,
0113                                       option.palette, m_view->isEnabled(), text);
0114 
0115     } else {
0116         QItemDelegate::paint(painter, option, index);
0117     }
0118 }
0119 
0120 QSize SheetDelegate::sizeHint(const QStyleOptionViewItem& opt, const QModelIndex& index) const
0121 {
0122     QSize sz = QItemDelegate::sizeHint(opt, index) + QSize(2, 2);
0123     return sz;
0124 }
0125 
0126 
0127 CollectionTreeWidget::CollectionTreeWidget(QWidget* parent): QTreeWidget(parent)
0128 {
0129     header()->hide();
0130     header()->setResizeMode(QHeaderView::Stretch);
0131     setRootIsDecorated(false);
0132     setItemsExpandable(true);
0133     setFocusPolicy(Qt::NoFocus);
0134     setIndentation(0);
0135     setColumnCount(1);
0136     setVerticalScrollMode(ScrollPerPixel);
0137     setAcceptDrops(true);
0138     setItemDelegate(new SheetDelegate(this, this));
0139 
0140     connect(this, SIGNAL(itemPressed(QTreeWidgetItem*,int)),
0141             this, SLOT(handleMousePress(QTreeWidgetItem*)));
0142 
0143     loadOptions();
0144 }
0145 
0146 CollectionTreeWidget::~CollectionTreeWidget()
0147 {
0148     saveOptions();
0149 }
0150 
0151 void CollectionTreeWidget::setFamilyMap(QMap<QString, CollectionItemModel*> map)
0152 {
0153     m_familyMap = map;
0154 }
0155 
0156 void CollectionTreeWidget::regenerateFilteredMap()
0157 {
0158     QMapIterator<QString, CollectionItemModel*> i(m_familyMap);
0159     while (i.hasNext()) {
0160         i.next();
0161         i.value()->setViewMode(m_viewMode);
0162         QSortFilterProxyModel* proxy = new QSortFilterProxyModel();
0163         proxy->setSourceModel(i.value());
0164         m_filteredMap.insert(i.key(), proxy);
0165     }
0166 
0167     //regenerate category view
0168     QMapIterator<QString, QSortFilterProxyModel*> j(m_filteredMap);
0169     while (j.hasNext()) {
0170         j.next();
0171         QTreeWidgetItem* category = new QTreeWidgetItem(this);
0172         category->setText(0, j.key());
0173         addStencilListView(category, m_viewMode, j.value());
0174     }
0175 }
0176 
0177 //Link a StencilListView to each TreeWidgetItem
0178 void CollectionTreeWidget::addStencilListView(QTreeWidgetItem* parent,
0179         QListView::ViewMode viewMode, QSortFilterProxyModel* model)
0180 {
0181     QTreeWidgetItem* embed_item = new QTreeWidgetItem(parent);
0182     embed_item->setFlags(Qt::ItemIsEnabled);
0183     StencilListView* categoryView = new StencilListView();
0184     categoryView->setViewMode(viewMode);
0185     categoryView->setModel(model);
0186     setItemWidget(embed_item, 0, categoryView);
0187 }
0188 
0189 StencilListView* CollectionTreeWidget::stencilListViewAt(int idx) const
0190 {
0191     StencilListView* rc = 0;
0192     if (QTreeWidgetItem* cat_item = topLevelItem(idx)) {
0193         if (QTreeWidgetItem* embedItem = cat_item->child(0)) {
0194             rc = qobject_cast<StencilListView*>(itemWidget(embedItem, 0));
0195         }
0196     }
0197     Q_ASSERT(rc);
0198     return rc;
0199 }
0200 
0201 void CollectionTreeWidget::saveOptions()
0202 {
0203     KConfigGroup group = KSharedConfig::openConfig()->group("Stencil Box");
0204     group.writeEntry("viewMode", (int)m_viewMode);
0205 }
0206 
0207 void  CollectionTreeWidget::loadOptions()
0208 {
0209     KConfigGroup group = KSharedConfig::openConfig()->group("Stencil Box");
0210     int viewMode = group.readEntry("viewMode", (int)QListView::IconMode);
0211     m_viewMode = (QListView::ViewMode)viewMode;
0212     updateViewMode();
0213 }
0214 
0215 void CollectionTreeWidget::handleMousePress(QTreeWidgetItem* item)
0216 {
0217     if (item && !item->parent() &&
0218         QApplication::mouseButtons() == Qt::LeftButton)
0219         setItemExpanded(item, !isItemExpanded(item));
0220 }
0221 
0222 void CollectionTreeWidget::slotListMode()
0223 {
0224     m_viewMode = QListView::ListMode;
0225     updateViewMode();
0226 }
0227 
0228 void CollectionTreeWidget::slotIconMode()
0229 {
0230     m_viewMode = QListView::IconMode;
0231     updateViewMode();
0232 }
0233 
0234 void CollectionTreeWidget::updateViewMode()
0235 {
0236     QMapIterator<QString, CollectionItemModel*> i(m_familyMap);
0237     while (i.hasNext()) {
0238         i.next();
0239         i.value()->setViewMode(m_viewMode);
0240     }
0241     if (const int numTopLevels = topLevelItemCount()) {
0242         for (int i = numTopLevels - 1; i >= 0; --i) {
0243             StencilListView* categoryView = stencilListViewAt(i);
0244 
0245             if (m_viewMode != categoryView->viewMode()) {
0246                 categoryView->setViewMode(m_viewMode);
0247                 categoryView->setMovement(QListView::Static);
0248                 categoryView->setDragDropMode(QAbstractItemView::DragDrop);
0249                 adjustStencilListSize(topLevelItem(i));
0250             }
0251         }
0252     }
0253     updateGeometries();
0254 }
0255 
0256 void CollectionTreeWidget::adjustStencilListSize(QTreeWidgetItem* cat_item)
0257 {
0258     QTreeWidgetItem* embedItem = cat_item->child(0);
0259     if (embedItem == 0)
0260         return;
0261 
0262     StencilListView* list_widget = static_cast<StencilListView*>(itemWidget(embedItem, 0));
0263     list_widget->setFixedWidth(header()->width());
0264     list_widget->doItemsLayout();
0265     const int height = qMax(list_widget->contentsSize().height() , 1);
0266     list_widget->setFixedHeight(height);
0267     embedItem->setSizeHint(0, QSize(-1, height - 1));
0268 }
0269 
0270 void CollectionTreeWidget::setFilter(QRegExp regExp)
0271 {
0272     QMapIterator<QString, QSortFilterProxyModel*> j(m_filteredMap);
0273     while (j.hasNext()) {
0274         j.next();
0275         j.value()->setFilterRegExp(regExp);
0276         j.value()->setFilterRole(Qt::UserRole + 1);
0277     }
0278     for (int i = 0; i < topLevelItemCount(); i++) {
0279         QTreeWidgetItem* tl = topLevelItem(i);
0280         StencilListView* categoryView = stencilListViewAt(i);
0281         QAbstractItemModel* model = categoryView->model();
0282         const bool categoryEnabled = model->rowCount() > 0;
0283         if (categoryView->model()->rowCount() > 0) {
0284             categoryView->adjustSize();
0285             adjustStencilListSize(tl);
0286         }
0287         setRowHidden(i, QModelIndex(), !categoryEnabled);
0288     }
0289     updateGeometries();
0290 }
0291 
0292 void CollectionTreeWidget::resizeEvent(QResizeEvent* e)
0293 {
0294     QTreeWidget::resizeEvent(e);
0295     if (const int numTopLevels = topLevelItemCount()) {
0296         for (int i = numTopLevels - 1; i >= 0; --i) {
0297             adjustStencilListSize(topLevelItem(i));
0298         }
0299     }
0300 }
0301 
0302 void CollectionTreeWidget::contextMenuEvent(QContextMenuEvent* e)
0303 {
0304     QMenu menu;
0305     menu.addAction(i18n("Expand all"), this, SLOT(expandAll()));
0306     menu.addAction(i18n("Collapse all"), this, SLOT(collapseAll()));
0307     menu.addSeparator();
0308 
0309     QAction* listModeAction = menu.addAction(i18n("List View"));
0310     QAction* iconModeAction = menu.addAction(i18n("Icon View"));
0311     listModeAction->setCheckable(true);
0312     iconModeAction->setCheckable(true);
0313     QActionGroup* viewModeGroup = new QActionGroup(&menu);
0314     viewModeGroup->addAction(listModeAction);
0315     viewModeGroup->addAction(iconModeAction);
0316     if (m_viewMode == QListView::IconMode) {
0317         iconModeAction->setChecked(true);
0318     } else {
0319         listModeAction->setChecked(true);
0320     }
0321     connect(listModeAction, SIGNAL(triggered()), SLOT(slotListMode()));
0322     connect(iconModeAction, SIGNAL(triggered()), SLOT(slotIconMode()));
0323 
0324     e->accept();
0325     menu.exec(mapToGlobal(e->pos()));
0326 }