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