00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kextendableitemdelegate.h"
00022
00023 #include <QModelIndex>
00024 #include <QScrollBar>
00025 #include <QTreeView>
00026 #include <QPainter>
00027 #include <QApplication>
00028
00029
00030 class KExtendableItemDelegate::Private {
00031 public:
00032 Private(KExtendableItemDelegate *parent) :
00033 q(parent),
00034 stateTick(0),
00035 hasExtenders(false)
00036 {}
00037
00038 void _k_extenderDestructionHandler(QObject *destroyed);
00039 void _k_verticalScroll();
00040
00041 QSize maybeExtendedSize(const QStyleOptionViewItem &option, const QModelIndex &index) const;
00042 QModelIndex indexOfExtendedColumnInSameRow(const QModelIndex &index) const;
00043 void scheduleUpdateViewLayout();
00044
00045 KExtendableItemDelegate *q;
00046
00050 void deleteExtenders();
00051
00052
00053
00054 QHash<QPersistentModelIndex, QWidget *> extenders;
00055 QHash<QWidget *, QPersistentModelIndex> extenderIndices;
00056 QPixmap extendPixmap;
00057 QPixmap contractPixmap;
00058 int stateTick;
00059
00060
00061 bool hasExtenders;
00062 };
00063
00064
00065 KExtendableItemDelegate::KExtendableItemDelegate(QAbstractItemView* parent)
00066 : QStyledItemDelegate(parent),
00067 d(new Private(this))
00068 {
00069 connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)),
00070 this, SLOT(_k_verticalScroll()));
00071 }
00072
00073
00074 KExtendableItemDelegate::~KExtendableItemDelegate()
00075 {
00076 delete d;
00077 }
00078
00079
00080 void KExtendableItemDelegate::extendItem(QWidget *ext, const QModelIndex &index)
00081 {
00082
00083
00084 if (!ext || !index.isValid())
00085 return;
00086
00087 d->stateTick++;
00088 contractItem(d->indexOfExtendedColumnInSameRow(index));
00089 d->stateTick++;
00090
00091 QAbstractItemView *aiv = qobject_cast<QAbstractItemView *>(parent());
00092 if (!aiv)
00093 return;
00094 ext->setParent(aiv->viewport());
00095 d->extenders.insert(index, ext);
00096 d->extenderIndices.insert(ext, index);
00097 d->hasExtenders = true;
00098 connect(ext, SIGNAL(destroyed(QObject *)), this, SLOT(_k_extenderDestructionHandler(QObject *)));
00099 emit extenderCreated(ext, index);
00100 d->scheduleUpdateViewLayout();
00101 }
00102
00103
00104 void KExtendableItemDelegate::contractItem(const QModelIndex& index)
00105 {
00106 QWidget *extender = d->extenders.value(index);
00107 if (!extender)
00108 return;
00109
00110 extender->hide();
00111 extender->deleteLater();
00112
00113 d->scheduleUpdateViewLayout();
00114 }
00115
00116
00117 void KExtendableItemDelegate::contractAll()
00118 {
00119 d->deleteExtenders();
00120 }
00121
00122
00123
00124 void KExtendableItemDelegate::Private::_k_extenderDestructionHandler(QObject *destroyed)
00125 {
00126
00127
00128 QWidget *extender = static_cast<QWidget *>(destroyed);
00129 stateTick++;
00130
00131 if (extenderIndices.value(extender).isValid() &&
00132 q->receivers(SIGNAL(extenderDestroyed(QWidget *, QModelIndex)))) {
00133 QPersistentModelIndex persistentIndex = extenderIndices.take(extender);
00134 QModelIndex index = persistentIndex;
00135 emit q->extenderDestroyed(extender, index);
00136 extenders.remove(persistentIndex);
00137 } else
00138 extenders.remove(extenderIndices.take(extender));
00139
00140 if (extenders.isEmpty())
00141 hasExtenders = false;
00142
00143 scheduleUpdateViewLayout();
00144 }
00145
00146
00147
00148 void KExtendableItemDelegate::Private::_k_verticalScroll()
00149 {
00150 foreach (QWidget *extender, extenders) {
00151
00152
00153
00154
00155
00156
00157
00158 extender->hide();
00159 }
00160 }
00161
00162
00163 bool KExtendableItemDelegate::isExtended(const QModelIndex &index) const
00164 {
00165 return d->extenders.value(index);
00166 }
00167
00168
00169 QSize KExtendableItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
00170 {
00171 QSize ret;
00172
00173 if (d->hasExtenders)
00174 ret = d->maybeExtendedSize(option, index);
00175 else
00176 ret = QStyledItemDelegate::sizeHint(option, index);
00177
00178 bool showExtensionIndicator = index.model() ?
00179 index.model()->data(index, ShowExtensionIndicatorRole).toBool() : false;
00180 if (showExtensionIndicator)
00181 ret.rwidth() += d->extendPixmap.width();
00182
00183 return ret;
00184 }
00185
00186
00187 void KExtendableItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
00188 {
00189 int indicatorX = 0;
00190 int indicatorY = 0;
00191
00192 QStyleOptionViewItemV4 indicatorOption(option);
00193 initStyleOption(&indicatorOption, index);
00194 if (index.column() == 0)
00195 indicatorOption.viewItemPosition = QStyleOptionViewItemV4::Beginning;
00196 else if (index.column() == index.model()->columnCount() - 1)
00197 indicatorOption.viewItemPosition = QStyleOptionViewItemV4::End;
00198 else
00199 indicatorOption.viewItemPosition = QStyleOptionViewItemV4::Middle;
00200
00201 QStyleOptionViewItemV4 itemOption(option);
00202 initStyleOption(&itemOption, index);
00203 if (index.column() == 0)
00204 itemOption.viewItemPosition = QStyleOptionViewItemV4::Beginning;
00205 else if (index.column() == index.model()->columnCount() - 1)
00206 itemOption.viewItemPosition = QStyleOptionViewItemV4::End;
00207 else
00208 itemOption.viewItemPosition = QStyleOptionViewItemV4::Middle;
00209
00210 const bool showExtensionIndicator = index.model()->data(index, ShowExtensionIndicatorRole).toBool();
00211
00212 if (showExtensionIndicator) {
00213 if (QApplication::isRightToLeft()) {
00214 indicatorX = option.rect.right() - d->extendPixmap.width();
00215 itemOption.rect.setRight(option.rect.right() - d->extendPixmap.width());
00216 indicatorOption.rect.setLeft(option.rect.right() - d->extendPixmap.width());
00217 } else {
00218 indicatorX = option.rect.left();
00219 indicatorOption.rect.setRight(option.rect.left() + d->extendPixmap.width());
00220 itemOption.rect.setLeft(option.rect.left() + d->extendPixmap.width());
00221 }
00222 indicatorY = option.rect.top() + ((option.rect.height() - d->extendPixmap.height()) >> 1);
00223 }
00224
00225
00226 if (!d->hasExtenders) {
00227 QStyledItemDelegate::paint(painter, itemOption, index);
00228 if (showExtensionIndicator) {
00229 painter->save();
00230 QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &indicatorOption,
00231 painter);
00232 painter->restore();
00233 painter->drawPixmap(indicatorX, indicatorY, d->extendPixmap);
00234 }
00235 return;
00236 }
00237
00238
00239 static int cachedStateTick = -1;
00240 static int cachedRow = -20;
00241 static QModelIndex cachedParentIndex;
00242 static QWidget *extender = 0;
00243 static int extenderHeight;
00244 int row = index.row();
00245 QModelIndex parentIndex = index.parent();
00246
00247 if (row != cachedRow || cachedStateTick != d->stateTick
00248 || cachedParentIndex != parentIndex) {
00249 extender = d->extenders.value(d->indexOfExtendedColumnInSameRow(index));
00250 cachedStateTick = d->stateTick;
00251 cachedRow = row;
00252 cachedParentIndex = parentIndex;
00253 if (extender) {
00254 extenderHeight = extender->sizeHint().height();
00255 }
00256 }
00257
00258 if (!extender) {
00259 QStyledItemDelegate::paint(painter, itemOption, index);
00260 if (showExtensionIndicator) {
00261 painter->save();
00262 QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &indicatorOption,
00263 painter);
00264 painter->restore();
00265 painter->drawPixmap(indicatorX, indicatorY, d->extendPixmap);
00266 }
00267 return;
00268 }
00269
00270
00271 if (isExtended(index)) {
00272 QStyleOptionViewItemV4 extOption(option);
00273 initStyleOption(&extOption, index);
00274 extOption.rect = extenderRect(extender, option, index);
00275 updateExtenderGeometry(extender, extOption, index);
00276
00277
00278 extender->show();
00279 }
00280
00281 indicatorOption.rect.setHeight(option.rect.height() - extenderHeight);
00282 itemOption.rect.setHeight(option.rect.height() - extenderHeight);
00283
00284
00285
00286 QStyledItemDelegate::paint(painter, itemOption, index);
00287
00288 if (showExtensionIndicator) {
00289
00290 indicatorY = indicatorOption.rect.top() + ((indicatorOption.rect.height() - d->extendPixmap.height()) >> 1);
00291 painter->save();
00292 QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &indicatorOption,
00293 painter);
00294 painter->restore();
00295
00296 if (d->extenders.contains(index))
00297 painter->drawPixmap(indicatorX, indicatorY, d->contractPixmap);
00298 else
00299 painter->drawPixmap(indicatorX, indicatorY, d->extendPixmap);
00300 }
00301 }
00302
00303
00304 QRect KExtendableItemDelegate::extenderRect(QWidget *extender, const QStyleOptionViewItem &option, const QModelIndex &index) const
00305 {
00306 Q_ASSERT(extender);
00307 QRect rect(option.rect);
00308 rect.setTop(rect.bottom() + 1 - extender->sizeHint().height());
00309
00310 rect.setLeft(0);
00311 QTreeView *tv = qobject_cast<QTreeView *>(parent());
00312 if (tv)
00313 for (QModelIndex idx(index.parent()); idx.isValid(); idx = idx.parent())
00314 rect.translate(tv->indentation(), 0);
00315
00316 QAbstractScrollArea *container = qobject_cast<QAbstractScrollArea *>(parent());
00317 Q_ASSERT(container);
00318 rect.setRight(container->viewport()->width() - 1);
00319 return rect;
00320 }
00321
00322
00323 QSize KExtendableItemDelegate::Private::maybeExtendedSize(const QStyleOptionViewItem &option, const QModelIndex &index) const
00324 {
00325 QWidget *extender = extenders.value(index);
00326 QSize size(q->QStyledItemDelegate::sizeHint(option, index));
00327 if (!extender)
00328 return size;
00329
00330
00331 int itemHeight = size.height();
00332
00333 int row = index.row();
00334 int thisColumn = index.column();
00335
00336
00337 for (int column = 0; index.model()->columnCount() < column; column++) {
00338 if (column == thisColumn)
00339 continue;
00340
00341 QModelIndex neighborIndex(index.sibling(row, column));
00342 if (!neighborIndex.isValid())
00343 break;
00344 itemHeight = qMax(itemHeight, q->QStyledItemDelegate::sizeHint(option, neighborIndex).height());
00345 }
00346
00347
00348 size.rheight() = itemHeight + extender->sizeHint().height();
00349 return size;
00350 }
00351
00352
00353 QModelIndex KExtendableItemDelegate::Private::indexOfExtendedColumnInSameRow(const QModelIndex &index) const
00354 {
00355 const QAbstractItemModel *const model = index.model();
00356 const QModelIndex parentIndex(index.parent());
00357 const int row = index.row();
00358 const int columnCount = model->columnCount();
00359
00360
00361 for (int column = 0; column < columnCount; column++) {
00362 QModelIndex indexOfExt(model->index(row, column, parentIndex));
00363 if (extenders.value(indexOfExt))
00364 return indexOfExt;
00365 }
00366
00367 return QModelIndex();
00368 }
00369
00370
00371 void KExtendableItemDelegate::updateExtenderGeometry(QWidget *extender, const QStyleOptionViewItem &option,
00372 const QModelIndex &index) const
00373 {
00374 Q_UNUSED(index);
00375 extender->setGeometry(option.rect);
00376 }
00377
00378
00379 void KExtendableItemDelegate::Private::deleteExtenders()
00380 {
00381 foreach (QWidget *ext, extenders) {
00382
00383
00384 ext->hide();
00385 ext->deleteLater();
00386 }
00387 extenders.clear();
00388 extenderIndices.clear();
00389 }
00390
00391
00392
00393
00394 void KExtendableItemDelegate::Private::scheduleUpdateViewLayout()
00395 {
00396 QAbstractItemView *aiv = qobject_cast<QAbstractItemView *>(q->parent());
00397
00398 if (aiv)
00399
00400 aiv->setRootIndex(aiv->rootIndex());
00401 }
00402
00403
00404 void KExtendableItemDelegate::setExtendPixmap(const QPixmap &pixmap)
00405 {
00406 d->extendPixmap = pixmap;
00407 }
00408
00409
00410 void KExtendableItemDelegate::setContractPixmap(const QPixmap &pixmap)
00411 {
00412 d->contractPixmap = pixmap;
00413 }
00414
00415
00416 QPixmap KExtendableItemDelegate::extendPixmap()
00417 {
00418 return d->extendPixmap;
00419 }
00420
00421
00422 QPixmap KExtendableItemDelegate::contractPixmap()
00423 {
00424 return d->contractPixmap;
00425 }
00426
00427 #include "kextendableitemdelegate.moc"