-rw-r--r-- | library/fileselector.cpp | 470 |
1 files changed, 340 insertions, 130 deletions
diff --git a/library/fileselector.cpp b/library/fileselector.cpp index 013f43a..382012f 100644 --- a/library/fileselector.cpp +++ b/library/fileselector.cpp @@ -1,353 +1,563 @@ /********************************************************************** -** Copyright (C) 2000 Trolltech AS. All rights reserved. +** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** -** This file is part of Qtopia Environment. +** This file is part of the Qtopia Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ // WARNING: Do *NOT* define this yourself. The SL5xxx from SHARP does NOT // have this class. #define QTOPIA_INTERNAL_FSLP #include "fileselector.h" #include "fileselector_p.h" #include "global.h" #include "resource.h" #include "config.h" #include "applnk.h" #include "storage.h" #include "qpemenubar.h" +#ifdef QWS #include "qcopchannel_qws.h" +#endif #include "lnkproperties.h" #include "applnk.h" #include "qpeapplication.h" #include "categorymenu.h" +#include "categoryselect.h" +#include "mimetype.h" +#include "categories.h" #include <stdlib.h> #include <qdir.h> #include <qwidget.h> #include <qpopupmenu.h> #include <qtoolbutton.h> #include <qpushbutton.h> #include <qheader.h> #include <qtooltip.h> +#include <qwhatsthis.h> +class TypeCombo : public QComboBox +{ + Q_OBJECT +public: + TypeCombo( QWidget *parent, const char *name=0 ) + : QComboBox( parent, name ) + { + connect( this, SIGNAL(activated(int)), this, SLOT(selectType(int)) ); + } + + void reread( DocLnkSet &files, const QString &filter ); + +signals: + void selected( const QString & ); + +protected slots: + void selectType( int idx ) { + emit selected( typelist[idx] ); + } + +protected: + QStringList typelist; + QString prev; +}; + +void TypeCombo::reread( DocLnkSet &files, const QString &filter ) +{ + typelist.clear(); + QStringList filters = QStringList::split( ';', filter ); + int pos = filter.find( '/' ); + //### do for each filter + if ( filters.count() == 1 && pos >= 0 && filter[pos+1] != '*' ) { + typelist.append( filter ); + clear(); + QString minor = filter.mid( pos+1 ); + minor[0] = minor[0].upper(); + insertItem( tr("%1 files").arg(minor) ); + setCurrentItem(0); + setEnabled( FALSE ); + return; + } + + QListIterator<DocLnk> dit( files.children() ); + for ( ; dit.current(); ++dit ) { + if ( !typelist.contains( (*dit)->type() ) ) + typelist.append( (*dit)->type() ); + } + + QStringList types; + QStringList::ConstIterator it; + for (it = typelist.begin(); it!=typelist.end(); ++it) { + QString t = *it; + if ( t.left(12) == "application/" ) { + MimeType mt(t); + const AppLnk* app = mt.application(); + if ( app ) + t = app->name(); + else + t = t.mid(12); + } else { + QString major, minor; + int pos = t.find( '/' ); + if ( pos >= 0 ) { + major = t.left( pos ); + minor = t.mid( pos+1 ); + } + if ( minor.find( "x-" ) == 0 ) + minor = minor.mid( 2 ); + minor[0] = minor[0].upper(); + major[0] = major[0].upper(); + if ( filters.count() > 1 ) + t = tr("%1 %2", "minor mimetype / major mimetype").arg(minor).arg(major); + else + t = minor; + } + types += tr("%1 files").arg(t); + } + for (it = filters.begin(); it!=filters.end(); ++it) { + typelist.append( *it ); + int pos = (*it).find( '/' ); + if ( pos >= 0 ) { + QString maj = (*it).left( pos ); + maj[0] = maj[0].upper(); + types << tr("All %1 files").arg(maj); + } + } + if ( filters.count() > 1 ) { + typelist.append( filter ); + types << tr("All files"); + } + prev = currentText(); + clear(); + insertStringList(types); + for (int i=0; i<count(); i++) { + if ( text(i) == prev ) { + setCurrentItem(i); + break; + } + } + if ( prev.isNull() ) + setCurrentItem(count()-1); + setEnabled( TRUE ); +} + + +//=========================================================================== FileSelectorItem::FileSelectorItem( QListView *parent, const DocLnk &f ) : QListViewItem( parent ), fl( f ) { setText( 0, f.name() ); setPixmap( 0, f.pixmap() ); } FileSelectorItem::~FileSelectorItem() { } -class FileSelectorViewPrivate -{ -public: - CategoryMenu *cm; - bool m_noItems:1; -}; - -FileSelectorView::FileSelectorView( const QString &f, QWidget *parent, const char *name ) - : QListView( parent, name ), filter( f ), count( 0 ) +FileSelectorView::FileSelectorView( QWidget *parent, const char *name ) + : QListView( parent, name ) { - d = new FileSelectorViewPrivate(); - d->cm = 0; - d->m_noItems = false; setAllColumnsShowFocus( TRUE ); addColumn( tr( "Name" ) ); header()->hide(); - - fileManager = new FileManager; - reread(); - QCopChannel *channel = new QCopChannel( "QPE/Card", this ); - connect( channel, SIGNAL(received(const QCString &, const QByteArray &)), - this, SLOT(cardMessage( const QCString &, const QByteArray &)) ); } FileSelectorView::~FileSelectorView() { } -void FileSelectorView::reread() -{ - QString oldFile; - FileSelectorItem *item; - if( !d->m_noItems ) { // there are items - item = (FileSelectorItem *)selectedItem(); - if ( item ) - oldFile = item->file().file(); - } - clear(); - DocLnkSet files; - Global::findDocuments(&files, filter); - count = files.children().count(); - if(count == 0 ){ // No Documents - d->m_noItems = true; - QListViewItem *it = new QListViewItem(this, tr("There are no files in this directory." ), "empty" ); - it->setSelectable(FALSE ); - return; - } - QListIterator<DocLnk> dit( files.children() ); - for ( ; dit.current(); ++dit ) { - if (d->cm) - if (!d->cm->isSelected((**dit).categories())) - continue; - item = new FileSelectorItem( this, **dit ); - if ( item->file().file() == oldFile ) - setCurrentItem( item ); - } - if ( !selectedItem() ) - setCurrentItem( firstChild() ); -} - -void FileSelectorView::setCategoryFilter(CategoryMenu *cm) -{ - d->cm = cm; - connect(cm, SIGNAL(categoryChange()), this, SLOT(categoryChanged()) ); -} - -void FileSelectorView::categoryChanged() { reread(); } - -void FileSelectorView::cardMessage( const QCString &msg, const QByteArray &) -{ - if ( msg == "mtabChanged()" ) - reread(); -} - void FileSelectorView::keyPressEvent( QKeyEvent *e ) { QString txt = e->text(); if (e->key() == Key_Space) emit returnPressed( currentItem() ); else if ( !txt.isNull() && txt[0] > ' ' && e->key() < 0x1000 ) e->ignore(); else QListView::keyPressEvent(e); } +class NewDocItem : public FileSelectorItem +{ +public: + NewDocItem( QListView *parent, const DocLnk &f ) + : FileSelectorItem( parent, f ) { + setText( 0, QObject::tr("New Document") ); + QImage img( Resource::loadImage( "new" ) ); + QPixmap pm; + pm = img.smoothScale( AppLnk::smallIconSize(), AppLnk::smallIconSize() ); + setPixmap( 0, pm ); + } + QString key ( int, bool ) const { + return QString("\n"); + } + + void paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int alignment ) { + QFont oldFont = p->font(); + QFont newFont = p->font(); + newFont.setWeight( QFont::Bold ); + p->setFont( newFont ); + FileSelectorItem::paintCell( p, cg, column, width, alignment ); + p->setFont( oldFont ); + } + + int width( const QFontMetrics &fm, const QListView *v, int c ) const { + return FileSelectorItem::width( fm, v, c )*4/3; // allow for bold font + } +}; + +//=========================================================================== + class FileSelectorPrivate { public: - CategoryMenu *cm; - QMenuBar *mb; + TypeCombo *typeCombo; + CategorySelect *catSelect; + QValueList<QRegExp> mimeFilters; + int catId; + bool showNew; + NewDocItem *newDocItem; + DocLnkSet files; + QHBox *toolbar; }; /*! \class FileSelector fileselector.h \brief The FileSelector widget allows the user to select DocLnk objects. + + This class presents a file selection dialog to the user. This widget + is usually the first widget seen in a \link docwidget.html + document-oriented application\endlink. The developer will most often + create this widget in combination with a <a + href="../qt/qwidgetstack.html"> QWidgetStack</a> and the appropriate + editor and/or viewer widget for their application. This widget + should be shown first and the user can the select which document + they wish to operate on. Please refer to the implementation of + texteditor for an example of how to tie these classes together. + + Use setNewVisible() depending on whether the application can be used + to create new files or not. Use setCloseVisible() depending on + whether the user may leave the dialog without creating or selecting + a document or not. The number of files in the view is available from + fileCount(). To force the view to be updated call reread(). + + If the user presses the 'New Document' button the newSelected() + signal is emitted. If the user selects an existing file the + fileSelected() signal is emitted. The selected file's \link + doclnk.html DocLnk\endlink is available from the selected() + function. If the file selector is no longer necessary the closeMe() + signal is emitted. + + \ingroup qtopiaemb + \sa FileManager */ /*! Constructs a FileSelector with mime filter \a f. The standard Qt \a parent and \a name parameters are passed to the - parent. + parent widget. - If \a newVisible is TRUE, the widget has an button allowing the user - the create "new" documents - editor applications will have this while - viewer applications will not. + If \a newVisible is TRUE, the widget has a button to allow the user + the create "new" documents; this is useful for applications that can + create and edit documents but not suitable for applications that + only provide viewing. - If \a closeVisible is TRUE, the widget has an button allowinf the user - to select "no document". + \a closeVisible is deprecated \sa DocLnkSet::DocLnkSet() */ FileSelector::FileSelector( const QString &f, QWidget *parent, const char *name, bool newVisible, bool closeVisible ) : QVBox( parent, name ), filter( f ) { setMargin( 0 ); setSpacing( 0 ); - QHBox *top = new QHBox( this ); - top->setBackgroundMode( PaletteButton ); // same colour as toolbars - top->setSpacing( 0 ); - - QWidget *spacer = new QWidget( top ); - spacer->setBackgroundMode( PaletteButton ); d = new FileSelectorPrivate(); - d->mb = new QMenuBar(spacer); - d->cm = new CategoryMenu("Document View", this); - QPEMenuToolFocusManager::manager()->addWidget( d->mb ); - d->mb->insertItem(tr("View"), d->cm); + d->newDocItem = 0; + d->showNew = newVisible; + d->catId = -2; // All files + d->toolbar = new QHBox( this ); + d->toolbar->setBackgroundMode( PaletteButton ); // same colour as toolbars + d->toolbar->setSpacing( 0 ); + d->toolbar->hide(); - QToolButton *tb = new QToolButton( top ); - tb->setPixmap( Resource::loadPixmap( "new" ) ); - connect( tb, SIGNAL( clicked() ), this, SLOT( createNew() ) ); - buttonNew = tb; - tb->setFixedSize( 18, 20 ); // tb->sizeHint() ); - tb->setAutoRaise( TRUE ); - QToolTip::add( tb, tr( "Create a new Document" ) ); - QPEMenuToolFocusManager::manager()->addWidget( tb ); + QWidget *spacer = new QWidget( d->toolbar ); + spacer->setBackgroundMode( PaletteButton ); - tb = new QToolButton( top ); + QToolButton *tb = new QToolButton( d->toolbar ); tb->setPixmap( Resource::loadPixmap( "close" ) ); connect( tb, SIGNAL( clicked() ), this, SIGNAL( closeMe() ) ); buttonClose = tb; tb->setFixedSize( 18, 20 ); // tb->sizeHint() ); tb->setAutoRaise( TRUE ); QToolTip::add( tb, tr( "Close the File Selector" ) ); QPEMenuToolFocusManager::manager()->addWidget( tb ); - view = new FileSelectorView( filter, this, "fileview" ); - view->setCategoryFilter(d->cm); + view = new FileSelectorView( this, "fileview" ); QPEApplication::setStylusOperation( view->viewport(), QPEApplication::RightOnHold ); connect( view, SIGNAL( mouseButtonClicked( int, QListViewItem *, const QPoint &, int ) ), this, SLOT( fileClicked( int, QListViewItem *, const QPoint &, int ) ) ); connect( view, SIGNAL( mouseButtonPressed( int, QListViewItem *, const QPoint &, int ) ), this, SLOT( filePressed( int, QListViewItem *, const QPoint &, int ) ) ); connect( view, SIGNAL( returnPressed( QListViewItem * ) ), this, SLOT( fileClicked( QListViewItem * ) ) ); - setNewVisible( newVisible ); + QHBox *hb = new QHBox( this ); + d->typeCombo = new TypeCombo( hb ); + connect( d->typeCombo, SIGNAL(selected(const QString&)), + this, SLOT(typeSelected(const QString&)) ); + QWhatsThis::add( d->typeCombo, tr("Show documents of this type") ); + + Categories c; + c.load(categoryFileName()); + QArray<int> vl( 0 ); + d->catSelect = new CategorySelect( hb ); + d->catSelect->setRemoveCategoryEdit( TRUE ); + d->catSelect->setCategories( vl, "Document View", tr("Document View") ); + d->catSelect->setAllCategories( TRUE ); + connect( d->catSelect, SIGNAL(signalSelected(int)), this, SLOT(catSelected(int)) ); + QWhatsThis::add( d->catSelect, tr("Show documents in this category") ); + setCloseVisible( closeVisible ); + + QCopChannel *channel = new QCopChannel( "QPE/Card", this ); + connect( channel, SIGNAL(received(const QCString &, const QByteArray &)), + this, SLOT(cardMessage( const QCString &, const QByteArray &)) ); + + reread(); + updateWhatsThis(); } /*! Destroys the widget. */ FileSelector::~FileSelector() { - + delete d; } /*! - Returns the number of files in the view. If this is zero, and editor - application might avoid using the selector and immediately start with + Returns the number of files in the view. If this is zero, an editor + application might bypass the selector and immediately start with a "new" document. */ int FileSelector::fileCount() { - return view->fileCount(); + return d->files.children().count();; } /*! - Causes the file selector to act as if the "new" button was chosen. + Calling this function is the programmatic equivalent of the user + pressing the "new" button. \sa newSelected(), closeMe() */ void FileSelector::createNew() { DocLnk f; emit newSelected( f ); emit closeMe(); } void FileSelector::fileClicked( int button, QListViewItem *i, const QPoint &, int ) { if ( !i ) return; - if(i->text(1) == QString::fromLatin1("empty" ) ) - return; - if ( button == Qt::LeftButton ) { fileClicked( i ); } } void FileSelector::filePressed( int button, QListViewItem *i, const QPoint &, int ) { - if ( !i ) + if ( !i || i == d->newDocItem ) return; - if(i->text(1) == QString::fromLatin1("empty" ) ) - return; - if ( button == Qt::RightButton ) { DocLnk l = ((FileSelectorItem *)i)->file(); LnkProperties prop( &l ); prop.showMaximized(); prop.exec(); - d->cm->reload(); reread(); } } void FileSelector::fileClicked( QListViewItem *i ) { if ( !i ) return; - emit fileSelected( ( (FileSelectorItem*)i )->file() ); - emit closeMe(); + if ( i == d->newDocItem ) { + createNew(); + } else { + emit fileSelected( ( (FileSelectorItem*)i )->file() ); + emit closeMe(); + } +} + +void FileSelector::typeSelected( const QString &type ) +{ + d->mimeFilters.clear(); + QStringList subFilter = QStringList::split(";", type); + for( QStringList::Iterator it = subFilter.begin(); it != subFilter.end(); ++it ) + d->mimeFilters.append( QRegExp(*it, FALSE, TRUE) ); + updateView(); } +void FileSelector::catSelected( int c ) +{ + d->catId = c; + updateView(); +} + +void FileSelector::cardMessage( const QCString &msg, const QByteArray &) +{ + if ( msg == "mtabChanged()" ) + reread(); +} + + /*! - Returns the selected DocLnk. The caller is responsible for deleting - the returned value. + Returns the selected \link doclnk.html DocLnk\endlink. The caller is + responsible for deleting the returned value. */ const DocLnk *FileSelector::selected() { FileSelectorItem *item = (FileSelectorItem *)view->selectedItem(); - if ( item ) + if ( item && item != d->newDocItem ) return new DocLnk( item->file() ); return NULL; } /*! \fn void FileSelector::fileSelected( const DocLnk &f ) - This signal is emitted when the user selects a file. - \a f is the file. + This signal is emitted when the user selects a document. + \a f is the document. */ /*! \fn void FileSelector::newSelected( const DocLnk &f ) - This signal is emitted when the user selects "new" file. - \a f is a DocLnk for the file. You will need to set the type - of the value after copying it. + This signal is emitted when the user selects a "new" document. + \a f is a DocLnk for the document. You will need to set the type + of the document after copying it. */ /*! \fn void FileSelector::closeMe() This signal is emitted when the user no longer needs to view the widget. */ /*! - Sets whether a "new document" button is visible, according to \a b. + If \a b is TRUE a "new document" entry is visible; if \a b is FALSE + this entry is not visible and the user is unable to create new + documents from the dialog. */ void FileSelector::setNewVisible( bool b ) { - if ( b ) - buttonNew->show(); - else - buttonNew->hide(); + if ( d->showNew != b ) { + d->showNew = b; + updateView(); + updateWhatsThis(); + } } /*! - Sets whether a "no document" button is visible, according to \a b. + If \a b is TRUE a "close" or "no document" button is visible; if \a + b is FALSE this button is not visible and the user is unable to + leave the dialog without creating or selecting a document. + + This function is deprecated. */ void FileSelector::setCloseVisible( bool b ) { - if ( b ) - buttonClose->show(); + if ( b ) + d->toolbar->show(); else - buttonClose->hide(); + d->toolbar->hide(); } /*! - Rereads the list of files. + Rereads the list of documents. */ void FileSelector::reread() { - view->reread(); + d->files.clear(); + Global::findDocuments(&d->files, filter); + d->typeCombo->reread( d->files, filter ); + updateView(); } +void FileSelector::updateView() +{ + FileSelectorItem *item = (FileSelectorItem *)view->selectedItem(); + if ( item == d->newDocItem ) + item = 0; + QString oldFile; + if ( item ) + oldFile = item->file().file(); + view->clear(); + QListIterator<DocLnk> dit( d->files.children() ); + for ( ; dit.current(); ++dit ) { + bool mimeMatch = FALSE; + if ( d->mimeFilters.count() ) { + QValueList<QRegExp>::Iterator it; + for ( it = d->mimeFilters.begin(); it != d->mimeFilters.end(); ++it ) { + if ( (*it).match((*dit)->type()) >= 0 ) { + mimeMatch = TRUE; + break; + } + } + } else { + mimeMatch = TRUE; + } + if ( mimeMatch && + (d->catId == -2 || (*dit)->categories().contains(d->catId) || + (d->catId == -1 && (*dit)->categories().isEmpty())) ) { + item = new FileSelectorItem( view, **dit ); + if ( item->file().file() == oldFile ) + view->setCurrentItem( item ); + } + } + + if ( d->showNew ) + d->newDocItem = new NewDocItem( view, DocLnk() ); + else + d->newDocItem = 0; + + if ( !view->selectedItem() || view->childCount() == 1 ) { + view->setCurrentItem( view->firstChild() ); + view->setSelected( view->firstChild(), TRUE ); + } +} + +void FileSelector::updateWhatsThis() +{ + QWhatsThis::remove( this ); + QString text = tr("Click to select a document from the list"); + if ( d->showNew ) + text += tr(", or select <b>New Document</b> to create a new document."); + text += tr("<br><br>Click and hold for document properties."); + QWhatsThis::add( this, text ); +} + +#include "fileselector.moc" + |