Warning, file /office/calligra/libs/main/KoDocumentSectionDelegate.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 Copyright (c) 2008 Cyrille Berger <cberger@cberger.net> 0004 Copyright (c) 2011 José Luis Vergara <pentalis@gmail.com> 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 Boston, MA 02110-1301, USA. 0020 */ 0021 #include "KoDocumentSectionDelegate.h" 0022 #include "KoDocumentSectionModel.h" 0023 #include "KoDocumentSectionToolTip.h" 0024 #include "KoDocumentSectionView.h" 0025 0026 #include <QApplication> 0027 #include <QKeyEvent> 0028 #include <QLineEdit> 0029 #include <QModelIndex> 0030 #include <QMouseEvent> 0031 #include <QPainter> 0032 #include <QPointer> 0033 #include <QStyle> 0034 #include <QStyleOptionViewItem> 0035 0036 #include <klocalizedstring.h> 0037 0038 class KoDocumentSectionDelegate::Private 0039 { 0040 public: 0041 Private() : view(0), edit(0) {} 0042 0043 KoDocumentSectionView *view; 0044 QPointer<QWidget> edit; 0045 KoDocumentSectionToolTip tip; 0046 static const int margin = 1; 0047 }; 0048 0049 KoDocumentSectionDelegate::KoDocumentSectionDelegate(KoDocumentSectionView *view, QObject *parent) 0050 : QAbstractItemDelegate(parent) 0051 , d(new Private) 0052 { 0053 d->view = view; 0054 view->setItemDelegate(this); 0055 QApplication::instance()->installEventFilter(this); 0056 } 0057 0058 KoDocumentSectionDelegate::~KoDocumentSectionDelegate() 0059 { 0060 delete d; 0061 } 0062 0063 QSize KoDocumentSectionDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 0064 { 0065 switch(d->view->displayMode()) { 0066 case View::ThumbnailMode: { 0067 const int height = thumbnailHeight(option, index) + textBoxHeight(option) + d->margin * 2; 0068 return QSize(availableWidth(), height); 0069 } 0070 case View::DetailedMode: 0071 return QSize(option.rect.width(), 0072 textBoxHeight(option) + option.decorationSize.height() + d->margin); 0073 case View::MinimalMode: 0074 return QSize(option.rect.width(), textBoxHeight(option)); 0075 default: 0076 return option.rect.size(); 0077 } 0078 } 0079 0080 void KoDocumentSectionDelegate::paint(QPainter *p, const QStyleOptionViewItem &o, const QModelIndex &index) const 0081 { 0082 p->save(); 0083 { 0084 QStyleOptionViewItem option = getOptions(o, index); 0085 QStyle *style = option.widget ? option.widget->style() : QApplication::style(); 0086 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget); 0087 0088 p->setFont(option.font); 0089 0090 drawText(p, option, index); 0091 drawIcons(p, option, index); 0092 drawThumbnail(p, option, index); 0093 drawDecoration(p, option, index); 0094 drawProgressBar(p, option, index); 0095 } 0096 p->restore(); 0097 } 0098 0099 bool KoDocumentSectionDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) 0100 { 0101 if ((event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) 0102 && (index.flags() & Qt::ItemIsEnabled)) 0103 { 0104 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); 0105 0106 const QRect iconsRect_ = iconsRect(option, index).translated(option.rect.topLeft()); 0107 0108 if (iconsRect_.isValid() && iconsRect_.contains(mouseEvent->pos())) { 0109 const int iconWidth = option.decorationSize.width(); 0110 int xPos = mouseEvent->pos().x() - iconsRect_.left(); 0111 if (xPos % (iconWidth + d->margin) < iconWidth) { //it's on an icon, not a margin 0112 Model::PropertyList propertyList = index.data(Model::PropertiesRole).value<Model::PropertyList>(); 0113 int clickedProperty = -1; 0114 // Discover which of all properties was clicked 0115 for (int i = 0; i < propertyList.count(); ++i) { 0116 if (propertyList[i].isMutable) { 0117 xPos -= iconWidth + d->margin; 0118 } 0119 ++clickedProperty; 0120 if (xPos < 0) break; 0121 } 0122 // Using Ctrl+click to enter stasis 0123 if (mouseEvent->modifiers() == Qt::ControlModifier 0124 && propertyList[clickedProperty].canHaveStasis) { 0125 // STEP 0: Prepare to Enter or Leave control key stasis 0126 quint16 numberOfLeaves = model->rowCount(index.parent()); 0127 QModelIndex eachItem; 0128 // STEP 1: Go. 0129 if (propertyList[clickedProperty].isInStasis == false) { // Enter 0130 /* Make every leaf of this node go State = False, saving the old property value to stateInStasis */ 0131 for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent()) 0132 eachItem = model->index(i, 0, index.parent()); 0133 // The entire property list has to be altered because model->setData cannot set individual properties 0134 Model::PropertyList eachPropertyList = eachItem.data(Model::PropertiesRole).value<Model::PropertyList>(); 0135 eachPropertyList[clickedProperty].stateInStasis = eachPropertyList[clickedProperty].state.toBool(); 0136 eachPropertyList[clickedProperty].state = false; 0137 eachPropertyList[clickedProperty].isInStasis = true; 0138 model->setData(eachItem, QVariant::fromValue(eachPropertyList), Model::PropertiesRole); 0139 } 0140 /* Now set the current node's clickedProperty back to True, to save the user time 0141 (obviously, if the user is clicking one item with ctrl+click, that item should 0142 have a True property, value while the others are in stasis and set to False) */ 0143 // First refresh propertyList, otherwise old data will be saved back causing bugs 0144 propertyList = index.data(Model::PropertiesRole).value<Model::PropertyList>(); 0145 propertyList[clickedProperty].state = true; 0146 model->setData(index, QVariant::fromValue(propertyList), Model::PropertiesRole); 0147 } else { // Leave 0148 /* Make every leaf of this node go State = stateInStasis */ 0149 for (quint16 i = 0; i < numberOfLeaves; ++i) { 0150 eachItem = model->index(i, 0, index.parent()); 0151 // The entire property list has to be altered because model->setData cannot set individual properties 0152 Model::PropertyList eachPropertyList = eachItem.data(Model::PropertiesRole).value<Model::PropertyList>(); 0153 eachPropertyList[clickedProperty].state = eachPropertyList[clickedProperty].stateInStasis; 0154 eachPropertyList[clickedProperty].isInStasis = false; 0155 model->setData(eachItem, QVariant::fromValue(eachPropertyList), Model::PropertiesRole); 0156 } 0157 } 0158 } else { 0159 propertyList[clickedProperty].state = !propertyList[clickedProperty].state.toBool(); 0160 model->setData(index, QVariant::fromValue(propertyList), Model::PropertiesRole); 0161 } 0162 } 0163 return true; 0164 } 0165 0166 if (mouseEvent->button() == Qt::LeftButton && 0167 mouseEvent->modifiers() == Qt::AltModifier) { 0168 0169 d->view->setCurrentIndex(index); 0170 model->setData(index, true, Model::AlternateActiveRole); 0171 return true; 0172 } 0173 0174 if (mouseEvent->button() != Qt::LeftButton) { 0175 d->view->setCurrentIndex(index); 0176 return false; 0177 } 0178 } 0179 else if (event->type() == QEvent::ToolTip) { 0180 QHelpEvent *helpEvent = static_cast<QHelpEvent*>(event); 0181 d->tip.showTip(d->view, helpEvent->pos(), option, index); 0182 return true; 0183 } else if (event->type() == QEvent::Leave) { 0184 d->tip.hide(); 0185 } 0186 0187 return false; 0188 } 0189 0190 QWidget *KoDocumentSectionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex&) const 0191 { 0192 d->edit = new QLineEdit(parent); 0193 d->edit->installEventFilter(const_cast<KoDocumentSectionDelegate*>(this)); //hack? 0194 return d->edit; 0195 } 0196 0197 void KoDocumentSectionDelegate::setEditorData(QWidget *widget, const QModelIndex &index) const 0198 { 0199 QLineEdit *edit = qobject_cast<QLineEdit*>(widget); 0200 Q_ASSERT(edit); 0201 0202 edit->setText(index.data(Qt::DisplayRole).toString()); 0203 } 0204 0205 void KoDocumentSectionDelegate::setModelData(QWidget *widget, QAbstractItemModel *model, const QModelIndex &index) const 0206 { 0207 QLineEdit *edit = qobject_cast<QLineEdit*>(widget); 0208 Q_ASSERT(edit); 0209 0210 model->setData(index, edit->text(), Qt::DisplayRole); 0211 } 0212 0213 void KoDocumentSectionDelegate::updateEditorGeometry(QWidget *widget, const QStyleOptionViewItem &option, const QModelIndex &index) const 0214 { 0215 widget->setGeometry(textRect(option, index).translated(option.rect.topLeft())); 0216 } 0217 0218 0219 // PROTECTED 0220 0221 0222 bool KoDocumentSectionDelegate::eventFilter(QObject *object, QEvent *event) 0223 { 0224 switch (event->type()) { 0225 case QEvent::MouseButtonPress: { 0226 if (d->edit) { 0227 QMouseEvent *me = static_cast<QMouseEvent*>(event); 0228 if (!QRect(d->edit->mapToGlobal(QPoint()), d->edit->size()).contains(me->globalPos())) 0229 emit closeEditor(d->edit); 0230 } 0231 } break; 0232 case QEvent::KeyPress: { 0233 QLineEdit *edit = qobject_cast<QLineEdit*>(object); 0234 if (edit && edit == d->edit) { 0235 QKeyEvent *ke = static_cast<QKeyEvent*>(event); 0236 switch (ke->key()) { 0237 case Qt::Key_Escape: 0238 emit closeEditor(edit); 0239 return true; 0240 case Qt::Key_Tab: 0241 emit commitData(edit); 0242 emit closeEditor(edit,EditNextItem); 0243 return true; 0244 case Qt::Key_Backtab: 0245 emit commitData(edit); 0246 emit closeEditor(edit, EditPreviousItem); 0247 return true; 0248 case Qt::Key_Return: 0249 case Qt::Key_Enter: 0250 emit commitData(edit); 0251 emit closeEditor(edit); 0252 return true; 0253 default: break; 0254 } 0255 } 0256 } break; 0257 case QEvent::FocusOut : { 0258 QLineEdit *edit = qobject_cast<QLineEdit*>(object); 0259 if (edit && edit == d->edit) { 0260 emit commitData(edit); 0261 emit closeEditor(edit); 0262 } 0263 } 0264 default: break; 0265 } 0266 0267 return QAbstractItemDelegate::eventFilter(object, event); 0268 } 0269 0270 0271 // PRIVATE 0272 0273 0274 QStyleOptionViewItem KoDocumentSectionDelegate::getOptions(const QStyleOptionViewItem &o, const QModelIndex &index) 0275 { 0276 QStyleOptionViewItem option = o; 0277 QVariant v = index.data(Qt::FontRole); 0278 if (v.isValid()) { 0279 option.font = v.value<QFont>(); 0280 option.fontMetrics = QFontMetrics(option.font); 0281 } 0282 v = index.data(Qt::TextAlignmentRole); 0283 if (v.isValid()) 0284 option.displayAlignment = QFlag(v.toInt()); 0285 v = index.data(Qt::TextColorRole); 0286 if (v.isValid()) 0287 option.palette.setColor(QPalette::Text, v.value<QColor>()); 0288 v = index.data(Qt::BackgroundColorRole); 0289 if (v.isValid()) 0290 option.palette.setColor(QPalette::Window, v.value<QColor>()); 0291 0292 return option; 0293 } 0294 0295 int KoDocumentSectionDelegate::thumbnailHeight(const QStyleOptionViewItem &option, const QModelIndex &index) const 0296 { 0297 const QSize size = index.data(Qt::SizeHintRole).toSize(); 0298 int width = option.rect.width(); 0299 if (!option.rect.isValid()) 0300 width = availableWidth(); 0301 if (size.width() <= width) 0302 return size.height(); 0303 else 0304 return int(width / (qreal(size.width()) / size.height())); 0305 } 0306 0307 int KoDocumentSectionDelegate::availableWidth() const 0308 { 0309 return d->view->width(); // not viewport()->width(), otherwise we get infinite scrollbar addition/removal! 0310 } 0311 0312 int KoDocumentSectionDelegate::textBoxHeight(const QStyleOptionViewItem &option) const 0313 { 0314 return qMax(option.fontMetrics.height(), option.decorationSize.height()); 0315 } 0316 0317 QRect KoDocumentSectionDelegate::textRect(const QStyleOptionViewItem &option, const QModelIndex &index) const 0318 { 0319 if (d->view->displayMode() == View::ThumbnailMode) { 0320 const QRect r = decorationRect(option, index); 0321 const int left = r.right() + d->margin; 0322 return QRect(left, r.top(), option.rect.width() - left, textBoxHeight(option)); 0323 } else { 0324 static QFont f; 0325 static int minbearing = 1337 + 666; //can be 0 or negative, 2003 is less likely 0326 if (minbearing == 2003 || f != option.font) { 0327 f = option.font; //getting your bearings can be expensive, so we cache them 0328 minbearing = option.fontMetrics.minLeftBearing() + option.fontMetrics.minRightBearing(); 0329 } 0330 0331 int indent = decorationRect(option, index).right() + d->margin; 0332 0333 const int width = (d->view->displayMode() == View::DetailedMode 0334 ? option.rect.width() 0335 : iconsRect(option, index).left()) 0336 - indent - d->margin + minbearing; 0337 0338 return QRect(indent, 0, width, textBoxHeight(option)); 0339 } 0340 } 0341 0342 QRect KoDocumentSectionDelegate::iconsRect(const QStyleOptionViewItem &option, const QModelIndex &index) const 0343 { 0344 if (d->view->displayMode() == View::ThumbnailMode) 0345 return QRect(); 0346 0347 Model::PropertyList lp = index.data(Model::PropertiesRole).value<Model::PropertyList>(); 0348 int propscount = 0; 0349 for (int i = 0, n = lp.count(); i < n; ++i) 0350 if (lp[i].isMutable) 0351 propscount++; 0352 0353 const int iconswidth = propscount * option.decorationSize.width() + (propscount - 1) * d->margin; 0354 0355 const int x = d->view->displayMode() == View::DetailedMode ? thumbnailRect(option, index).right() + d->margin : option.rect.width() - iconswidth; 0356 const int y = d->view->displayMode() == View::DetailedMode ? textBoxHeight(option) + d->margin : 0; 0357 0358 return QRect(x, y, iconswidth, option.decorationSize.height()); 0359 } 0360 0361 QRect KoDocumentSectionDelegate::thumbnailRect(const QStyleOptionViewItem &option, const QModelIndex &index) const 0362 { 0363 if (d->view->displayMode() == View::ThumbnailMode) 0364 return QRect(0, 0, option.rect.width(), thumbnailHeight(option, index)); 0365 else 0366 return QRect(0, 0, option.rect.height(), option.rect.height()); 0367 } 0368 0369 QRect KoDocumentSectionDelegate::decorationRect(const QStyleOptionViewItem &option, const QModelIndex &index) const 0370 { 0371 int width = option.decorationSize.width(); 0372 if (index.data(Qt::DecorationRole).value<QIcon>().isNull()) 0373 width = 0; 0374 switch(d->view->displayMode()) { 0375 case View::ThumbnailMode: { 0376 QFont font = option.font; 0377 if (index.data(Model::ActiveRole).toBool()) 0378 font.setBold(!font.bold()); 0379 const QFontMetrics metrics(font); 0380 const int totalwidth = metrics.width(index.data(Qt::DisplayRole).toString()) + width + d->margin; 0381 int left; 0382 if (totalwidth < option.rect.width()) 0383 left = (option.rect.width() - totalwidth) / 2; 0384 else 0385 left = 0; 0386 return QRect(left, thumbnailRect(option, index).bottom() + d->margin, width, textBoxHeight(option)); 0387 } 0388 case View::DetailedMode: 0389 case View::MinimalMode: { 0390 const int left = thumbnailRect(option, index).right() + d->margin; 0391 return QRect(left, 0, width, textBoxHeight(option)); 0392 } 0393 default: return QRect(); 0394 } 0395 } 0396 0397 QRect KoDocumentSectionDelegate::progressBarRect(const QStyleOptionViewItem &option, const QModelIndex &index) const 0398 { 0399 if (d->view->displayMode() == View::ThumbnailMode) 0400 return QRect(); 0401 QRect iconsRect_ = iconsRect(option, index); 0402 int width = d->view->width() / 4; 0403 if (d->view->displayMode() == View::DetailedMode) { 0404 // In detailed mode the progress bar take 50% width on the right of the icons 0405 return QRect(option.rect.width() - width - d->margin, iconsRect_.top(), width, iconsRect_.height()) ; 0406 } else { 0407 // In minimal mode the progress bar take 50% width on the left of icons 0408 return QRect(iconsRect_.left() - width - d->margin , iconsRect_.top(), 0409 width, iconsRect_.height()); 0410 } 0411 } 0412 0413 void KoDocumentSectionDelegate::drawText(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const 0414 { 0415 const QRect r = textRect(option, index).translated(option.rect.topLeft()); 0416 0417 p->save(); 0418 { 0419 p->setClipRect(r); 0420 p->translate(r.left(), r.top()); 0421 QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Active : QPalette::Disabled; 0422 QPalette::ColorRole cr = (option.state & QStyle::State_Selected) ? QPalette::HighlightedText : QPalette::Text; 0423 p->setPen(option.palette.color(cg, cr)); 0424 0425 if (index.data(Model::ActiveRole).toBool()) { 0426 QFont f = p->font(); 0427 f.setBold(!f.bold()); 0428 p->setFont(f); 0429 } 0430 0431 const QString text = index.data(Qt::DisplayRole).toString(); 0432 const QString elided = elidedText(p->fontMetrics(), r.width(), Qt::ElideRight, text); 0433 p->drawText(d->margin, 0, r.width(), r.height(), Qt::AlignLeft | Qt::AlignTop, elided); 0434 } 0435 p->restore(); 0436 } 0437 0438 void KoDocumentSectionDelegate::drawIcons(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const 0439 { 0440 const QRect r = iconsRect(option, index).translated(option.rect.topLeft()); 0441 0442 p->save(); 0443 { 0444 p->setClipRect(r); 0445 p->translate(r.left(), r.top()); 0446 int x = 0; 0447 Model::PropertyList lp = index.data(Model::PropertiesRole).value<Model::PropertyList>(); 0448 for(int i = 0, n = lp.count(); i < n; ++i) { 0449 if (lp[i].isMutable) { 0450 QIcon icon = lp[i].state.toBool() ? lp[i].onIcon : lp[i].offIcon; 0451 p->drawPixmap(x, 0, icon.pixmap(option.decorationSize, (option.state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled)); 0452 x += option.decorationSize.width() + d->margin; 0453 } 0454 } 0455 } 0456 p->restore(); 0457 } 0458 0459 void KoDocumentSectionDelegate::drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const 0460 { 0461 const QRect r = thumbnailRect(option, index).translated(option.rect.topLeft()); 0462 0463 p->save(); 0464 { 0465 p->setClipRect(r); 0466 const qreal myratio = qreal(r.width()) / r.height(); 0467 const qreal thumbratio = index.data(Model::AspectRatioRole).toDouble(); 0468 const int s = (myratio > thumbratio) ? r.height() : r.width(); 0469 0470 QImage img = index.data(int(Model::BeginThumbnailRole) + s).value<QImage>(); 0471 if (!(option.state & QStyle::State_Enabled)) { 0472 // Make the image grayscale 0473 // TODO: if someone feel bored a more optimized version of this would be welcome 0474 for(int i = 0; i < img.width(); ++i) { 0475 for(int j = 0; j < img.width(); ++j) { 0476 img.setPixel(i, j, qGray(img.pixel(i,j))); 0477 } 0478 } 0479 } 0480 QPoint offset; 0481 offset.setX(r.width()/2 - img.width()/2); 0482 offset.setY(r.height()/2 - img.height()/2); 0483 0484 if (!img.isNull() && img.width() > 0 && img.height() > 0) { 0485 p->drawImage(r.topLeft() + offset, img); 0486 } 0487 } 0488 p->restore(); 0489 } 0490 0491 void KoDocumentSectionDelegate::drawDecoration(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const 0492 { 0493 const QRect r = decorationRect(option, index).translated(option.rect.topLeft()); 0494 0495 p->save(); 0496 { 0497 p->setClipRect(r); 0498 p->translate(r.topLeft()); 0499 if (!index.data(Qt::DecorationRole).value<QIcon>().isNull()) 0500 p->drawPixmap(0, 0, index.data(Qt::DecorationRole).value<QIcon>().pixmap(option.decorationSize, (option.state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled)); 0501 } 0502 p->restore(); 0503 } 0504 0505 void KoDocumentSectionDelegate::drawProgressBar(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const 0506 { 0507 QVariant value = index.data(KoDocumentSectionModel::ProgressRole); 0508 if (!value.isNull() && (value.toInt() >= 0 && value.toInt() <= 100)) { 0509 const QRect r = progressBarRect(option, index).translated(option.rect.topLeft()); 0510 p->save(); 0511 { 0512 p->setClipRect(r); 0513 QStyle* style = QApplication::style(); 0514 QStyleOptionProgressBar opt; 0515 0516 opt.minimum = 0; 0517 opt.maximum = 100; 0518 opt.progress = value.toInt(); 0519 opt.textVisible = true; 0520 opt.textAlignment = Qt::AlignHCenter; 0521 opt.text = i18n("%1 %", opt.progress); 0522 opt.rect = r; 0523 opt.orientation = Qt::Horizontal; 0524 opt.state = option.state; 0525 style->drawControl(QStyle::CE_ProgressBar, &opt, p, 0); 0526 } 0527 p->restore(); 0528 } 0529 }