00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "qxtcrumbview.h"
00027 #include "qxtcrumbview_p.h"
00028 #include <QApplication>
00029 #include <QPaintEvent>
00030 #include <QListView>
00031 #include <QToolButton>
00032 #include <QBoxLayout>
00033 #include <QModelIndex>
00034 #include <QAbstractItemDelegate>
00035 #include <QStyle>
00036 #include <QStylePainter>
00037 #include <QSize>
00038 #include <QFont>
00039 #include <QtAlgorithms>
00040 #include <QtDebug>
00041
00042
00043 class QxtCrumbViewList : public QListView {
00044 friend class QxtCrumbView;
00045 public:
00046 QxtCrumbViewList(QWidget* parent) : QListView(parent) {}
00047 };
00048
00049 class QxtCrumbViewDelegate : public QAbstractItemDelegate {
00050 public:
00051 QxtCrumbViewDelegate(QAbstractItemDelegate* other, QObject* parent) : QAbstractItemDelegate(parent), delegate(other) {
00052 QObject::connect(other, SIGNAL(closeEditor(QWidget*, QAbstractItemDelegate::EndEditHint)),
00053 this, SIGNAL(closeEditor(QWidget*, QAbstractItemDelegate::EndEditHint)));
00054 QObject::connect(other, SIGNAL(commitData(QWidget*)), this, SIGNAL(commitData(QWidget*)));
00055 QObject::connect(other, SIGNAL(sizeHintChanged(QModelIndex)), this, SIGNAL(sizeHintChanged(QModelIndex)));
00056
00057 }
00058 QAbstractItemDelegate* delegate;
00059
00060 void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
00061 delegate->paint(painter, option, index);
00062
00063 if(!index.model()->hasChildren(index)) return;
00064
00065 int arrow = 8;
00066 int pad = (option.rect.height() - arrow) / 2;
00067 QStyleOption arrowOption;
00068 arrowOption = option;
00069 arrowOption.rect = QRect(option.rect.right() - arrow, option.rect.top() + pad, arrow, arrow);
00070 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &arrowOption, painter);
00071 }
00072
00073 QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
00074 return delegate->sizeHint(option, index) + QSize(8, 0);
00075 }
00076
00077 QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) {
00078 return delegate->createEditor(parent, option, index);
00079 }
00080
00081 void setEditorData(QWidget* editor, const QModelIndex& index) const {
00082 delegate->setEditorData(editor, index);
00083 }
00084
00085 void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const {
00086 delegate->setModelData(editor, model, index);
00087 }
00088
00089 void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const {
00090 delegate->updateEditorGeometry(editor, option, index);
00091 }
00092
00093 bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) {
00094 return delegate->editorEvent(event, model, option, index);
00095 }
00096 };
00097
00098 class QxtCrumbViewButton : public QAbstractButton {
00099 public:
00100 QxtCrumbViewButton(const QModelIndex& idx, QxtCrumbView* parent) : QAbstractButton(parent), index(idx) {
00101
00102 }
00103
00104 QModelIndex index;
00105
00106 QSize sizeHint() const {
00107 QStyleOptionViewItem itemOption;
00108 itemOption.initFrom(this);
00109 int border = style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2;
00110 int arrow = 8;
00111 if(index.isValid())
00112 return static_cast<QxtCrumbView*>(parent())->itemDelegate()->sizeHint(itemOption, index) + QSize(border + arrow, border);
00113 else
00114 return QSize(border + arrow, border + arrow);
00115 }
00116
00117 protected:
00118 void enterEvent(QEvent* event) {
00119 Q_UNUSED(event);
00120 update();
00121 }
00122
00123 void leaveEvent(QEvent* event) {
00124 Q_UNUSED(event);
00125 update();
00126 }
00127
00128 void paintEvent(QPaintEvent* event) {
00129 Q_UNUSED(event);
00130 QStylePainter painter(this);
00131 QStyleOptionButton option;
00132 option.initFrom(this);
00133 if(rect().contains(mapFromGlobal(QCursor::pos()))) {
00134 painter.drawPrimitive(QStyle::PE_PanelButtonTool, option);
00135 }
00136 int border = painter.style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
00137 option.rect = option.rect.adjusted(border, border, -border, -border);
00138 QAbstractItemDelegate* delegate = static_cast<QxtCrumbView*>(parent())->itemDelegate();
00139 QStyleOptionViewItem itemOption;
00140 itemOption.initFrom(this);
00141 itemOption.rect = option.rect;
00142 delegate->paint(&painter, itemOption, index);
00143 int arrow = 8;
00144 int pad = (height() - (2 * border) - arrow) / 2;
00145 QStyleOption arrowOption;
00146 arrowOption.initFrom(this);
00147 arrowOption.rect = QRect(width() - border - arrow, pad, arrow, arrow);
00148 painter.drawPrimitive(QStyle::PE_IndicatorArrowRight, arrowOption);
00149 }
00150 };
00151
00152 void QxtCrumbViewPrivate::addCrumb(const QModelIndex& index) {
00153 crumbs.append(index);
00154 QxtCrumbViewButton* button = new QxtCrumbViewButton(index, &qxt_p());
00155 button->setFocusPolicy(Qt::NoFocus);
00156 button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
00157 buttons.append(button);
00158 buttonLayout->insertWidget(buttons.count() - 1, button, 0);
00159 QObject::connect(button, SIGNAL(clicked()), this, SLOT(buttonPressed()));
00160 }
00161
00162 void QxtCrumbViewPrivate::buttonPressed() {
00163 qxt_p().enterTree(static_cast<QxtCrumbViewButton*>(sender())->index);
00164 }
00165
00166 QxtCrumbView::QxtCrumbView(QWidget* parent) : QAbstractItemView(parent) {
00167 QXT_INIT_PRIVATE(QxtCrumbView);
00168 setAutoFillBackground(false);
00169 setBackgroundRole(QPalette::Window);
00170 viewport()->setAutoFillBackground(false);
00171 viewport()->setBackgroundRole(QPalette::Window);
00172 setFrameStyle(QFrame::NoFrame);
00173 QVBoxLayout* layout = new QVBoxLayout(this);
00174 qxt_d().buttonLayout = new QHBoxLayout;
00175 qxt_d().buttonLayout->setSpacing(0);
00176 qxt_d().buttonLayout->addStretch(1);
00177 QToolButton* backButton = new QToolButton(this);
00178 backButton->setIcon(style()->standardPixmap(QStyle::SP_FileDialogBack));
00179 backButton->setAutoRaise(true);
00180 backButton->setFocusPolicy(Qt::NoFocus);
00181 qxt_d().buttonLayout->addWidget(backButton);
00182 layout->addLayout(qxt_d().buttonLayout, 0);
00183 qxt_d().view = new QxtCrumbViewList(this);
00184 layout->addWidget(qxt_d().view, 1);
00185 qxt_d().addCrumb(QModelIndex());
00186
00187 QObject::connect(qxt_d().view, SIGNAL(activated(QModelIndex)), this, SLOT(enterTree(QModelIndex)));
00188 QObject::connect(backButton, SIGNAL(clicked()), this, SLOT(back()));
00189 }
00190
00191 void QxtCrumbView::setModel(QAbstractItemModel* model) {
00192 qxt_d().view->setModel(model);
00193 QAbstractItemView::setModel(model);
00194 delete qxt_d().view->itemDelegate();
00195 qxt_d().view->setItemDelegate(new QxtCrumbViewDelegate(itemDelegate(), this));
00196 }
00197
00198 void QxtCrumbView::reset() {
00199 QAbstractItemView::reset();
00200 qDeleteAll(qxt_d().buttons);
00201 qxt_d().crumbs.clear();
00202 qxt_d().buttons.clear();
00203 qxt_d().addCrumb(QModelIndex());
00204 }
00205
00206 int QxtCrumbView::horizontalOffset() const {
00207 return qxt_d().view->horizontalOffset();
00208 }
00209
00210 int QxtCrumbView::verticalOffset() const {
00211 return qxt_d().view->verticalOffset();
00212 }
00213
00214 QModelIndex QxtCrumbView::indexAt(const QPoint& point) const {
00215 return qxt_d().view->indexAt(qxt_d().view->mapFromParent(point));
00216 }
00217
00218 bool QxtCrumbView::isIndexHidden(const QModelIndex& index) const {
00219 return qxt_d().view->isIndexHidden(index);
00220 }
00221
00222 QModelIndex QxtCrumbView::moveCursor(CursorAction action, Qt::KeyboardModifiers mods) {
00223 return qxt_d().view->moveCursor(action, mods);
00224 }
00225
00226 void QxtCrumbView::scrollTo(const QModelIndex& index, ScrollHint hint) {
00227 if(index.parent() != qxt_d().view->rootIndex()) {
00228
00229 }
00230 qxt_d().view->scrollTo(index, hint);
00231 }
00232
00233 void QxtCrumbView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags) {
00234 QPoint tl = qxt_d().view->mapFromParent(rect.topLeft());
00235 qxt_d().view->setSelection(QRect(tl, rect.size()), flags);
00236 }
00237
00238 QRect QxtCrumbView::visualRect(const QModelIndex& index) const {
00239 QRect rect = qxt_d().view->visualRect(index);
00240 return QRect(qxt_d().view->mapToParent(rect.topLeft()), rect.size());
00241 }
00242
00243 QRegion QxtCrumbView::visualRegionForSelection(const QItemSelection& selection) const {
00244 QRegion region = qxt_d().view->visualRegionForSelection(selection);
00245 return region.translated(qxt_d().view->pos());
00246 }
00247
00248 void QxtCrumbView::enterTree(const QModelIndex& index) {
00249 if(!model() || !model()->hasChildren(index)) return;
00250 if(index == qxt_d().view->rootIndex()) {
00251
00252 } else if(index.parent() != qxt_d().view->rootIndex()) {
00253 foreach(QxtCrumbViewButton* b, qxt_d().buttons) b->deleteLater();
00254 qxt_d().crumbs.clear();
00255 qxt_d().buttons.clear();
00256 qxt_d().addCrumb(QModelIndex());
00257 QList<QModelIndex> chain;
00258 QModelIndex pos = index;
00259 while(pos.isValid()) {
00260 chain.append(pos);
00261 pos = pos.parent();
00262 }
00263 while(!chain.isEmpty()) {
00264 qxt_d().addCrumb(chain.last());
00265 chain.removeLast();
00266 }
00267 } else {
00268 qxt_d().addCrumb(index);
00269 }
00270 qxt_d().view->setRootIndex(index);
00271 }
00272
00273 void QxtCrumbView::back() {
00274 if(qxt_d().crumbs.count() <= 1) return;
00275 qxt_d().buttons.last()->deleteLater();
00276 qxt_d().buttons.removeLast();
00277 qxt_d().crumbs.removeLast();
00278 qxt_d().view->setRootIndex(qxt_d().crumbs.last());
00279 }
00280
00281 void QxtCrumbView::setItemDelegate(QAbstractItemDelegate* delegate) {
00282 QAbstractItemView::setItemDelegate(delegate);
00283 delete qxt_d().view->itemDelegate();
00284 qxt_d().view->setItemDelegate(new QxtCrumbViewDelegate(itemDelegate(), this));
00285 }
00286
00287 QListView* QxtCrumbView::listView() const {
00288 return qxt_d().view;
00289 }