1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 // File: QtxPathListEdit.cxx
21 // Author: Sergey TELKOV
23 #include "QtxPathListEdit.h"
25 #include "QtxPathEdit.h"
34 #include <QToolButton>
35 #include <QMessageBox>
36 #include <QFileDialog>
37 #include <QItemDelegate>
38 #include <QStringListModel>
40 static const char* delete_icon[] = {
63 static const char* insert_icon[] = {
88 static const char* movedown_icon[] = {
110 static const char* moveup_icon[] = {
134 \class QtxPathListEdit::Editor
135 \brief Path editor widget
139 class QtxPathListEdit::Editor : public QtxPathEdit
146 Editor( QWidget* parent = 0 ) : QtxPathEdit( parent, true )
148 layout()->setSpacing( 0 );
149 lineEdit()->setFrame( false );
160 \class QtxPathListEdit::Delegate
161 \brief Custom item delegate for the paths list widget.
165 class QtxPathListEdit::Delegate : public QItemDelegate
168 Delegate( QtxPathListEdit*, QObject* = 0 );
171 virtual QWidget* createEditor( QWidget*, const QStyleOptionViewItem&, const QModelIndex& ) const;
172 virtual void setModelData( QWidget*, QAbstractItemModel*, const QModelIndex& ) const;
173 virtual void setEditorData( QWidget*, const QModelIndex& ) const;
174 virtual void paint( QPainter*, const QStyleOptionViewItem&, const QModelIndex& ) const;
177 virtual void drawFocus( QPainter*, const QStyleOptionViewItem&, const QRect& ) const;
180 QtxPathListEdit* myPathEdit;
186 \param pe path list editor
187 \param parent parent widget
189 QtxPathListEdit::Delegate::Delegate( QtxPathListEdit* pe, QObject* parent )
190 : QItemDelegate( parent ),
199 QtxPathListEdit::Delegate::~Delegate()
204 \brief Create editor widget.
206 \param parent parent widget
207 \param option style option
208 \param index data model index
210 QWidget* QtxPathListEdit::Delegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option,
211 const QModelIndex& index ) const
213 return myPathEdit->createEditor( parent );
217 \brief Set modified data back to the data model.
219 \param editor editor widget
220 \param model data model
221 \param index data model index
223 void QtxPathListEdit::Delegate::setModelData( QWidget* editor, QAbstractItemModel* model,
224 const QModelIndex& index ) const
226 myPathEdit->setModelData( editor, index );
230 \brief Set data from the data model to the editor.
232 \param editor editor widget
233 \param index data model index
235 void QtxPathListEdit::Delegate::setEditorData( QWidget* editor, const QModelIndex& index ) const
237 myPathEdit->setEditorData( editor, index );
241 \brief Customize paint operation.
243 \param painter painter
244 \param option style option
245 \param index data model index
247 void QtxPathListEdit::Delegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
248 const QModelIndex& index ) const
250 QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
251 if ( cg == QPalette::Normal && !( option.state & QStyle::State_Active ) )
252 cg = QPalette::Inactive;
254 if ( option.state & QStyle::State_Selected )
256 painter->fillRect( option.rect, option.palette.brush( cg, QPalette::Highlight ) );
257 painter->setPen( option.palette.color( cg, QPalette::HighlightedText ) );
260 painter->setPen( option.palette.color( cg, QPalette::Text ) );
262 QItemDelegate::paint( painter, option, index );
266 \brief Customize drawing selection focus operation.
268 \param painter painter
269 \param option style option
270 \param rect selection rectangle
272 void QtxPathListEdit::Delegate::drawFocus( QPainter* painter, const QStyleOptionViewItem& option,
273 const QRect& rect ) const
275 QItemDelegate::drawFocus( painter, option, option.rect );
279 \class QtxPathListEdit
280 \brief The QtxPathListEdit class represents a widget for files or
281 directories paths list preference items editing.
283 The path list preference item is represented as the list box widget.
284 It provides such operations like adding new file/directory path to the
285 list, removing selected paths and modifying of already entered ones.
287 The widget can be used in two modes: list of files or list of directories.
288 The mode defines the type of the standard browse dialog box which is
289 invoked on the browse button clicking.
291 Initial path list value can be set with setPathList() method. Chosen path
292 list can be retrieved with the pathList() method. The widget mode can be set
293 with setPathType() and retrieved with pathType() method.
295 In addition, it is possible to add path items to the list with the insert()
296 method, remove items with the remove() methods, clear all the widget contents
297 with the clear() method. To get the number of entered paths can be retrieved
298 with the count() method. To check if any path already exists in the paths list,
299 use contains() method.
304 \param type widget mode (Qtx::PathType)
305 \param parent parent widget
306 \sa pathType(), setPathType()
308 QtxPathListEdit::QtxPathListEdit( const Qtx::PathType type, QWidget* parent )
320 Qtx::PT_OpenFile mode is used by default.
322 \param parent parent widget
323 \sa pathType(), setPathType()
325 QtxPathListEdit::QtxPathListEdit( QWidget* parent )
328 myType( Qtx::PT_OpenFile ),
337 QtxPathListEdit::~QtxPathListEdit()
342 \brief Get widget mode.
343 \return currently used widget mode (Qtx::PathType)
346 Qtx::PathType QtxPathListEdit::pathType() const
352 \brief Set widget mode.
353 \param t new widget mode (Qtx::PathType)
356 void QtxPathListEdit::setPathType( const Qtx::PathType t )
368 \brief Get currently selected paths list.
369 \return files or directories paths list entered by the user
372 QStringList QtxPathListEdit::pathList() const
374 return myModel->stringList();
378 \brief Set paths list.
379 \param lst files or directories paths list
382 void QtxPathListEdit::setPathList( const QStringList& lst )
384 myModel->setStringList( lst );
388 \brief Check if the duplication of paths is enabled.
389 \return \c true if the duplication is enabled
391 bool QtxPathListEdit::isDuplicateEnabled() const
397 \brief Enable/disable paths duplication.
398 \param on new flag value
400 void QtxPathListEdit::setDuplicateEnabled( const bool on )
406 \brief Get number of currently entered paths.
407 \return current paths number
409 int QtxPathListEdit::count() const
411 return myModel->rowCount();
415 \brief Check if the specified \a path already exists in
417 \param path path to be checked
418 \return \c true if the path is already selected by the user
419 or \c false otherwise
421 bool QtxPathListEdit::contains( const QString& path ) const
423 return myModel->stringList().contains( path );
427 \brief Clear paths list.
429 void QtxPathListEdit::clear()
431 myModel->removeRows( 0, myModel->rowCount() );
435 \brief Remove path from the paths list.
436 \param idx path index in the list
438 void QtxPathListEdit::remove( const int idx )
440 if ( 0 <= idx && idx < myModel->rowCount() )
441 myModel->removeRow( idx );
445 \brief Remove path from the paths list.
446 \param path path to be removed
448 void QtxPathListEdit::remove( const QString& path )
450 QModelIndexList indexes = myModel->match( myModel->index( 0, 0 ), Qt::DisplayRole, path,
451 myModel->rowCount(), Qt::MatchExactly | Qt::MatchCaseSensitive );
452 while ( !indexes.isEmpty() )
454 myModel->removeRow( indexes.last().row() );
455 indexes.removeLast();
460 \brief Add path to the list of paths.
462 If the specified index is out of range, the path is added to
465 \param path path to be added
466 \param idx index in the list to which the path should be inserted.
468 void QtxPathListEdit::insert( const QString& path, const int idx )
470 int index = idx < 0 ? myModel->rowCount() : qMin( idx, myModel->rowCount() );
471 if ( myModel->insertRow( index ) )
472 myModel->setData( myModel->index( index, 0 ), path, Qt::EditRole );
476 bool QtxPathListEdit::validate( const bool quietMode )
480 QString dirPath = QFileInfo( myEdit->text().stripWhiteSpace() ).filePath();
482 QListBoxItem* found = 0;
483 for (unsigned i = 0; i < myList->count()-1; i++) {
484 QDir aDir(myList->text(i));
485 if ( aDir.canonicalPath().isNull() && myList->text(i) == dir.absPath() ||
486 !aDir.canonicalPath().isNull() && aDir.exists() && aDir.canonicalPath() == dir.canonicalPath()) {
487 found = myList->item(i);
491 if (dirPath.isEmpty()) {
493 // it should be last (empty) item in the list - nothing to do
497 // delete directory from the list
498 removeDir(myLastSelected);
504 if (found != myLastSelected) {
505 // it is forbidden to add directory more then once
507 QMessageBox::critical(this,
509 tr("Directory already specified."),
517 if ( !quietMode && QMessageBox::information(this,
519 tr("%1\n\nThe directory doesn't exist.\nAdd directory anyway?").arg(dir.absPath()),
520 tr("Yes"), tr("No"), QString(), 1, 1) == 1) {
526 appendDir(myLastSelected, dir.absPath());
535 \brief Customize child widget events processing.
536 \param o event receiver object
538 \return \c true if the further event processing should be stopped.
540 bool QtxPathListEdit::eventFilter( QObject* o, QEvent* e )
542 if ( e->type() == QEvent::KeyPress )
544 QKeyEvent* ke = (QKeyEvent*)e;
545 if ( ke->key() == Qt::Key_Delete )
547 else if ( ke->key() == Qt::Key_Insert )
549 else if ( ke->key() == Qt::Key_Up && ke->modifiers() == Qt::CTRL )
554 else if ( ke->key() == Qt::Key_Down && ke->modifiers() == Qt::CTRL )
561 return QFrame::eventFilter( o, e );
565 \brief Called when <Insert> button is clicked.
567 Inserts new empty line to the list and sets input focus to it.
571 void QtxPathListEdit::onInsert( bool /*on*/ )
574 QStringList lst = myModel->stringList();
575 for ( int r = 0; r < lst.count() && empty == -1; r++ )
577 if ( lst.at( r ).isEmpty() )
582 myModel->insertRows( empty = myModel->rowCount(), 1 );
584 QModelIndex idx = myModel->index( empty, 0 );
585 myList->setCurrentIndex( idx );
590 \brief Called when <Delete> button is clicked.
592 Removes currently selected path item.
596 void QtxPathListEdit::onDelete( bool )
598 QModelIndex idx = myList->currentIndex();
599 if ( !idx.isValid() )
602 myModel->removeRow( idx.row() );
606 \brief Called when <Up> button is clicked.
608 Move currently selected path item up to one row in the paths list.
612 void QtxPathListEdit::onUp( bool )
614 QModelIndex idx = myList->currentIndex();
615 if ( !idx.isValid() || idx.row() < 1 )
618 QModelIndex toIdx = myModel->index( idx.row() - 1, 0 );
620 QVariant val = myModel->data( toIdx, Qt::DisplayRole );
621 myModel->setData( toIdx, myModel->data( idx, Qt::DisplayRole ), Qt::DisplayRole );
622 myModel->setData( idx, val, Qt::DisplayRole );
624 myList->setCurrentIndex( toIdx );
628 \brief Called when <Down> button is clicked.
630 Move currently selected path item down to one row in the paths list.
634 void QtxPathListEdit::onDown( bool )
636 QModelIndex idx = myList->currentIndex();
637 if ( !idx.isValid() || idx.row() >= myModel->rowCount() - 1 )
640 QModelIndex toIdx = myModel->index( idx.row() + 1, 0 );
642 QVariant val = myModel->data( toIdx, Qt::DisplayRole );
643 myModel->setData( toIdx, myModel->data( idx, Qt::DisplayRole ), Qt::DisplayRole );
644 myModel->setData( idx, val, Qt::DisplayRole );
646 myList->setCurrentIndex( toIdx );
650 \brief Perform internal widget initialization.
652 void QtxPathListEdit::initialize()
654 QVBoxLayout* base = new QVBoxLayout( this );
655 base->setMargin( 0 );
656 base->setSpacing( 5 );
658 QWidget* cBox = new QWidget( this );
659 base->addWidget( cBox );
661 QHBoxLayout* cLayout = new QHBoxLayout( cBox );
662 cLayout->setMargin( 0 );
663 cLayout->setSpacing( 0 );
665 cLayout->addStretch( 1 );
667 QToolButton* insertBtn = new QToolButton( cBox );
668 insertBtn->setIcon( QPixmap( insert_icon ) );
669 cLayout->addWidget( insertBtn );
671 QToolButton* deleteBtn = new QToolButton( cBox );
672 deleteBtn->setIcon( QPixmap( delete_icon ) );
673 cLayout->addWidget( deleteBtn );
675 QToolButton* upBtn = new QToolButton( cBox );
676 upBtn->setIcon( QPixmap( moveup_icon ) );
677 cLayout->addWidget( upBtn );
679 QToolButton* downBtn = new QToolButton( cBox );
680 downBtn->setIcon( QPixmap( movedown_icon ) );
681 cLayout->addWidget( downBtn );
684 myList = new QListView( this );
685 myList->setAlternatingRowColors( true );
686 myList->setItemDelegate( new Delegate( this, myList ) );
687 myList->setModel( myModel = new QStringListModel( myList ) );
688 myList->setSelectionMode( QListView::SingleSelection );
689 myList->setSelectionBehavior( QListView::SelectRows );
690 myList->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
691 myList->setEditTriggers( QListView::DoubleClicked );
692 myList->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) );
693 myList->installEventFilter( this );
695 base->addWidget( myList );
697 connect( insertBtn, SIGNAL( clicked( bool ) ), this, SLOT( onInsert( bool ) ) );
698 connect( deleteBtn, SIGNAL( clicked( bool ) ), this, SLOT( onDelete( bool ) ) );
699 connect( upBtn, SIGNAL( clicked( bool ) ), this, SLOT( onUp( bool ) ) );
700 connect( downBtn, SIGNAL( clicked( bool ) ), this, SLOT( onDown( bool ) ) );
704 \brief Create editor widget.
705 \param parent parent widget for the editor
706 \return created editor widget
708 QWidget* QtxPathListEdit::createEditor( QWidget* parent )
710 QtxPathEdit* edit = new Editor( parent );
711 edit->setPathType( pathType() );
716 \brief Set modified data from the editor to the list widget.
717 \param editor editor widget
718 \param index data model index
720 void QtxPathListEdit::setModelData( QWidget* editor, const QModelIndex& index )
722 QtxPathEdit* edit = ::qobject_cast<QtxPathEdit*>( editor );
726 QString path = edit->path().trimmed();
728 if ( !isDuplicateEnabled() && !checkDuplicate( path, index.row() ) )
731 if ( !checkExistance( path ) )
734 myModel->setData( index, path, Qt::EditRole );
738 \brief Set data to the editor from the list widget when
739 user starts path edition.
740 \param editor editor widget
741 \param index data model index
743 void QtxPathListEdit::setEditorData( QWidget* editor, const QModelIndex& index )
745 QtxPathEdit* edit = ::qobject_cast<QtxPathEdit*>( editor );
749 QVariant v = myModel->data( index, Qt::EditRole );
750 edit->setPath( v.toString() );
754 \brief Check if path is correct (exists) and optionally
755 show the question message box.
756 \param str path to be checked
757 \param msg if \c true and path does not exist, question message box is shown
758 \return \c true if the user confirms the path adding
760 bool QtxPathListEdit::checkExistance( const QString& str, const bool msg )
762 if ( pathType() == Qtx::PT_SaveFile )
765 QFileInfo aFI = QFileInfo( Qtx::makeEnvVarSubst( str ) );
766 bool ok = aFI.exists();
768 ok = QMessageBox::question( this, tr( "Warning" ), tr( "Path \"%1\" doesn't exist. Add it to list anyway?" ).arg( str ),
769 QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
771 if ( ok && aFI.exists() )
773 switch ( pathType() )
775 case Qtx::PT_OpenFile:
778 QMessageBox::warning( this, tr( "Error" ), tr( "Location \"%1\" doesn't point to file" ).arg( str ) );
780 case Qtx::PT_Directory:
783 QMessageBox::warning( this, tr( "Error" ), tr( "Location \"%1\" doesn't point to directory" ).arg( str ) );
792 \brief Check if path already exists in the list and optionally
793 show the warning message box.
794 \param str path to be checked
795 \param row row corresponding to the path checked
796 \param msg if \c true and path does not exist, warning message box is shown
797 \return \c true if the user confirms the path adding
799 bool QtxPathListEdit::checkDuplicate( const QString& str, const int row, const bool msg )
802 QStringList lst = myModel->stringList();
803 for ( int r = 0; r < lst.count() && cur == -1; r++ )
805 if ( r != row && lst.at( r ) == str )
809 if ( cur != -1 && msg )
810 QMessageBox::warning( this, tr( "Error" ), tr( "Path \"%1\" already exist in the list" ).arg( str ) );