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 ¤t, 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 }