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