Warning, file /office/calligra/libs/main/KoDocumentSectionView.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002   Copyright (c) 2006 Gábor Lehel <illissius@gmail.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 #include "KoDocumentSectionView.h"
0020 #include "KoDocumentSectionPropertyAction_p.h"
0021 #include "KoDocumentSectionDelegate.h"
0022 #include "KoDocumentSectionModel.h"
0023 
0024 #include <kconfig.h>
0025 #include <kconfiggroup.h>
0026 #include <kiconloader.h>
0027 #include <ksharedconfig.h>
0028 
0029 #include <QContextMenuEvent>
0030 #include <QHeaderView>
0031 #include <QHelpEvent>
0032 #include <QMenu>
0033 #include <QDrag>
0034 #include <QMouseEvent>
0035 #include <QPersistentModelIndex>
0036 #include <QApplication>
0037 #include <QPainter>
0038 #include <QScrollBar>
0039 
0040 #ifdef HAVE_X11
0041 #define DRAG_WHILE_DRAG_WORKAROUND
0042 #endif
0043 
0044 #ifdef DRAG_WHILE_DRAG_WORKAROUND
0045 #define DRAG_WHILE_DRAG_WORKAROUND_START() d->isDragging = true
0046 #define DRAG_WHILE_DRAG_WORKAROUND_STOP() d->isDragging = false
0047 #else
0048 #define DRAG_WHILE_DRAG_WORKAROUND_START()
0049 #define DRAG_WHILE_DRAG_WORKAROUND_STOP()
0050 #endif
0051 
0052 
0053 class Q_DECL_HIDDEN KoDocumentSectionView::Private
0054 {
0055 public:
0056     Private()
0057         : delegate(0)
0058         , mode(DetailedMode)
0059 #ifdef DRAG_WHILE_DRAG_WORKAROUND
0060         , isDragging(false)
0061 #endif
0062     {
0063         KSharedConfigPtr config =  KSharedConfig::openConfig();
0064         KConfigGroup group = config->group("DocumentSectionView");
0065         mode = (DisplayMode) group.readEntry("DocumentSectionViewMode", (int)DetailedMode);
0066     }
0067     KoDocumentSectionDelegate *delegate;
0068     DisplayMode mode;
0069     QPersistentModelIndex hovered;
0070     QPoint lastPos;
0071 
0072 #ifdef DRAG_WHILE_DRAG_WORKAROUND
0073     bool isDragging;
0074 #endif
0075 };
0076 
0077 KoDocumentSectionView::KoDocumentSectionView(QWidget *parent)
0078     : QTreeView(parent)
0079     , m_draggingFlag(false)
0080     , d(new Private)
0081 {
0082     d->delegate = new KoDocumentSectionDelegate(this, this);
0083     setMouseTracking(true);
0084     setVerticalScrollMode(ScrollPerPixel);
0085     setSelectionMode(SingleSelection);
0086     setSelectionBehavior(SelectItems);
0087     header()->hide();
0088     setDragEnabled(true);
0089     setDragDropMode(QAbstractItemView::DragDrop);
0090     setAcceptDrops(true);
0091     setDropIndicatorShown(true);
0092 }
0093 
0094 KoDocumentSectionView::~KoDocumentSectionView()
0095 {
0096     delete d;
0097 }
0098 
0099 void KoDocumentSectionView::setDisplayMode(DisplayMode mode)
0100 {
0101     if (d->mode != mode) {
0102         d->mode = mode;
0103         KSharedConfigPtr config =  KSharedConfig::openConfig();
0104         KConfigGroup group = config->group("DocumentSectionView");
0105         group.writeEntry("DocumentSectionViewMode", (int)mode);
0106         scheduleDelayedItemsLayout();
0107     }
0108 }
0109 
0110 KoDocumentSectionView::DisplayMode KoDocumentSectionView::displayMode() const
0111 {
0112     return d->mode;
0113 }
0114 
0115 void KoDocumentSectionView::addPropertyActions(QMenu *menu, const QModelIndex &index)
0116 {
0117     Model::PropertyList list = index.data(Model::PropertiesRole).value<Model::PropertyList>();
0118     for (int i = 0, n = list.count(); i < n; ++i) {
0119         if (list.at(i).isMutable) {
0120             PropertyAction *a = new PropertyAction(i, list.at(i), index, menu);
0121             connect(a, SIGNAL(toggled(bool,QPersistentModelIndex,int)),
0122                     this, SLOT(slotActionToggled(bool,QPersistentModelIndex,int)));
0123             menu->addAction(a);
0124         }
0125     }
0126 }
0127 
0128 bool KoDocumentSectionView::viewportEvent(QEvent *e)
0129 {
0130     if (model()) {
0131         switch(e->type()) {
0132         case QEvent::MouseButtonPress: {
0133             DRAG_WHILE_DRAG_WORKAROUND_STOP();
0134 
0135             const QPoint pos = static_cast<QMouseEvent*>(e)->pos();
0136             d->lastPos = pos;
0137             if (!indexAt(pos).isValid()) {
0138                 return QTreeView::viewportEvent(e);
0139             }
0140             QModelIndex index = model()->buddy(indexAt(pos));
0141             if (d->delegate->editorEvent(e, model(), optionForIndex(index), index)) {
0142                 return true;
0143             }
0144         } break;
0145         case QEvent::Leave: {
0146             QEvent e(QEvent::Leave);
0147             d->delegate->editorEvent(&e, model(), optionForIndex(d->hovered), d->hovered);
0148             d->hovered = QModelIndex();
0149         } break;
0150         case QEvent::MouseMove: {
0151 #ifdef DRAG_WHILE_DRAG_WORKAROUND
0152             if (d->isDragging) {
0153                 return false;
0154             }
0155 #endif
0156 
0157             const QPoint pos = static_cast<QMouseEvent*>(e)->pos();
0158             QModelIndex hovered = indexAt(pos);
0159             if (hovered != d->hovered) {
0160                 if (d->hovered.isValid()) {
0161                     QEvent e(QEvent::Leave);
0162                     d->delegate->editorEvent(&e, model(), optionForIndex(d->hovered), d->hovered);
0163                 }
0164                 if (hovered.isValid()) {
0165                     QEvent e(QEvent::Enter);
0166                     d->delegate->editorEvent(&e, model(), optionForIndex(hovered), hovered);
0167                 }
0168                 d->hovered = hovered;
0169             }
0170             /* This is a workaround for a bug in QTreeView that immediately begins a dragging action
0171             when the mouse lands on the decoration/icon of a different index and moves 1 pixel or more */
0172             Qt::MouseButtons buttons = static_cast<QMouseEvent*>(e)->buttons();
0173             if ((Qt::LeftButton | Qt::MidButton) & buttons) {
0174                 if ((pos - d->lastPos).manhattanLength() > qApp->startDragDistance()) {
0175                     return QTreeView::viewportEvent(e);
0176                 }
0177                 return true;
0178             }
0179         } break;
0180         case QEvent::ToolTip: {
0181             const QPoint pos = static_cast<QHelpEvent*>(e)->pos();
0182             if (!indexAt(pos).isValid()) {
0183                 return QTreeView::viewportEvent(e);
0184             }
0185             QModelIndex index = model()->buddy(indexAt(pos));
0186             return d->delegate->editorEvent(e, model(), optionForIndex(index), index);
0187         } break;
0188         case QEvent::Resize: {
0189             scheduleDelayedItemsLayout();
0190             break;
0191         }
0192         default: break;
0193         }
0194     }
0195     return QTreeView::viewportEvent(e);
0196 }
0197 
0198 void KoDocumentSectionView::contextMenuEvent(QContextMenuEvent *e)
0199 {
0200     QTreeView::contextMenuEvent(e);
0201     QModelIndex i = indexAt(e->pos());
0202     if (model())
0203         i = model()->buddy(i);
0204     showContextMenu(e->globalPos(), i);
0205 }
0206 
0207 void KoDocumentSectionView::showContextMenu(const QPoint &globalPos, const QModelIndex &index)
0208 {
0209     emit contextMenuRequested(globalPos, index);
0210 }
0211 
0212 void KoDocumentSectionView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
0213 {
0214     QTreeView::currentChanged(current, previous);
0215     if (current != previous /*&& current.isValid()*/) { //hack?
0216         Q_ASSERT(!current.isValid() || current.model() == model());
0217         model()->setData(current, true, Model::ActiveRole);
0218     }
0219 }
0220 
0221 void KoDocumentSectionView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
0222 {
0223     Q_UNUSED(roles);
0224     QTreeView::dataChanged(topLeft, bottomRight);
0225     for (int x = topLeft.row(); x <= bottomRight.row(); ++x) {
0226         for (int y = topLeft.column(); y <= bottomRight.column(); ++y) {
0227             if (topLeft.sibling(x, y).data(Model::ActiveRole).toBool()) {
0228                 setCurrentIndex(topLeft.sibling(x, y));
0229                 return;
0230             }
0231         }
0232     }
0233 }
0234 
0235 void KoDocumentSectionView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
0236 {
0237     QTreeView::selectionChanged(selected, deselected);
0238     emit selectionChanged(selectedIndexes());
0239 
0240 }
0241 
0242 void KoDocumentSectionView::slotActionToggled(bool on, const QPersistentModelIndex &index, int num)
0243 {
0244     Model::PropertyList list = index.data(Model::PropertiesRole).value<Model::PropertyList>();
0245     list[num].state = on;
0246     const_cast<QAbstractItemModel*>(index.model())->setData(index, QVariant::fromValue(list), Model::PropertiesRole);
0247 }
0248 
0249 QStyleOptionViewItem KoDocumentSectionView::optionForIndex(const QModelIndex &index) const
0250 {
0251     QStyleOptionViewItem option = viewOptions();
0252     option.rect = visualRect(index);
0253     if (index == currentIndex())
0254         option.state |= QStyle::State_HasFocus;
0255     return option;
0256 }
0257 
0258 void KoDocumentSectionView::startDrag(Qt::DropActions supportedActions)
0259 {
0260     DRAG_WHILE_DRAG_WORKAROUND_START();
0261 
0262     if (displayMode() == KoDocumentSectionView::ThumbnailMode) {
0263         const QModelIndexList indexes = selectionModel()->selectedIndexes();
0264         if (!indexes.isEmpty()) {
0265             QMimeData *data = model()->mimeData(indexes);
0266             if (!data) {
0267                 return;
0268             }
0269             QDrag *drag = new QDrag(this);
0270             drag->setPixmap(createDragPixmap());
0271             drag->setMimeData(data);
0272             //m_dragSource = this;
0273             drag->exec(supportedActions);
0274         }
0275     }
0276     else {
0277         QTreeView::startDrag(supportedActions);
0278     }
0279 }
0280 
0281 QPixmap KoDocumentSectionView::createDragPixmap() const
0282 {
0283     const QModelIndexList selectedIndexes = selectionModel()->selectedIndexes();
0284     Q_ASSERT(!selectedIndexes.isEmpty());
0285 
0286     const int itemCount = selectedIndexes.count();
0287 
0288     // If more than one item is dragged, align the items inside a
0289     // rectangular grid. The maximum grid size is limited to 4 x 4 items.
0290     int xCount = 2;
0291     int size = 96;
0292     if (itemCount > 9) {
0293         xCount = 4;
0294         size = KIconLoader::SizeLarge;
0295     }
0296     else if (itemCount > 4) {
0297         xCount = 3;
0298         size = KIconLoader::SizeHuge;
0299     }
0300     else if (itemCount < xCount) {
0301         xCount = itemCount;
0302     }
0303 
0304     int yCount = itemCount / xCount;
0305     if (itemCount % xCount != 0) {
0306         ++yCount;
0307     }
0308 
0309     if (yCount > xCount) {
0310         yCount = xCount;
0311     }
0312 
0313     // Draw the selected items into the grid cells
0314     QPixmap dragPixmap(xCount * size + xCount - 1, yCount * size + yCount - 1);
0315     dragPixmap.fill(Qt::transparent);
0316 
0317     QPainter painter(&dragPixmap);
0318     int x = 0;
0319     int y = 0;
0320     foreach (const QModelIndex &selectedIndex, selectedIndexes) {
0321         const QImage img = selectedIndex.data(int(Model::BeginThumbnailRole) + size).value<QImage>();
0322         painter.drawPixmap(x, y, QPixmap().fromImage(img.scaled(QSize(size, size), Qt::KeepAspectRatio)));
0323 
0324         x += size + 1;
0325         if (x >= dragPixmap.width()) {
0326             x = 0;
0327             y += size + 1;
0328         }
0329         if (y >= dragPixmap.height()) {
0330             break;
0331         }
0332     }
0333 
0334     return dragPixmap;
0335 }
0336 
0337 void KoDocumentSectionView::paintEvent(QPaintEvent *event)
0338 {
0339     event->accept();
0340     QTreeView::paintEvent(event);
0341 
0342     // Paint the line where the slide should go
0343     if (isDragging() && (displayMode() == KoDocumentSectionView::ThumbnailMode)) {
0344         QSize size(visualRect(model()->index(0, 0, QModelIndex())).width(), visualRect(model()->index(0, 0, QModelIndex())).height());
0345         int numberRow = cursorPageIndex();
0346         int scrollBarValue = verticalScrollBar()->value();
0347 
0348         QPoint point1(0, numberRow * size.height() - scrollBarValue);
0349         QPoint point2(size.width(), numberRow * size.height() - scrollBarValue);
0350         QLineF line(point1, point2);
0351 
0352         QPainter painter(this->viewport());
0353         QPen pen = QPen(palette().brush(QPalette::Highlight), 8);
0354         pen.setCapStyle(Qt::RoundCap);
0355         painter.setPen(pen);
0356         painter.setOpacity(0.8);
0357         painter.drawLine(line);
0358     }
0359 }
0360 
0361 void KoDocumentSectionView::dropEvent(QDropEvent *ev)
0362 {
0363     if (displayMode() == KoDocumentSectionView::ThumbnailMode) {
0364         setDraggingFlag(false);
0365         ev->accept();
0366         clearSelection();
0367 
0368         if (!model()) {
0369             return;
0370         }
0371 
0372         int newIndex = cursorPageIndex();
0373         model()->dropMimeData(ev->mimeData(), ev->dropAction(), newIndex, -1, QModelIndex());
0374         return;
0375     }
0376     QTreeView::dropEvent(ev);
0377 
0378     DRAG_WHILE_DRAG_WORKAROUND_STOP();
0379 }
0380 
0381 int KoDocumentSectionView::cursorPageIndex() const
0382 {
0383     QSize size(visualRect(model()->index(0, 0, QModelIndex())).width(), visualRect(model()->index(0, 0, QModelIndex())).height());
0384     int scrollBarValue = verticalScrollBar()->value();
0385 
0386     QPoint cursorPosition = QWidget::mapFromGlobal(QCursor::pos());
0387 
0388     int numberRow = (cursorPosition.y() + scrollBarValue) / size.height();
0389 
0390     //If cursor is at the half button of the page then the move action is performed after the slide, otherwise it is
0391     //performed before the page
0392     if (abs((cursorPosition.y() + scrollBarValue) - size.height()*numberRow) > (size.height()/2)) {
0393         numberRow++;
0394     }
0395 
0396     if (numberRow > model()->rowCount(QModelIndex())) {
0397         numberRow = model()->rowCount(QModelIndex());
0398     }
0399 
0400     return numberRow;
0401 }
0402 
0403 void KoDocumentSectionView::dragEnterEvent(QDragEnterEvent *ev)
0404 {
0405     DRAG_WHILE_DRAG_WORKAROUND_START();
0406     QTreeView::dragEnterEvent(ev);
0407 }
0408 
0409 void KoDocumentSectionView::dragMoveEvent(QDragMoveEvent *ev)
0410 {
0411     DRAG_WHILE_DRAG_WORKAROUND_START();
0412 
0413     if (displayMode() == KoDocumentSectionView::ThumbnailMode) {
0414         ev->accept();
0415         if (!model()) {
0416             return;
0417         }
0418         QTreeView::dragMoveEvent(ev);
0419         setDraggingFlag();
0420         viewport()->update();
0421         return;
0422     }
0423     QTreeView::dragMoveEvent(ev);
0424 }
0425 
0426 void KoDocumentSectionView::dragLeaveEvent(QDragLeaveEvent *e)
0427 {
0428     if (displayMode() == KoDocumentSectionView::ThumbnailMode) {
0429         setDraggingFlag(false);
0430     } else {
0431         QTreeView::dragLeaveEvent(e);
0432     }
0433 
0434     DRAG_WHILE_DRAG_WORKAROUND_STOP();
0435 }
0436 
0437 bool KoDocumentSectionView::isDragging() const
0438 {
0439     return m_draggingFlag;
0440 }
0441 
0442 void KoDocumentSectionView::setDraggingFlag(bool flag)
0443 {
0444     m_draggingFlag = flag;
0445 }