1 // Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 // File: QtxPathListEdit.cxx
23 // Author: Sergey TELKOV
25 #include "QtxPathListEdit.h"
27 #include "QtxPathEdit.h"
36 #include <QToolButton>
37 #include <QMessageBox>
38 #include <QFileDialog>
39 #include <QItemDelegate>
40 #include <QStringListModel>
42 static const char* delete_icon[] = {
65 static const char* insert_icon[] = {
90 static const char* movedown_icon[] = {
112 static const char* moveup_icon[] = {
136 \class QtxPathListEdit::Editor
137 \brief Path editor widget
141 class QtxPathListEdit::Editor : public QtxPathEdit
148 Editor( QWidget* parent = 0 ) : QtxPathEdit( parent )
150 layout()->setSpacing( 0 );
151 lineEdit()->setFrame( false );
162 \class QtxPathListEdit::Delegate
163 \brief Custom item delegate for the paths list widget.
167 class QtxPathListEdit::Delegate : public QItemDelegate
170 Delegate( QtxPathListEdit*, QObject* = 0 );
173 virtual QWidget* createEditor( QWidget*, const QStyleOptionViewItem&, const QModelIndex& ) const;
174 virtual void setModelData( QWidget*, QAbstractItemModel*, const QModelIndex& ) const;
175 virtual void setEditorData( QWidget*, const QModelIndex& ) const;
176 virtual void paint( QPainter*, const QStyleOptionViewItem&, const QModelIndex& ) const;
179 virtual void drawFocus( QPainter*, const QStyleOptionViewItem&, const QRect& ) const;
182 QtxPathListEdit* myPathEdit;
188 \param pe path list editor
189 \param parent parent widget
191 QtxPathListEdit::Delegate::Delegate( QtxPathListEdit* pe, QObject* parent )
192 : QItemDelegate( parent ),
201 QtxPathListEdit::Delegate::~Delegate()
206 \brief Create editor widget.
208 \param parent parent widget
209 \param option style option
210 \param index data model index
212 QWidget* QtxPathListEdit::Delegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option,
213 const QModelIndex& index ) const
215 return myPathEdit->createEditor( parent );
219 \brief Set modified data back to the data model.
221 \param editor editor widget
222 \param model data model
223 \param index data model index
225 void QtxPathListEdit::Delegate::setModelData( QWidget* editor, QAbstractItemModel* model,
226 const QModelIndex& index ) const
228 myPathEdit->setModelData( editor, index );
232 \brief Set data from the data model to the editor.
234 \param editor editor widget
235 \param index data model index
237 void QtxPathListEdit::Delegate::setEditorData( QWidget* editor, const QModelIndex& index ) const
239 myPathEdit->setEditorData( editor, index );
243 \brief Customize paint operation.
245 \param painter painter
246 \param option style option
247 \param index data model index
249 void QtxPathListEdit::Delegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
250 const QModelIndex& index ) const
252 QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
253 if ( cg == QPalette::Normal && !( option.state & QStyle::State_Active ) )
254 cg = QPalette::Inactive;
256 if ( option.state & QStyle::State_Selected )
258 painter->fillRect( option.rect, option.palette.brush( cg, QPalette::Highlight ) );
259 painter->setPen( option.palette.color( cg, QPalette::HighlightedText ) );
262 painter->setPen( option.palette.color( cg, QPalette::Text ) );
264 QItemDelegate::paint( painter, option, index );
268 \brief Customize drawing selection focus operation.
270 \param painter painter
271 \param option style option
272 \param rect selection rectangle
274 void QtxPathListEdit::Delegate::drawFocus( QPainter* painter, const QStyleOptionViewItem& option,
275 const QRect& rect ) const
277 QItemDelegate::drawFocus( painter, option, option.rect );
281 \class QtxPathListEdit
282 \brief The QtxPathListEdit class represents a widget for files or
283 directories paths list preference items editing.
285 The path list preference item is represented as the list box widget.
286 It provides such operations like adding new file/directory path to the
287 list, removing selected paths and modifying of already entered ones.
289 The widget can be used in two modes: list of files or list of directories.
290 The mode defines the type of the standard browse dialog box which is
291 invoked on the browse button clicking.
293 Initial path list value can be set with setPathList() method. Chosen path
294 list can be retrieved with the pathList() method. The widget mode can be set
295 with setPathType() and retrieved with pathType() method.
297 In addition, it is possible to add path items to the list with the insert()
298 method, remove items with the remove() methods, clear all the widget contents
299 with the clear() method. To get the number of entered paths can be retrieved
300 with the count() method. To check if any path already exists in the paths list,
301 use contains() method.
306 \param type widget mode (Qtx::PathType)
307 \param parent parent widget
308 \sa pathType(), setPathType()
310 QtxPathListEdit::QtxPathListEdit( const Qtx::PathType type, QWidget* parent )
322 Qtx::PT_OpenFile mode is used by default.
324 \param parent parent widget
325 \sa pathType(), setPathType()
327 QtxPathListEdit::QtxPathListEdit( QWidget* parent )
330 myType( Qtx::PT_OpenFile ),
339 QtxPathListEdit::~QtxPathListEdit()
344 \brief Get widget mode.
345 \return currently used widget mode (Qtx::PathType)
348 Qtx::PathType QtxPathListEdit::pathType() const
354 \brief Set widget mode.
355 \param t new widget mode (Qtx::PathType)
358 void QtxPathListEdit::setPathType( const Qtx::PathType t )
370 \brief Get currently selected paths list.
371 \return files or directories paths list entered by the user
374 QStringList QtxPathListEdit::pathList() const
376 return myModel->stringList();
380 \brief Set paths list.
381 \param lst files or directories paths list
384 void QtxPathListEdit::setPathList( const QStringList& lst )
386 myModel->setStringList( lst );
390 \brief Check if the duplication of paths is enabled.
391 \return \c true if the duplication is enabled
393 bool QtxPathListEdit::isDuplicateEnabled() const
399 \brief Enable/disable paths duplication.
400 \param on new flag value
402 void QtxPathListEdit::setDuplicateEnabled( const bool on )
408 \brief Get number of currently entered paths.
409 \return current paths number
411 int QtxPathListEdit::count() const
413 return myModel->rowCount();
417 \brief Check if the specified \a path already exists in
419 \param path path to be checked
420 \return \c true if the path is already selected by the user
421 or \c false otherwise
423 bool QtxPathListEdit::contains( const QString& path ) const
425 return myModel->stringList().contains( path );
429 \brief Clear paths list.
431 void QtxPathListEdit::clear()
433 myModel->removeRows( 0, myModel->rowCount() );
437 \brief Remove path from the paths list.
438 \param idx path index in the list
440 void QtxPathListEdit::remove( const int idx )
442 if ( 0 <= idx && idx < myModel->rowCount() )
443 myModel->removeRow( idx );
447 \brief Remove path from the paths list.
448 \param path path to be removed
450 void QtxPathListEdit::remove( const QString& path )
452 QModelIndexList indexes = myModel->match( myModel->index( 0, 0 ), Qt::DisplayRole, path,
453 myModel->rowCount(), Qt::MatchExactly | Qt::MatchCaseSensitive );
454 while ( !indexes.isEmpty() )
456 myModel->removeRow( indexes.last().row() );
457 indexes.removeLast();
462 \brief Add path to the list of paths.
464 If the specified index is out of range, the path is added to
467 \param path path to be added
468 \param idx index in the list to which the path should be inserted.
470 void QtxPathListEdit::insert( const QString& path, const int idx )
472 int index = idx < 0 ? myModel->rowCount() : qMin( idx, myModel->rowCount() );
473 if ( myModel->insertRow( index ) )
474 myModel->setData( myModel->index( index, 0 ), path, Qt::EditRole );
478 bool QtxPathListEdit::validate( const bool quietMode )
482 QString dirPath = QFileInfo( myEdit->text().stripWhiteSpace() ).filePath();
484 QListBoxItem* found = 0;
485 for (unsigned i = 0; i < myList->count()-1; i++) {
486 QDir aDir(myList->text(i));
487 if ( aDir.canonicalPath().isNull() && myList->text(i) == dir.absPath() ||
488 !aDir.canonicalPath().isNull() && aDir.exists() && aDir.canonicalPath() == dir.canonicalPath()) {
489 found = myList->item(i);
493 if (dirPath.isEmpty()) {
495 // it should be last (empty) item in the list - nothing to do
499 // delete directory from the list
500 removeDir(myLastSelected);
506 if (found != myLastSelected) {
507 // it is forbidden to add directory more then once
509 QMessageBox::critical(this,
511 tr("Directory already specified."),
519 if ( !quietMode && QMessageBox::information(this,
521 tr("%1\n\nThe directory doesn't exist.\nAdd directory anyway?").arg(dir.absPath()),
522 tr("Yes"), tr("No"), QString(), 1, 1) == 1) {
528 appendDir(myLastSelected, dir.absPath());
537 \brief Customize child widget events processing.
538 \param o event receiver object
540 \return \c true if the further event processing should be stopped.
542 bool QtxPathListEdit::eventFilter( QObject* o, QEvent* e )
544 if ( e->type() == QEvent::KeyPress )
546 QKeyEvent* ke = (QKeyEvent*)e;
547 if ( ke->key() == Qt::Key_Delete )
549 else if ( ke->key() == Qt::Key_Insert )
551 else if ( ke->key() == Qt::Key_Up && ke->modifiers() == Qt::CTRL )
556 else if ( ke->key() == Qt::Key_Down && ke->modifiers() == Qt::CTRL )
563 return QFrame::eventFilter( o, e );
567 \brief Called when <Insert> button is clicked.
569 Inserts new empty line to the list and sets input focus to it.
573 void QtxPathListEdit::onInsert( bool /*on*/ )
576 QStringList lst = myModel->stringList();
577 for ( int r = 0; r < lst.count() && empty == -1; r++ )
579 if ( lst.at( r ).isEmpty() )
584 myModel->insertRows( empty = myModel->rowCount(), 1 );
586 QModelIndex idx = myModel->index( empty, 0 );
587 myList->setCurrentIndex( idx );
592 \brief Called when <Delete> button is clicked.
594 Removes currently selected path item.
598 void QtxPathListEdit::onDelete( bool )
600 QModelIndex idx = myList->currentIndex();
601 if ( !idx.isValid() )
604 myModel->removeRow( idx.row() );
608 \brief Called when <Up> button is clicked.
610 Move currently selected path item up to one row in the paths list.
614 void QtxPathListEdit::onUp( bool )
616 QModelIndex idx = myList->currentIndex();
617 if ( !idx.isValid() || idx.row() < 1 )
620 QModelIndex toIdx = myModel->index( idx.row() - 1, 0 );
622 QVariant val = myModel->data( toIdx, Qt::DisplayRole );
623 myModel->setData( toIdx, myModel->data( idx, Qt::DisplayRole ), Qt::DisplayRole );
624 myModel->setData( idx, val, Qt::DisplayRole );
626 myList->setCurrentIndex( toIdx );
630 \brief Called when <Down> button is clicked.
632 Move currently selected path item down to one row in the paths list.
636 void QtxPathListEdit::onDown( bool )
638 QModelIndex idx = myList->currentIndex();
639 if ( !idx.isValid() || idx.row() >= myModel->rowCount() - 1 )
642 QModelIndex toIdx = myModel->index( idx.row() + 1, 0 );
644 QVariant val = myModel->data( toIdx, Qt::DisplayRole );
645 myModel->setData( toIdx, myModel->data( idx, Qt::DisplayRole ), Qt::DisplayRole );
646 myModel->setData( idx, val, Qt::DisplayRole );
648 myList->setCurrentIndex( toIdx );
652 \brief Perform internal widget initialization.
654 void QtxPathListEdit::initialize()
656 QVBoxLayout* base = new QVBoxLayout( this );
657 base->setMargin( 0 );
658 base->setSpacing( 5 );
660 QWidget* cBox = new QWidget( this );
661 base->addWidget( cBox );
663 QHBoxLayout* cLayout = new QHBoxLayout( cBox );
664 cLayout->setMargin( 0 );
665 cLayout->setSpacing( 0 );
667 cLayout->addStretch( 1 );
669 QToolButton* insertBtn = new QToolButton( cBox );
670 insertBtn->setIcon( QPixmap( insert_icon ) );
671 cLayout->addWidget( insertBtn );
673 QToolButton* deleteBtn = new QToolButton( cBox );
674 deleteBtn->setIcon( QPixmap( delete_icon ) );
675 cLayout->addWidget( deleteBtn );
677 QToolButton* upBtn = new QToolButton( cBox );
678 upBtn->setIcon( QPixmap( moveup_icon ) );
679 cLayout->addWidget( upBtn );
681 QToolButton* downBtn = new QToolButton( cBox );
682 downBtn->setIcon( QPixmap( movedown_icon ) );
683 cLayout->addWidget( downBtn );
686 myList = new QListView( this );
687 myList->setAlternatingRowColors( true );
688 myList->setItemDelegate( new Delegate( this, myList ) );
689 myList->setModel( myModel = new QStringListModel( myList ) );
690 myList->setSelectionMode( QListView::SingleSelection );
691 myList->setSelectionBehavior( QListView::SelectRows );
692 myList->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
693 myList->setEditTriggers( QListView::DoubleClicked );
694 myList->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) );
695 myList->installEventFilter( this );
697 base->addWidget( myList );
699 connect( insertBtn, SIGNAL( clicked( bool ) ), this, SLOT( onInsert( bool ) ) );
700 connect( deleteBtn, SIGNAL( clicked( bool ) ), this, SLOT( onDelete( bool ) ) );
701 connect( upBtn, SIGNAL( clicked( bool ) ), this, SLOT( onUp( bool ) ) );
702 connect( downBtn, SIGNAL( clicked( bool ) ), this, SLOT( onDown( bool ) ) );
706 \brief Create editor widget.
707 \param parent parent widget for the editor
708 \return created editor widget
710 QWidget* QtxPathListEdit::createEditor( QWidget* parent )
712 QtxPathEdit* edit = new Editor( parent );
713 edit->setPathType( pathType() );
718 \brief Set modified data from the editor to the list widget.
719 \param editor editor widget
720 \param index data model index
722 void QtxPathListEdit::setModelData( QWidget* editor, const QModelIndex& index )
724 QtxPathEdit* edit = ::qobject_cast<QtxPathEdit*>( editor );
728 QString path = edit->path().trimmed();
730 if ( !isDuplicateEnabled() && !checkDuplicate( path, index.row() ) )
733 if ( !checkExistance( path ) )
736 myModel->setData( index, path, Qt::EditRole );
740 \brief Set data to the editor from the list widget when
741 user starts path edition.
742 \param editor editor widget
743 \param index data model index
745 void QtxPathListEdit::setEditorData( QWidget* editor, const QModelIndex& index )
747 QtxPathEdit* edit = ::qobject_cast<QtxPathEdit*>( editor );
751 QVariant v = myModel->data( index, Qt::EditRole );
752 edit->setPath( v.toString() );
756 \brief Check if path is correct (exists) and optionally
757 show the question message box.
758 \param str path to be checked
759 \param msg if \c true and path does not exist, question message box is shown
760 \return \c true if the user confirms the path adding
762 bool QtxPathListEdit::checkExistance( const QString& str, const bool msg )
764 if ( pathType() == Qtx::PT_SaveFile )
767 bool ok = QFileInfo( str ).exists();
769 ok = QMessageBox::question( this, tr( "Warning" ), tr( "Path \"%1\" doesn't exist. Add it to list anyway?" ).arg( str ),
770 QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes;
772 if ( ok && QFileInfo( str ).exists() )
774 switch ( pathType() )
776 case Qtx::PT_OpenFile:
777 ok = QFileInfo( str ).isFile();
779 QMessageBox::warning( this, tr( "Error" ), tr( "Location \"%1\" doesn't point to file" ).arg( str ) );
781 case Qtx::PT_Directory:
782 ok = QFileInfo( str ).isDir();
784 QMessageBox::warning( this, tr( "Error" ), tr( "Location \"%1\" doesn't point to directory" ).arg( str ) );
793 \brief Check if path already exists in the list and optionally
794 show the warning message box.
795 \param str path to be checked
796 \param row row corresponding to the path checked
797 \param msg if \c true and path does not exist, warning message box is shown
798 \return \c true if the user confirms the path adding
800 bool QtxPathListEdit::checkDuplicate( const QString& str, const int row, const bool msg )
803 QStringList lst = myModel->stringList();
804 for ( int r = 0; r < lst.count() && cur == -1; r++ )
806 if ( r != row && lst.at( r ) == str )
810 if ( cur != -1 && msg )
811 QMessageBox::warning( this, tr( "Error" ), tr( "Path \"%1\" already exist in the list" ).arg( str ) );