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