| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 | 
							- /*=========================================================================
 
-   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 <QActionEvent>
 
- #include <QCompleter>
 
- #include <QDebug>
 
- #include <QEvent>
 
- #include <QHBoxLayout>
 
- #include <QLineEdit>
 
- #include <QStringList>
 
- #include <QStringListModel>
 
- #include <QToolButton>
 
- // CTK includes
 
- #include "ctkCompleter.h"
 
- #include "ctkSearchBox.h"
 
- #include "ctkMenuComboBox_p.h"
 
- // -------------------------------------------------------------------------
 
- ctkMenuComboBoxInternal::ctkMenuComboBoxInternal()
 
- {
 
- }
 
- // -------------------------------------------------------------------------
 
- ctkMenuComboBoxInternal::~ctkMenuComboBoxInternal()
 
- {
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBoxInternal::showPopup()
 
- {
 
-   QMenu* menu = this->Menu.data();
 
-   if (!menu)
 
-     {
 
-     return;
 
-     }
 
-   menu->popup(this->mapToGlobal(this->rect().bottomLeft()));
 
-   static int minWidth = menu->sizeHint().width();
 
-   menu->setFixedWidth(qMax(this->width(), minWidth));
 
-   emit popupShown();
 
- }
 
- // -------------------------------------------------------------------------
 
- QSize ctkMenuComboBoxInternal::minimumSizeHint()const
 
- {
 
-   // Cached QComboBox::minimumSizeHint is not recomputed when the current
 
-   // index change, however QComboBox::sizeHint is. Use it instead.
 
-   return this->sizeHint();
 
- }
 
- // -------------------------------------------------------------------------
 
- ctkMenuComboBoxPrivate::ctkMenuComboBoxPrivate(ctkMenuComboBox& object)
 
-   :q_ptr(&object)
 
- {
 
-   this->MenuComboBox = 0;
 
-   this->SearchCompleter = 0;
 
-   this->EditBehavior = ctkMenuComboBox::NotEditable;
 
-   this->IsDefaultTextCurrent = true;
 
-   this->IsDefaultIconCurrent = true;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBoxPrivate::init()
 
- {
 
-   Q_Q(ctkMenuComboBox);
 
-   this->setParent(q);
 
-   QHBoxLayout* layout = new QHBoxLayout(q);
 
-   layout->setContentsMargins(0,0,0,0);
 
-   layout->setSizeConstraint(QLayout::SetMinimumSize);
 
-   layout->setSpacing(0);
 
-   // SearchButton
 
-   this->SearchButton = new QToolButton();
 
-   this->SearchButton->setText(q->tr("Search"));
 
-   this->SearchButton->setIcon(QIcon(":/Icons/search.svg"));
 
-   this->SearchButton->setCheckable(true);
 
-   this->SearchButton->setAutoRaise(true);
 
-   layout->addWidget(this->SearchButton);
 
-   q->connect(this->SearchButton, SIGNAL(toggled(bool)),
 
-              this, SLOT(setComboBoxEditable(bool)));
 
-   // MenuComboBox
 
-   this->MenuComboBox = new ctkMenuComboBoxInternal();
 
-   this->MenuComboBox->setMinimumContentsLength(12);
 
-   layout->addWidget(this->MenuComboBox);
 
-   this->MenuComboBox->installEventFilter(q);
 
-   this->MenuComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
 
-   this->MenuComboBox->addItem(this->DefaultIcon, this->DefaultText);
 
-   q->connect(this->MenuComboBox, SIGNAL(popupShown()),
 
-              q, SIGNAL(popupShown()));
 
-   this->SearchCompleter = new ctkCompleter(QStringList(), this->MenuComboBox);
 
-   this->SearchCompleter->popup()->setParent(q);
 
-   this->SearchCompleter->setCaseSensitivity(Qt::CaseInsensitive);
 
-   this->SearchCompleter->setModelFiltering(ctkCompleter::FilterWordStartsWith);
 
-   q->connect(this->SearchCompleter, SIGNAL(activated(QString)),
 
-              q, SLOT(onEditingFinished()));
 
-   // Automatically set the minimumSizeHint of the layout to the widget
 
-   layout->setSizeConstraint(QLayout::SetMinimumSize);
 
-   // Behave like a QComboBox
 
-   q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed,
 
-                                QSizePolicy::ComboBox));
 
-   q->setDefaultText(ctkMenuComboBox::tr("Search..."));
 
- }
 
- //  ------------------------------------------------------------------------
 
- QAction* ctkMenuComboBoxPrivate::actionByTitle(const QString& text, const QMenu* parentMenu)
 
- {
 
-   if (parentMenu->title() == text)
 
-     {
 
-     return 0;
 
-     }
 
-   foreach(QAction* action, parentMenu->actions())
 
-     {
 
-     if (!action->menu() && action->text().toLower() == text.toLower())
 
-       {
 
-       return action;
 
-       }
 
-     if (action->menu())
 
-       {
 
-       QAction* subAction = this->actionByTitle(text, action->menu());
 
-       if(subAction)
 
-         {
 
-         return subAction;
 
-         }
 
-       }
 
-     }
 
-   return 0;
 
- }
 
- //  ------------------------------------------------------------------------
 
- void ctkMenuComboBoxPrivate::setCurrentText(const QString& newCurrentText)
 
- {
 
-   if (this->MenuComboBox->lineEdit())
 
-     {
 
-     static_cast<ctkSearchBox*>(this->MenuComboBox->lineEdit())
 
-       ->setPlaceholderText(newCurrentText);
 
-     }
 
-   this->MenuComboBox->setItemText(this->MenuComboBox->currentIndex(),
 
-                                   newCurrentText);
 
- }
 
- //  ------------------------------------------------------------------------
 
- QString ctkMenuComboBoxPrivate::currentText()const
 
- {
 
-   return this->MenuComboBox->itemText(this->MenuComboBox->currentIndex());
 
- }
 
- //  ------------------------------------------------------------------------
 
- QIcon ctkMenuComboBoxPrivate::currentIcon()const
 
- {
 
-   return this->MenuComboBox->itemIcon(this->MenuComboBox->currentIndex());
 
- }
 
- //  ------------------------------------------------------------------------
 
- void ctkMenuComboBoxPrivate::setCurrentIcon(const QIcon& newCurrentIcon)
 
- {
 
-   this->MenuComboBox->setItemIcon(this->MenuComboBox->currentIndex(),
 
-                                   newCurrentIcon);
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBoxPrivate::setComboBoxEditable(bool edit)
 
- {
 
-   Q_Q(ctkMenuComboBox);
 
-   if(edit)
 
-     {
 
-     if (!this->MenuComboBox->lineEdit())
 
-       {
 
-       ctkSearchBox* line = new ctkSearchBox();
 
-       this->MenuComboBox->setLineEdit(line);
 
-       if (q->isSearchIconVisible())
 
-         {
 
-         this->MenuComboBox->lineEdit()->selectAll();
 
-         this->MenuComboBox->setFocus();
 
-         }
 
-       q->connect(line, SIGNAL(editingFinished()),
 
-                  q,SLOT(onEditingFinished()));
 
-       }
 
-     this->MenuComboBox->setCompleter(this->SearchCompleter);
 
-     }
 
-   this->MenuComboBox->setEditable(edit);
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBoxPrivate::addAction(QAction *action)
 
- {
 
-   if (action->menu())
 
-     {
 
-     this->addMenuToCompleter(action->menu());
 
-     }
 
-   else
 
-     {
 
-     this->addActionToCompleter(action);
 
-     }
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBoxPrivate::addMenuToCompleter(QMenu* menu)
 
- {
 
-   Q_Q(ctkMenuComboBox);
 
-   menu->installEventFilter(q);
 
-   // Bug QT : see this link for more details
 
-   // https://bugreports.qt.nokia.com/browse/QTBUG-20929?focusedCommentId=161370#comment-161370
 
-   // if the submenu doesn't have a parent, the submenu triggered(QAction*)
 
-   // signal is not propagated. So we listened this submenu to fix the bug.
 
-   QObject* emptyObject = 0;
 
-   if(menu->parent() == emptyObject)
 
-     {
 
-     q->connect(menu, SIGNAL(triggered(QAction*)),
 
-                q, SLOT(onActionSelected(QAction*)), Qt::UniqueConnection);
 
-     }
 
-   foreach (QAction* action, menu->actions())
 
-     {
 
-     this->addAction(action);
 
-     }
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBoxPrivate::addActionToCompleter(QAction *action)
 
- {
 
-   QStringListModel* model = qobject_cast<QStringListModel* >(
 
-     this->SearchCompleter->sourceModel());
 
-   Q_ASSERT(model);
 
-   QModelIndex start = model->index(0,0);
 
-   QModelIndexList indexList = model->match(start, 0, action->text());
 
-   if (indexList.count())
 
-     {
 
-     return;
 
-     }
 
-   int actionCount = model->rowCount();
 
-   model->insertRow(actionCount);
 
-   QModelIndex index = model->index(actionCount, 0);
 
-   model->setData(index, action->text());
 
- }
 
- //  ------------------------------------------------------------------------
 
- void ctkMenuComboBoxPrivate::removeActionToCompleter(QAction *action)
 
- {
 
-   QStringListModel* model = qobject_cast<QStringListModel* >(
 
-     this->SearchCompleter->sourceModel());
 
-   Q_ASSERT(model);
 
-   if (!model->stringList().contains(action->text()) )
 
-     {
 
-     return;
 
-     }
 
-   // Maybe the action is present multiple times in different submenus
 
-   // Don't remove its entry from the completer model if there are still some action instances
 
-   // in the menus.
 
-   if (this->actionByTitle(action->text(), this->Menu.data()))
 
-     {
 
-     return;
 
-     }
 
-   QModelIndex start = model->index(0,0);
 
-   QModelIndexList indexList = model->match(start, 0, action->text());
 
-   Q_ASSERT(indexList.count() == 1);
 
-   foreach (QModelIndex index, indexList)
 
-     {
 
-     // Search completer model is a flat list
 
-     model->removeRow(index.row());
 
-     }
 
- }
 
- //  ------------------------------------------------------------------------
 
- ctkMenuComboBox::ctkMenuComboBox(QWidget* _parent)
 
-   :QWidget(_parent)
 
-   , d_ptr(new ctkMenuComboBoxPrivate(*this))
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   d->init();
 
- }
 
- //  ------------------------------------------------------------------------
 
- ctkMenuComboBox::~ctkMenuComboBox()
 
- {
 
- }
 
- //  ------------------------------------------------------------------------
 
- void ctkMenuComboBox::setMenu(QMenu* menu)
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   if (d->Menu.data() == menu)
 
-     {
 
-     return;
 
-     }
 
-   if (d->Menu)
 
-     {
 
-     this->removeAction(d->Menu.data()->menuAction());
 
-     QObject::disconnect(d->Menu.data(),SIGNAL(triggered(QAction*)),
 
-                         this,SLOT(onActionSelected(QAction*)));
 
-     }
 
-   d->Menu = menu;
 
-   d->MenuComboBox->Menu = menu;
 
-   d->addMenuToCompleter(menu);
 
-   if (d->Menu)
 
-     {
 
-     this->addAction(d->Menu.data()->menuAction());
 
-     QObject::connect(d->Menu.data(),SIGNAL(triggered(QAction*)),
 
-                      this,SLOT(onActionSelected(QAction*)), Qt::UniqueConnection);
 
-     }
 
- }
 
- // -------------------------------------------------------------------------
 
- QMenu* ctkMenuComboBox::menu()const
 
- {
 
-   Q_D(const ctkMenuComboBox);
 
-   return d->Menu.data();
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBox::setDefaultText(const QString& newDefaultText)
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   d->DefaultText = newDefaultText;
 
-   if (d->IsDefaultTextCurrent)
 
-     {
 
-     d->setCurrentText(d->DefaultText);
 
-     }
 
- }
 
- // -------------------------------------------------------------------------
 
- QString ctkMenuComboBox::defaultText()const
 
- {
 
-   Q_D(const ctkMenuComboBox);
 
-   return d->DefaultText;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBox::setDefaultIcon(const QIcon& newIcon)
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   d->DefaultIcon = newIcon;
 
-   if (d->IsDefaultIconCurrent)
 
-     {
 
-     d->setCurrentIcon(d->DefaultIcon);
 
-     }
 
- }
 
- // -------------------------------------------------------------------------
 
- QIcon ctkMenuComboBox::defaultIcon()const
 
- {
 
-   Q_D(const ctkMenuComboBox);
 
-   return d->DefaultIcon;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBox::setEditableBehavior(ctkMenuComboBox::EditableBehavior edit)
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   d->EditBehavior = edit;
 
-       this->disconnect(d->MenuComboBox, SIGNAL(popupShown()),
 
-                     d, SLOT(setComboBoxEditable()));
 
-   switch (edit)
 
-   {
 
-     case ctkMenuComboBox::Editable:
 
-       d->MenuComboBox->setContextMenuPolicy(Qt::DefaultContextMenu);
 
-       d->setComboBoxEditable(true);
 
-       break;
 
-     case ctkMenuComboBox::NotEditable:
 
-       d->MenuComboBox->setContextMenuPolicy(Qt::DefaultContextMenu);
 
-       d->setComboBoxEditable(false);
 
-       break;
 
-     case ctkMenuComboBox::EditableOnFocus:
 
-       d->setComboBoxEditable(this->hasFocus());
 
-       // Here we set the context menu policy to fix a crash on the right click.
 
-       // Opening the context menu removes the focus on the line edit,
 
-       // the comboBox becomes not editable, and the line edit is deleted.
 
-       // The opening of the context menu is done in the line edit and lead to
 
-       // a crash because it infers that the line edit is valid. Another fix
 
-       // could be to delete the line edit later (deleteLater()).
 
-       d->MenuComboBox->setContextMenuPolicy(Qt::NoContextMenu);
 
-       break;
 
-     case ctkMenuComboBox::EditableOnPopup:
 
-       d->setComboBoxEditable(false);
 
-       this->connect(d->MenuComboBox, SIGNAL(popupShown()),
 
-                     d, SLOT(setComboBoxEditable()));
 
-       // Same reason as in ctkMenuComboBox::EditableOnFocus.
 
-       d->MenuComboBox->setContextMenuPolicy(Qt::NoContextMenu);
 
-       break;
 
-   }
 
- }
 
- // -------------------------------------------------------------------------
 
- ctkMenuComboBox::EditableBehavior ctkMenuComboBox::editableBehavior()const
 
- {
 
-   Q_D(const ctkMenuComboBox);
 
-   return d->EditBehavior;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBox::setSearchIconVisible(bool state)
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   d->SearchButton->setVisible(state);
 
- }
 
- // -------------------------------------------------------------------------
 
- bool ctkMenuComboBox::isSearchIconVisible() const
 
- {
 
-   Q_D(const ctkMenuComboBox);
 
-   return d->SearchButton->isVisibleTo(const_cast<ctkMenuComboBox*>(this));
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBox::setToolButtonStyle(Qt::ToolButtonStyle style)
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   d->SearchButton->setToolButtonStyle(style);
 
- }
 
- // -------------------------------------------------------------------------
 
- Qt::ToolButtonStyle ctkMenuComboBox::toolButtonStyle() const
 
- {
 
-   Q_D(const ctkMenuComboBox);
 
-   return d->SearchButton->toolButtonStyle();
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBox::setMinimumContentsLength(int characters)
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   d->MenuComboBox->setMinimumContentsLength(characters);
 
- }
 
- // -------------------------------------------------------------------------
 
- QComboBox* ctkMenuComboBox::menuComboBoxInternal() const
 
- {
 
-   Q_D(const ctkMenuComboBox);
 
-   return d->MenuComboBox;
 
- }
 
- // -------------------------------------------------------------------------
 
- QToolButton* ctkMenuComboBox::toolButtonInternal() const
 
- {
 
-   Q_D(const ctkMenuComboBox);
 
-   return d->SearchButton;
 
- }
 
- // -------------------------------------------------------------------------
 
- ctkCompleter* ctkMenuComboBox::searchCompleter() const
 
- {
 
-   Q_D(const ctkMenuComboBox);
 
-   return d->SearchCompleter;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBox::onActionSelected(QAction* action)
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   /// Set the action selected in the combobox.
 
-   d->IsDefaultTextCurrent = true;
 
-   QString newText = d->DefaultText;
 
-   if (action && !action->text().isEmpty())
 
-     {
 
-     newText = action->text();
 
-     d->IsDefaultTextCurrent = false;
 
-     }
 
-   d->setCurrentText(newText);
 
-   d->IsDefaultIconCurrent = true;
 
-   QIcon newIcon = d->DefaultIcon;
 
-   if (action && !action->icon().isNull())
 
-     {
 
-     d->IsDefaultIconCurrent = false;
 
-     newIcon = action->icon();
 
-     }
 
-   d->setCurrentIcon(newIcon);
 
-   d->MenuComboBox->clearFocus();
 
-   emit ctkMenuComboBox::actionChanged(action);
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBox::clearActiveAction()
 
- {
 
-   this->onActionSelected(0);
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkMenuComboBox::onEditingFinished()
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   if (!d->MenuComboBox->lineEdit())
 
-     {
 
-     return;
 
-     }
 
-   QAction* action = d->actionByTitle(d->MenuComboBox->lineEdit()->text(), d->Menu.data());
 
-   if (!action)
 
-     {
 
-     return;
 
-     }
 
-   if (this->isSearchIconVisible())
 
-     {
 
-     d->SearchButton->setChecked(false);
 
-     }
 
-   action->trigger();
 
- }
 
- // -------------------------------------------------------------------------
 
- bool ctkMenuComboBox::eventFilter(QObject* target, QEvent* event)
 
- {
 
-   Q_D(ctkMenuComboBox);
 
-   if (target == d->MenuComboBox)
 
-     {
 
-     if (event->type() == QEvent::Resize)
 
-       {
 
-       this->layout()->invalidate();
 
-       }
 
-     if (event->type() == QEvent::FocusIn &&
 
-         d->EditBehavior == ctkMenuComboBox::EditableOnFocus)
 
-       {
 
-       d->setComboBoxEditable(true);
 
-       }
 
-     if (event->type() == QEvent::FocusOut &&
 
-         (d->EditBehavior == ctkMenuComboBox::EditableOnFocus ||
 
-          d->EditBehavior == ctkMenuComboBox::EditableOnPopup))
 
-       {
 
-       d->setComboBoxEditable(false);
 
-       }
 
-     }
 
-   else if (event->type() == QEvent::ActionAdded)
 
-     {
 
-     QActionEvent* actionEvent = static_cast<QActionEvent *>(event);
 
-     d->addAction(actionEvent->action());
 
-     }
 
-   else if (event->type() == QEvent::ActionRemoved)
 
-     {
 
-     QActionEvent* actionEvent = static_cast<QActionEvent *>(event);
 
-     d->removeActionToCompleter(actionEvent->action());
 
-     }
 
-   return this->Superclass::eventFilter(target, event);
 
- }
 
 
  |