| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 | 
							- /*=========================================================================
 
-   Library:   CTK
 
-   Copyright (c) Kitware Inc.
 
-   Licensed under the Apache License, Version 2.0 (the "License");
 
-   you may not use this file except in compliance with the License.
 
-   You may obtain a copy of the License at
 
-       http://www.apache.org/licenses/LICENSE-2.0.txt
 
-   Unless required by applicable law or agreed to in writing, software
 
-   distributed under the License is distributed on an "AS IS" BASIS,
 
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
-   See the License for the specific language governing permissions and
 
-   limitations under the License.
 
- =========================================================================*/
 
- // Qt includes
 
- #include <QApplication>
 
- #include <QDesktopWidget>
 
- #include <QEvent>
 
- #include <QHeaderView>
 
- #include <QKeyEvent>
 
- #include <QLayout>
 
- #include <QScrollBar>
 
- #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
 
- # include <QInputContext>
 
- #endif
 
- #include <QMouseEvent>
 
- #include <QModelIndex>
 
- #include <QStack>
 
- #include <QTreeView>
 
- #include <QDebug>
 
- // CTK includes
 
- #include "ctkTreeComboBox.h"
 
- // -------------------------------------------------------------------------
 
- class ctkTreeComboBoxPrivate
 
- {
 
-   Q_DECLARE_PUBLIC(ctkTreeComboBox);
 
- protected:
 
-   ctkTreeComboBox* const q_ptr;
 
- public:
 
-   ctkTreeComboBoxPrivate(ctkTreeComboBox& object);
 
-   int computeWidthHint()const;
 
-   bool SkipNextHide;
 
-   bool RootSet;
 
-   bool SendCurrentItem;
 
-   QPersistentModelIndex Root;
 
-   int VisibleModelColumn;
 
- };
 
- // -------------------------------------------------------------------------
 
- ctkTreeComboBoxPrivate::ctkTreeComboBoxPrivate(ctkTreeComboBox& object)
 
-   :q_ptr(&object)
 
- {
 
-   this->SkipNextHide = false;
 
-   this->RootSet = false;
 
-   this->SendCurrentItem = false;
 
-   this->VisibleModelColumn = -1; // all visible by default
 
- }
 
- // -------------------------------------------------------------------------
 
- int ctkTreeComboBoxPrivate::computeWidthHint()const
 
- {
 
-   Q_Q(const ctkTreeComboBox);
 
-   return q->view()->sizeHintForColumn(q->modelColumn());
 
- }
 
- // -------------------------------------------------------------------------
 
- ctkTreeComboBox::ctkTreeComboBox(QWidget* _parent):Superclass(_parent)
 
-   , d_ptr(new ctkTreeComboBoxPrivate(*this))
 
- {
 
-   QTreeView* treeView = new QTreeView(this);
 
-   treeView->setHeaderHidden(true);
 
-   this->setView(treeView);
 
-   // we install the filter AFTER the QComboBox installed it.
 
-   // so that our eventFilter will be called first
 
-   this->view()->viewport()->installEventFilter(this);
 
-   connect(treeView, SIGNAL(collapsed(QModelIndex)),
 
-           this, SLOT(resizePopup()));
 
-   connect(treeView, SIGNAL(expanded(QModelIndex)),
 
-           this, SLOT(resizePopup()));
 
- }
 
- // -------------------------------------------------------------------------
 
- ctkTreeComboBox::~ctkTreeComboBox()
 
- {
 
- }
 
- // -------------------------------------------------------------------------
 
- int ctkTreeComboBox::visibleModelColumn()const
 
- {
 
-   Q_D(const ctkTreeComboBox);
 
-   return d->VisibleModelColumn;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkTreeComboBox::setVisibleModelColumn(int index)
 
- {
 
-   Q_D(ctkTreeComboBox);
 
-   d->VisibleModelColumn = index;
 
- }
 
- // -------------------------------------------------------------------------
 
- bool ctkTreeComboBox::eventFilter(QObject* object, QEvent* _event)
 
- {
 
-   Q_D(ctkTreeComboBox);
 
-   Q_UNUSED(object);
 
-   bool res = false;
 
-   d->SendCurrentItem = false;
 
-   switch (_event->type())
 
-     {
 
-     default:
 
-       break;
 
-     case QEvent::ShortcutOverride:
 
-       switch (static_cast<QKeyEvent*>(_event)->key())
 
-         {
 
-         case Qt::Key_Enter:
 
-         case Qt::Key_Return:
 
-         case Qt::Key_Select:
 
-           d->SendCurrentItem = true;
 
-           break;
 
-         default:
 
-           break;
 
-         }
 
-       break;
 
-     case QEvent::MouseButtonRelease:
 
-       QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(_event); 
 
-       QModelIndex index = this->view()->indexAt(mouseEvent->pos());
 
-       // do we click the branch (+ or -) or the item itself ?
 
-       if (this->view()->model()->hasChildren(index) && 
 
-           (index.flags() & Qt::ItemIsSelectable) &&
 
-           !this->view()->visualRect(index).contains(mouseEvent->pos()))
 
-         {//qDebug() << "Set skip on";
 
-         // if the branch is clicked, then we don't want to close the 
 
-         // popup. (we don't want to select the item, just expand it.)
 
-         // of course, all that doesn't apply with unselectable items, as
 
-         // they won't close the popup.
 
-         d->SkipNextHide = true;
 
-         }
 
-       // we want to get rid of an odd behavior. 
 
-       // If the user highlight a selectable item and then 
 
-       // click on the branch of an unselectable item while keeping the 
 
-       // previous selection. The popup would be normally closed in that
 
-       // case. We don't want that.
 
-       if ( this->view()->model()->hasChildren(index) && 
 
-            !(index.flags() & Qt::ItemIsSelectable) &&         
 
-            !this->view()->visualRect(index).contains(mouseEvent->pos()))
 
-         {//qDebug() << "eat";
 
-         // eat the event, don't go to the QComboBox event filters.
 
-         res = true;
 
-         }
 
-       d->SendCurrentItem = this->view()->rect().contains(mouseEvent->pos()) &&
 
-         this->view()->currentIndex().isValid() &&
 
-         (this->view()->currentIndex().flags() & Qt::ItemIsEnabled) &&
 
-         (this->view()->currentIndex().flags() & Qt::ItemIsSelectable);
 
-       break;
 
-     }
 
-   return res;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkTreeComboBox::showPopup()
 
- {
 
-   Q_D(ctkTreeComboBox);
 
-   QHeaderView* header = qobject_cast<QTreeView*>(this->view())->header();
 
-   for (int i = 0; i < header->count(); ++i)
 
-     {
 
-     header->setSectionHidden(i, d->VisibleModelColumn != -1 &&
 
-                                 i != d->VisibleModelColumn);
 
-     }
 
-   this->QComboBox::showPopup();
 
-   emit this->popupShow();
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkTreeComboBox::hidePopup()
 
- {
 
-   Q_D(ctkTreeComboBox);
 
-   
 
-   if (d->SkipNextHide)
 
-     {// don't hide the popup if the selected item is a parent.
 
-     d->SkipNextHide = false;
 
-     //this->setCurrentIndex(-1);
 
-     //qDebug() << "skip";
 
-     //this->QComboBox::showPopup();
 
-     }
 
-   else
 
-     {
 
-     //QModelIndex _currentIndex = this->view()->currentIndex();
 
-     //qDebug() << "ctkTreeComboBox::hidePopup() " << _currentIndex << " " << _currentIndex.row();
 
-     //qDebug() << "before: " << this->currentIndex() << this->view()->currentIndex();
 
-     this->QComboBox::hidePopup();
 
-     //qDebug() << "after: " << this->currentIndex() << this->view()->currentIndex();
 
-     //this->setRootModelIndex(_currentIndex.parent());
 
-     //this->setCurrentIndex(_currentIndex.row());
 
-     if (d->SendCurrentItem)
 
-       {
 
-       d->SendCurrentItem = false;
 
-       QKeyEvent event(QEvent::ShortcutOverride, Qt::Key_Enter, Qt::NoModifier);
 
-       QApplication::sendEvent(this->view(), &event);
 
-       }
 
-     emit this->popupHide();
 
-     //qDebug() << "after2: " << this->currentIndex() << this->view()->currentIndex();
 
-     }
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkTreeComboBox::paintEvent(QPaintEvent *p)
 
- {
 
-   //qDebug() << __FUNCTION__ << " " << this->currentText() << " " << this->currentIndex() ;
 
-   //qDebug() << this->itemText(0) << this->itemText(1);
 
-   this->QComboBox::paintEvent(p);
 
- }
 
- // -------------------------------------------------------------------------
 
- QTreeView* ctkTreeComboBox::treeView()const
 
- {
 
-   return qobject_cast<QTreeView*>(this->view());
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkTreeComboBox::resizePopup()
 
- {
 
-   // copied from QComboBox.cpp
 
-   Q_D(ctkTreeComboBox);
 
-   QStyle * const style = this->style();
 
-   QWidget* container = qobject_cast<QWidget*>(this->view()->parent());
 
-   QStyleOptionComboBox opt;
 
-   this->initStyleOption(&opt);
 
-   QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt,
 
-                                        QStyle::SC_ComboBoxListBoxPopup, this));
 
-   QRect screen = QApplication::desktop()->availableGeometry(
 
-     QApplication::desktop()->screenNumber(this));
 
-   QPoint below = this->mapToGlobal(listRect.bottomLeft());
 
-   int belowHeight = screen.bottom() - below.y();
 
-   QPoint above = this->mapToGlobal(listRect.topLeft());
 
-   int aboveHeight = above.y() - screen.y();
 
-   bool boundToScreen = !this->window()->testAttribute(Qt::WA_DontShowOnScreen);
 
-   const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this);
 
-     {
 
-     int listHeight = 0;
 
-     int count = 0;
 
-     QStack<QModelIndex> toCheck;
 
-     toCheck.push(this->view()->rootIndex());
 
- #ifndef QT_NO_TREEVIEW
 
-     QTreeView *treeView = qobject_cast<QTreeView*>(this->view());
 
-     if (treeView && treeView->header() && !treeView->header()->isHidden())
 
-       listHeight += treeView->header()->height();
 
- #endif
 
-     while (!toCheck.isEmpty())
 
-       {
 
-       QModelIndex parent = toCheck.pop();
 
-       for (int i = 0; i < this->model()->rowCount(parent); ++i)
 
-         {
 
-         QModelIndex idx = this->model()->index(i, this->modelColumn(), parent);
 
-         if (!idx.isValid())
 
-           {
 
-           continue;
 
-           }
 
-         listHeight += this->view()->visualRect(idx).height(); /* + container->spacing() */;
 
- #ifndef QT_NO_TREEVIEW
 
-         if (this->model()->hasChildren(idx) && treeView && treeView->isExpanded(idx))
 
-           {
 
-           toCheck.push(idx);
 
-           }
 
- #endif
 
-         ++count;
 
-         if (!usePopup && count > this->maxVisibleItems())
 
-           {
 
-           toCheck.clear();
 
-           break;
 
-           }
 
-         }
 
-       }
 
-     listRect.setHeight(listHeight);
 
-     }
 
-       {
 
-       // add the spacing for the grid on the top and the bottom;
 
-       int heightMargin = 0;//2*container->spacing();
 
-       // add the frame of the container
 
-       int marginTop, marginBottom;
 
-       container->getContentsMargins(0, &marginTop, 0, &marginBottom);
 
-       heightMargin += marginTop + marginBottom;
 
-       //add the frame of the view
 
-       this->view()->getContentsMargins(0, &marginTop, 0, &marginBottom);
 
-       //marginTop += static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(this->view()))->top;
 
-       //marginBottom += static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(this->view()))->bottom;
 
-       heightMargin += marginTop + marginBottom;
 
-       listRect.setHeight(listRect.height() + heightMargin);
 
-       }
 
-       // Add space for margin at top and bottom if the style wants it.
 
-       if (usePopup)
 
-         {
 
-         listRect.setHeight(listRect.height() + style->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) * 2);
 
-         }
 
-       // Make sure the popup is wide enough to display its contents.
 
-       if (usePopup)
 
-         {
 
-         const int diff = d->computeWidthHint() - this->width();
 
-         if (diff > 0)
 
-           {
 
-           listRect.setWidth(listRect.width() + diff);
 
-           }
 
-         }
 
-       //we need to activate the layout to make sure the min/maximum size are set when the widget was not yet show
 
-       container->layout()->activate();
 
-       //takes account of the minimum/maximum size of the container
 
-       listRect.setSize( listRect.size().expandedTo(container->minimumSize())
 
-                         .boundedTo(container->maximumSize()));
 
-       // make sure the widget fits on screen
 
-       if (boundToScreen)
 
-         {
 
-         if (listRect.width() > screen.width() )
 
-           {
 
-           listRect.setWidth(screen.width());
 
-           }
 
-         if (this->mapToGlobal(listRect.bottomRight()).x() > screen.right())
 
-           {
 
-           below.setX(screen.x() + screen.width() - listRect.width());
 
-           above.setX(screen.x() + screen.width() - listRect.width());
 
-           }
 
-         if (this->mapToGlobal(listRect.topLeft()).x() < screen.x() )
 
-           {
 
-           below.setX(screen.x());
 
-           above.setX(screen.x());
 
-           }
 
-         }
 
-       if (usePopup)
 
-         {
 
-         // Position horizontally.
 
-         listRect.moveLeft(above.x());
 
- #ifndef Q_WS_S60
 
-         // Position vertically so the curently selected item lines up
 
-         // with the combo box.
 
-         const QRect currentItemRect = this->view()->visualRect(this->view()->currentIndex());
 
-         const int offset = listRect.top() - currentItemRect.top();
 
-         listRect.moveTop(above.y() + offset - listRect.top());
 
- #endif
 
-       // Clamp the listRect height and vertical position so we don't expand outside the
 
-       // available screen geometry.This may override the vertical position, but it is more
 
-       // important to show as much as possible of the popup.
 
-         const int height = !boundToScreen ? listRect.height() : qMin(listRect.height(), screen.height());
 
- #ifdef Q_WS_S60
 
-         //popup needs to be stretched with screen minimum dimension
 
-         listRect.setHeight(qMin(screen.height(), screen.width()));
 
- #else
 
-         listRect.setHeight(height);
 
- #endif
 
-         if (boundToScreen)
 
-           {
 
-           if (listRect.top() < screen.top())
 
-             {
 
-             listRect.moveTop(screen.top());
 
-             }
 
-           if (listRect.bottom() > screen.bottom())
 
-             {
 
-             listRect.moveBottom(screen.bottom());
 
-             }
 
-           }
 
- #ifdef Q_WS_S60
 
-         if (screen.width() < screen.height())
 
-           {
 
-           // in portait, menu should be positioned above softkeys
 
-           listRect.moveBottom(screen.bottom());
 
-           }
 
-         else
 
-           {
 
-           TRect staConTopRect = TRect();
 
-           AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EStaconTop, staConTopRect);
 
-           listRect.setWidth(listRect.height());
 
-           //by default popup is centered on screen in landscape
 
-           listRect.moveCenter(screen.center());
 
-           if (staConTopRect.IsEmpty())
 
-             {
 
-             // landscape without stacon, menu should be at the right
 
-             (opt.direction == Qt::LeftToRight) ? listRect.setRight(screen.right()) :
 
-               listRect.setLeft(screen.left());
 
-             }
 
-           }
 
- #endif
 
-         }
 
-       else if (!boundToScreen || listRect.height() <= belowHeight)
 
-         {
 
-         listRect.moveTopLeft(below);
 
-         }
 
-       else if (listRect.height() <= aboveHeight)
 
-         {
 
-         listRect.moveBottomLeft(above);
 
-         }
 
-       else if (belowHeight >= aboveHeight)
 
-         {
 
-         listRect.setHeight(belowHeight);
 
-         listRect.moveTopLeft(below);
 
-         }
 
-       else
 
-         {
 
-         listRect.setHeight(aboveHeight);
 
-         listRect.moveBottomLeft(above);
 
-         }
 
- #if QT_VERSION < QT_VERSION_CHECK(5,0,0) && !defined QT_NO_IM
 
-       if (QInputContext *qic = this->inputContext())
 
-         {
 
-         qic->reset();
 
-         }
 
- #endif
 
-       QScrollBar *sb = this->view()->horizontalScrollBar();
 
-       Qt::ScrollBarPolicy policy = this->view()->horizontalScrollBarPolicy();
 
-       bool needHorizontalScrollBar =
 
-         (policy == Qt::ScrollBarAsNeeded || policy == Qt::ScrollBarAlwaysOn)
 
-         && sb->minimum() < sb->maximum();
 
-       if (needHorizontalScrollBar)
 
-         {
 
-         listRect.adjust(0, 0, 0, sb->height());
 
-         }
 
-       container->setGeometry(listRect);
 
- }
 
 
  |