kfiledialog.cpp
00001 // -*- c++ -*- 00002 /* This file is part of the KDE libraries 00003 Copyright (C) 1997, 1998 Richard Moore <rich@kde.org> 00004 1998 Stephan Kulow <coolo@kde.org> 00005 1998 Daniel Grana <grana@ie.iwi.unibe.ch> 00006 1999,2000,2001,2002,2003 Carsten Pfeiffer <pfeiffer@kde.org> 00007 2003 Clarence Dang <dang@kde.org> 00008 00009 This library is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU Library General Public 00011 License as published by the Free Software Foundation; either 00012 version 2 of the License, or (at your option) any later version. 00013 00014 This library is distributed in the hope that it will be useful, 00015 but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 Library General Public License for more details. 00018 00019 You should have received a copy of the GNU Library General Public License 00020 along with this library; see the file COPYING.LIB. If not, write to 00021 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00022 Boston, MA 02110-1301, USA. 00023 */ 00024 00025 #include "kfiledialog.h" 00026 00027 #include <unistd.h> 00028 #include <stdlib.h> 00029 #include <stdio.h> 00030 00031 #include <qptrcollection.h> 00032 #include <qcheckbox.h> 00033 #include <qcombobox.h> 00034 #include <qlabel.h> 00035 #include <qlayout.h> 00036 #include <qlineedit.h> 00037 #include <qptrlist.h> 00038 #include <qpixmap.h> 00039 #include <qtextcodec.h> 00040 #include <qtooltip.h> 00041 #include <qtimer.h> 00042 #include <qwhatsthis.h> 00043 #include <qfiledialog.h> 00044 00045 #include <kaccel.h> 00046 #include <kaction.h> 00047 #include <kapplication.h> 00048 #include <kcharsets.h> 00049 #include <kcmdlineargs.h> 00050 #include <kcompletionbox.h> 00051 #include <kconfig.h> 00052 #include <kdebug.h> 00053 #include <kglobal.h> 00054 #include <kglobalsettings.h> 00055 #include <kiconloader.h> 00056 #include <kimageio.h> 00057 #include <kio/job.h> 00058 #include <kio/netaccess.h> 00059 #include <kio/scheduler.h> 00060 #include <kio/kservicetypefactory.h> 00061 #include <klocale.h> 00062 #include <kmessagebox.h> 00063 #include <kmimetype.h> 00064 #include <kpopupmenu.h> 00065 #include <kprotocolinfo.h> 00066 #include <kpushbutton.h> 00067 #include <krecentdirs.h> 00068 #include <kshell.h> 00069 #include <kstandarddirs.h> 00070 #include <kstdguiitem.h> 00071 #include <kstaticdeleter.h> 00072 #include <ktoolbar.h> 00073 #include <ktoolbarbutton.h> 00074 #include <kurl.h> 00075 #include <kurlcombobox.h> 00076 #include <kurlcompletion.h> 00077 #include <kuser.h> 00078 00079 #include "config-kfile.h" 00080 #include "kpreviewwidgetbase.h" 00081 00082 #include <kdirselectdialog.h> 00083 #include <kfileview.h> 00084 #include <krecentdocument.h> 00085 #include <kfilefiltercombo.h> 00086 #include <kdiroperator.h> 00087 #include <kimagefilepreview.h> 00088 00089 #include <kfilespeedbar.h> 00090 #include <kfilebookmarkhandler.h> 00091 00092 #ifdef Q_WS_X11 00093 #include <X11/Xlib.h> 00094 #include <fixx11h.h> 00095 #endif 00096 00097 enum Buttons { HOTLIST_BUTTON, 00098 PATH_COMBO, CONFIGURE_BUTTON }; 00099 00100 template class QPtrList<KIO::StatJob>; 00101 00102 namespace { 00103 static void silenceQToolBar(QtMsgType, const char *) 00104 { 00105 } 00106 } 00107 00108 struct KFileDialogPrivate 00109 { 00110 // the last selected url 00111 KURL url; 00112 00113 // the selected filenames in multiselection mode -- FIXME 00114 QString filenames; 00115 00116 // the name of the filename set by setSelection 00117 QString selection; 00118 00119 // now following all kind of widgets, that I need to rebuild 00120 // the geometry management 00121 QBoxLayout *boxLayout; 00122 QWidget *mainWidget; 00123 00124 QLabel *locationLabel; 00125 00126 // @deprecated remove in KDE4 00127 QLabel *filterLabel; 00128 KURLComboBox *pathCombo; 00129 KPushButton *okButton, *cancelButton; 00130 KFileSpeedBar *urlBar; 00131 QHBoxLayout *urlBarLayout; 00132 QWidget *customWidget; 00133 00134 // Automatically Select Extension stuff 00135 QCheckBox *autoSelectExtCheckBox; 00136 bool autoSelectExtChecked; // whether or not the _user_ has checked the above box 00137 QString extension; // current extension for this filter 00138 00139 QPtrList<KIO::StatJob> statJobs; 00140 00141 KURL::List urlList; //the list of selected urls 00142 00143 QStringList mimetypes; //the list of possible mimetypes to save as 00144 00145 // indicates if the location edit should be kept or cleared when changing 00146 // directories 00147 bool keepLocation :1; 00148 00149 // the KDirOperators view is set in KFileDialog::show(), so to avoid 00150 // setting it again and again, we have this nice little boolean :) 00151 bool hasView :1; 00152 00153 bool hasDefaultFilter :1; // necessary for the operationMode 00154 KFileDialog::OperationMode operationMode; 00155 00156 // The file class used for KRecentDirs 00157 QString fileClass; 00158 00159 KFileBookmarkHandler *bookmarkHandler; 00160 00161 // the ID of the path drop down so subclasses can place their custom widgets properly 00162 int m_pathComboIndex; 00163 }; 00164 00165 KURL *KFileDialog::lastDirectory; // to set the start path 00166 00167 static KStaticDeleter<KURL> ldd; 00168 00169 KFileDialog::KFileDialog(const QString& startDir, const QString& filter, 00170 QWidget *parent, const char* name, bool modal) 00171 : KDialogBase( parent, name, modal, QString::null, 0 ) 00172 { 00173 init( startDir, filter, 0 ); 00174 } 00175 00176 KFileDialog::KFileDialog(const QString& startDir, const QString& filter, 00177 QWidget *parent, const char* name, bool modal, QWidget* widget) 00178 : KDialogBase( parent, name, modal, QString::null, 0 ) 00179 { 00180 init( startDir, filter, widget ); 00181 } 00182 00183 00184 KFileDialog::~KFileDialog() 00185 { 00186 hide(); 00187 00188 KConfig *config = KGlobal::config(); 00189 00190 if (d->urlBar) 00191 d->urlBar->save( config ); 00192 00193 config->sync(); 00194 00195 delete d->bookmarkHandler; // Should be deleted before ops! 00196 delete ops; 00197 delete d; 00198 } 00199 00200 void KFileDialog::setLocationLabel(const QString& text) 00201 { 00202 d->locationLabel->setText(text); 00203 } 00204 00205 void KFileDialog::setFilter(const QString& filter) 00206 { 00207 int pos = filter.find('/'); 00208 00209 // Check for an un-escaped '/', if found 00210 // interpret as a MIME filter. 00211 00212 if (pos > 0 && filter[pos - 1] != '\\') { 00213 QStringList filters = QStringList::split( " ", filter ); 00214 setMimeFilter( filters ); 00215 return; 00216 } 00217 00218 // Strip the escape characters from 00219 // escaped '/' characters. 00220 00221 QString copy (filter); 00222 for (pos = 0; (pos = copy.find("\\/", pos)) != -1; ++pos) 00223 copy.remove(pos, 1); 00224 00225 ops->clearFilter(); 00226 filterWidget->setFilter(copy); 00227 ops->setNameFilter(filterWidget->currentFilter()); 00228 d->hasDefaultFilter = false; 00229 filterWidget->setEditable( true ); 00230 00231 updateAutoSelectExtension (); 00232 } 00233 00234 QString KFileDialog::currentFilter() const 00235 { 00236 return filterWidget->currentFilter(); 00237 } 00238 00239 // deprecated 00240 void KFileDialog::setFilterMimeType(const QString &label, 00241 const KMimeType::List &types, 00242 const KMimeType::Ptr &defaultType) 00243 { 00244 d->mimetypes.clear(); 00245 d->filterLabel->setText(label); 00246 00247 KMimeType::List::ConstIterator it; 00248 for( it = types.begin(); it != types.end(); ++it) 00249 d->mimetypes.append( (*it)->name() ); 00250 00251 setMimeFilter( d->mimetypes, defaultType->name() ); 00252 } 00253 00254 void KFileDialog::setMimeFilter( const QStringList& mimeTypes, 00255 const QString& defaultType ) 00256 { 00257 d->mimetypes = mimeTypes; 00258 filterWidget->setMimeFilter( mimeTypes, defaultType ); 00259 00260 QStringList types = QStringList::split(" ", filterWidget->currentFilter()); 00261 types.append( QString::fromLatin1( "inode/directory" )); 00262 ops->clearFilter(); 00263 ops->setMimeFilter( types ); 00264 d->hasDefaultFilter = !defaultType.isEmpty(); 00265 filterWidget->setEditable( !d->hasDefaultFilter || 00266 d->operationMode != Saving ); 00267 00268 updateAutoSelectExtension (); 00269 } 00270 00271 void KFileDialog::clearFilter() 00272 { 00273 d->mimetypes.clear(); 00274 filterWidget->setFilter( QString::null ); 00275 ops->clearFilter(); 00276 d->hasDefaultFilter = false; 00277 filterWidget->setEditable( true ); 00278 00279 updateAutoSelectExtension (); 00280 } 00281 00282 QString KFileDialog::currentMimeFilter() const 00283 { 00284 int i = filterWidget->currentItem(); 00285 if (filterWidget->showsAllTypes()) 00286 i--; 00287 00288 if ((i >= 0) && (i < (int) d->mimetypes.count())) 00289 return d->mimetypes[i]; 00290 return QString::null; // The "all types" item has no mimetype 00291 } 00292 00293 KMimeType::Ptr KFileDialog::currentFilterMimeType() 00294 { 00295 return KMimeType::mimeType( currentMimeFilter() ); 00296 } 00297 00298 void KFileDialog::setPreviewWidget(const QWidget *w) { 00299 ops->setPreviewWidget(w); 00300 ops->clearHistory(); 00301 d->hasView = true; 00302 } 00303 00304 void KFileDialog::setPreviewWidget(const KPreviewWidgetBase *w) { 00305 ops->setPreviewWidget(w); 00306 ops->clearHistory(); 00307 d->hasView = true; 00308 } 00309 00310 KURL KFileDialog::getCompleteURL(const QString &_url) 00311 { 00312 QString url = KShell::tildeExpand(_url); 00313 KURL u; 00314 00315 if ( KURL::isRelativeURL(url) ) // only a full URL isn't relative. Even /path is. 00316 { 00317 if (!url.isEmpty() && !QDir::isRelativePath(url) ) // absolute path 00318 u.setPath( url ); 00319 else 00320 { 00321 u = ops->url(); 00322 u.addPath( url ); // works for filenames and relative paths 00323 u.cleanPath(); // fix "dir/.." 00324 } 00325 } 00326 else // complete URL 00327 u = url; 00328 00329 return u; 00330 } 00331 00332 // FIXME: check for "existing" flag here? 00333 void KFileDialog::slotOk() 00334 { 00335 kdDebug(kfile_area) << "slotOK\n"; 00336 00337 // a list of all selected files/directories (if any) 00338 // can only be used if the user didn't type any filenames/urls himself 00339 const KFileItemList *items = ops->selectedItems(); 00340 00341 if ( (mode() & KFile::Directory) != KFile::Directory ) { 00342 if ( locationEdit->currentText().stripWhiteSpace().isEmpty() ) { 00343 if ( !items || items->isEmpty() ) 00344 { 00345 QString msg; 00346 if ( d->operationMode == Saving ) 00347 msg = i18n("Please specify the filename to save to."); 00348 else 00349 msg = i18n("Please select the file to open."); 00350 KMessageBox::information(this, msg); 00351 return; 00352 } 00353 00354 // weird case: the location edit is empty, but there are 00355 // highlighted files 00356 else { 00357 00358 bool multi = (mode() & KFile::Files) != 0; 00359 KFileItemListIterator it( *items ); 00360 QString endQuote = QString::fromLatin1("\" "); 00361 QString name, files; 00362 while ( it.current() ) { 00363 name = (*it)->name(); 00364 if ( multi ) { 00365 name.prepend( '"' ); 00366 name.append( endQuote ); 00367 } 00368 00369 files.append( name ); 00370 ++it; 00371 } 00372 setLocationText( files ); 00373 return; 00374 } 00375 } 00376 } 00377 00378 bool dirOnly = ops->dirOnlyMode(); 00379 00380 // we can use our kfileitems, no need to parse anything 00381 if ( items && !locationEdit->lineEdit()->edited() && 00382 !(items->isEmpty() && !dirOnly) ) { 00383 00384 d->urlList.clear(); 00385 d->filenames = QString::null; 00386 00387 if ( dirOnly ) { 00388 d->url = ops->url(); 00389 } 00390 else { 00391 if ( !(mode() & KFile::Files) ) {// single selection 00392 d->url = items->getFirst()->url(); 00393 } 00394 00395 else { // multi (dirs and/or files) 00396 d->url = ops->url(); 00397 KFileItemListIterator it( *items ); 00398 while ( it.current() ) { 00399 d->urlList.append( (*it)->url() ); 00400 ++it; 00401 } 00402 } 00403 } 00404 00405 KURL url = KIO::NetAccess::mostLocalURL(d->url,topLevelWidget()); 00406 if ( (mode() & KFile::LocalOnly) == KFile::LocalOnly && 00407 !url.isLocalFile() ) { 00408 // ### after message freeze, add message for directories! 00409 KMessageBox::sorry( d->mainWidget, 00410 i18n("You can only select local files."), 00411 i18n("Remote Files Not Accepted") ); 00412 return; 00413 } 00414 00415 d->url = url; 00416 accept(); 00417 return; 00418 } 00419 00420 00421 KURL selectedURL; 00422 00423 if ( (mode() & KFile::Files) == KFile::Files ) {// multiselection mode 00424 QString locationText = locationEdit->currentText(); 00425 if ( locationText.contains( '/' )) { 00426 // relative path? -> prepend the current directory 00427 KURL u( ops->url(), KShell::tildeExpand(locationText)); 00428 if ( u.isValid() ) 00429 selectedURL = u; 00430 else 00431 selectedURL = ops->url(); 00432 } 00433 else // simple filename -> just use the current URL 00434 selectedURL = ops->url(); 00435 } 00436 00437 else { 00438 selectedURL = getCompleteURL(locationEdit->currentText()); 00439 00440 // appendExtension() may change selectedURL 00441 appendExtension (selectedURL); 00442 } 00443 00444 if ( !selectedURL.isValid() ) { 00445 KMessageBox::sorry( d->mainWidget, i18n("%1\ndoes not appear to be a valid URL.\n").arg(d->url.url()), i18n("Invalid URL") ); 00446 return; 00447 } 00448 00449 KURL url = KIO::NetAccess::mostLocalURL(selectedURL,topLevelWidget()); 00450 if ( (mode() & KFile::LocalOnly) == KFile::LocalOnly && 00451 !url.isLocalFile() ) { 00452 KMessageBox::sorry( d->mainWidget, 00453 i18n("You can only select local files."), 00454 i18n("Remote Files Not Accepted") ); 00455 return; 00456 } 00457 00458 d->url = url; 00459 00460 // d->url is a correct URL now 00461 00462 if ( (mode() & KFile::Directory) == KFile::Directory ) { 00463 kdDebug(kfile_area) << "Directory" << endl; 00464 bool done = true; 00465 if ( d->url.isLocalFile() ) { 00466 if ( locationEdit->currentText().stripWhiteSpace().isEmpty() ) { 00467 QFileInfo info( d->url.path() ); 00468 if ( info.isDir() ) { 00469 d->filenames = QString::null; 00470 d->urlList.clear(); 00471 d->urlList.append( d->url ); 00472 accept(); 00473 } 00474 else if (!info.exists() && (mode() & KFile::File) != KFile::File) { 00475 // directory doesn't exist, create and enter it 00476 if ( ops->mkdir( d->url.url(), true )) 00477 return; 00478 else 00479 accept(); 00480 } 00481 else { // d->url is not a directory, 00482 // maybe we are in File(s) | Directory mode 00483 if ( (mode() & KFile::File) == KFile::File || 00484 (mode() & KFile::Files) == KFile::Files ) 00485 done = false; 00486 } 00487 } 00488 else // Directory mode, with file[s]/dir[s] selected 00489 { 00490 if ( mode() & KFile::ExistingOnly ) 00491 { 00492 if ( ops->dirOnlyMode() ) 00493 { 00494 KURL fullURL(d->url, locationEdit->currentText()); 00495 if ( QFile::exists( fullURL.path() ) ) 00496 { 00497 d->url = fullURL; 00498 d->filenames = QString::null; 00499 d->urlList.clear(); 00500 accept(); 00501 return; 00502 } 00503 else // doesn't exist -> reject 00504 return; 00505 } 00506 } 00507 00508 d->filenames = locationEdit->currentText(); 00509 accept(); // what can we do? 00510 } 00511 00512 } 00513 else { // FIXME: remote directory, should we allow that? 00514 // qDebug( "**** Selected remote directory: %s", d->url.url().latin1()); 00515 d->filenames = QString::null; 00516 d->urlList.clear(); 00517 d->urlList.append( d->url ); 00518 00519 if ( mode() & KFile::ExistingOnly ) 00520 done = false; 00521 else 00522 accept(); 00523 } 00524 00525 if ( done ) 00526 return; 00527 } 00528 00529 if (!kapp->authorizeURLAction("open", KURL(), d->url)) 00530 { 00531 QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->url.prettyURL()); 00532 KMessageBox::error( d->mainWidget, msg); 00533 return; 00534 } 00535 00536 KIO::StatJob *job = 0L; 00537 d->statJobs.clear(); 00538 d->filenames = KShell::tildeExpand(locationEdit->currentText()); 00539 00540 if ( (mode() & KFile::Files) == KFile::Files && 00541 !locationEdit->currentText().contains( '/' )) { 00542 kdDebug(kfile_area) << "Files\n"; 00543 KURL::List list = parseSelectedURLs(); 00544 for ( KURL::List::ConstIterator it = list.begin(); 00545 it != list.end(); ++it ) 00546 { 00547 if (!kapp->authorizeURLAction("open", KURL(), *it)) 00548 { 00549 QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, (*it).prettyURL()); 00550 KMessageBox::error( d->mainWidget, msg); 00551 return; 00552 } 00553 } 00554 for ( KURL::List::ConstIterator it = list.begin(); 00555 it != list.end(); ++it ) 00556 { 00557 job = KIO::stat( *it, !(*it).isLocalFile() ); 00558 job->setWindow (topLevelWidget()); 00559 KIO::Scheduler::scheduleJob( job ); 00560 d->statJobs.append( job ); 00561 connect( job, SIGNAL( result(KIO::Job *) ), 00562 SLOT( slotStatResult( KIO::Job *) )); 00563 } 00564 return; 00565 } 00566 00567 job = KIO::stat(d->url,!d->url.isLocalFile()); 00568 job->setWindow (topLevelWidget()); 00569 d->statJobs.append( job ); 00570 connect(job, SIGNAL(result(KIO::Job*)), SLOT(slotStatResult(KIO::Job*))); 00571 } 00572 00573 00574 static bool isDirectory (const KIO::UDSEntry &t) 00575 { 00576 bool isDir = false; 00577 00578 for (KIO::UDSEntry::ConstIterator it = t.begin(); 00579 it != t.end(); 00580 it++) 00581 { 00582 if ((*it).m_uds == KIO::UDS_FILE_TYPE) 00583 { 00584 isDir = S_ISDIR ((mode_t) ((*it).m_long)); 00585 break; 00586 } 00587 } 00588 00589 return isDir; 00590 } 00591 00592 // FIXME : count all errors and show messagebox when d->statJobs.count() == 0 00593 // in case of an error, we cancel the whole operation (clear d->statJobs and 00594 // don't call accept) 00595 void KFileDialog::slotStatResult(KIO::Job* job) 00596 { 00597 kdDebug(kfile_area) << "slotStatResult" << endl; 00598 KIO::StatJob *sJob = static_cast<KIO::StatJob *>( job ); 00599 00600 if ( !d->statJobs.removeRef( sJob ) ) { 00601 return; 00602 } 00603 00604 int count = d->statJobs.count(); 00605 00606 // errors mean in general, the location is no directory ;/ 00607 // Can we be sure that it is exististant at all? (pfeiffer) 00608 if (sJob->error() && count == 0 && !ops->dirOnlyMode()) 00609 { 00610 accept(); 00611 return; 00612 } 00613 00614 KIO::UDSEntry t = sJob->statResult(); 00615 if (isDirectory (t)) 00616 { 00617 if ( ops->dirOnlyMode() ) 00618 { 00619 d->filenames = QString::null; 00620 d->urlList.clear(); 00621 accept(); 00622 } 00623 else // in File[s] mode, directory means error -> cd into it 00624 { 00625 if ( count == 0 ) { 00626 locationEdit->clearEdit(); 00627 locationEdit->lineEdit()->setEdited( false ); 00628 setURL( sJob->url() ); 00629 } 00630 } 00631 d->statJobs.clear(); 00632 return; 00633 } 00634 else if ( ops->dirOnlyMode() ) 00635 { 00636 return; // ### error message? 00637 } 00638 00639 kdDebug(kfile_area) << "filename " << sJob->url().url() << endl; 00640 00641 if ( count == 0 ) 00642 accept(); 00643 } 00644 00645 void KFileDialog::accept() 00646 { 00647 setResult( QDialog::Accepted ); // parseSelectedURLs() checks that 00648 00649 *lastDirectory = ops->url(); 00650 if (!d->fileClass.isEmpty()) 00651 KRecentDirs::add(d->fileClass, ops->url().url()); 00652 00653 // clear the topmost item, we insert it as full path later on as item 1 00654 locationEdit->changeItem( QString::null, 0 ); 00655 00656 KURL::List list = selectedURLs(); 00657 QValueListConstIterator<KURL> it = list.begin(); 00658 for ( ; it != list.end(); ++it ) { 00659 const KURL& url = *it; 00660 // we strip the last slash (-1) because KURLComboBox does that as well 00661 // when operating in file-mode. If we wouldn't , dupe-finding wouldn't 00662 // work. 00663 QString file = url.isLocalFile() ? url.path(-1) : url.prettyURL(-1); 00664 00665 // remove dupes 00666 for ( int i = 1; i < locationEdit->count(); i++ ) { 00667 if ( locationEdit->text( i ) == file ) { 00668 locationEdit->removeItem( i-- ); 00669 break; 00670 } 00671 } 00672 locationEdit->insertItem( file, 1 ); 00673 } 00674 00675 KConfig *config = KGlobal::config(); 00676 config->setForceGlobal( true ); 00677 writeConfig( config, ConfigGroup ); 00678 config->setForceGlobal( false ); 00679 00680 saveRecentFiles( config ); 00681 config->sync(); 00682 00683 KDialogBase::accept(); 00684 00685 addToRecentDocuments(); 00686 00687 if ( (mode() & KFile::Files) != KFile::Files ) // single selection 00688 emit fileSelected(d->url.url()); 00689 00690 ops->close(); 00691 emit okClicked(); 00692 } 00693 00694 00695 void KFileDialog::fileHighlighted(const KFileItem *i) 00696 { 00697 if (i && i->isDir()) 00698 return; 00699 00700 00701 if ( (ops->mode() & KFile::Files) != KFile::Files ) { 00702 if ( !i ) 00703 return; 00704 00705 d->url = i->url(); 00706 00707 if ( !locationEdit->hasFocus() ) { // don't disturb while editing 00708 setLocationText( i->name() ); 00709 } 00710 emit fileHighlighted(d->url.url()); 00711 } 00712 00713 else { 00714 multiSelectionChanged(); 00715 emit selectionChanged(); 00716 } 00717 } 00718 00719 void KFileDialog::fileSelected(const KFileItem *i) 00720 { 00721 if (i && i->isDir()) 00722 return; 00723 00724 if ( (ops->mode() & KFile::Files) != KFile::Files ) { 00725 if ( !i ) 00726 return; 00727 00728 d->url = i->url(); 00729 setLocationText( i->name() ); 00730 } 00731 else { 00732 multiSelectionChanged(); 00733 emit selectionChanged(); 00734 } 00735 slotOk(); 00736 } 00737 00738 00739 // I know it's slow to always iterate thru the whole filelist 00740 // (ops->selectedItems()), but what can we do? 00741 void KFileDialog::multiSelectionChanged() 00742 { 00743 if ( locationEdit->hasFocus() ) // don't disturb 00744 return; 00745 00746 locationEdit->lineEdit()->setEdited( false ); 00747 KFileItem *item; 00748 const KFileItemList *list = ops->selectedItems(); 00749 if ( !list ) { 00750 locationEdit->clearEdit(); 00751 return; 00752 } 00753 00754 static const QString &begin = KGlobal::staticQString(" \""); 00755 KFileItemListIterator it ( *list ); 00756 QString text; 00757 while ( (item = it.current()) ) { 00758 text.append( begin ).append( item->name() ).append( '\"' ); 00759 ++it; 00760 } 00761 00762 setLocationText( text.stripWhiteSpace() ); 00763 } 00764 00765 void KFileDialog::setLocationText( const QString& text ) 00766 { 00767 // setCurrentItem() will cause textChanged() being emitted, 00768 // so slotLocationChanged() will be called. Make sure we don't clear 00769 // the KDirOperator's view-selection in there 00770 disconnect( locationEdit, SIGNAL( textChanged( const QString& ) ), 00771 this, SLOT( slotLocationChanged( const QString& ) ) ); 00772 locationEdit->setCurrentItem( 0 ); 00773 connect( locationEdit, SIGNAL( textChanged( const QString& ) ), 00774 SLOT( slotLocationChanged( const QString& )) ); 00775 locationEdit->setEditText( text ); 00776 00777 // don't change selection when user has clicked on an item 00778 if ( d->operationMode == Saving && !locationEdit->isVisible()) 00779 setNonExtSelection(); 00780 } 00781 00782 static const char autocompletionWhatsThisText[] = I18N_NOOP("<p>While typing in the text area, you may be presented " 00783 "with possible matches. " 00784 "This feature can be controlled by clicking with the right mouse button " 00785 "and selecting a preferred mode from the <b>Text Completion</b> menu.") "</qt>"; 00786 void KFileDialog::updateLocationWhatsThis (void) 00787 { 00788 QString whatsThisText; 00789 if (d->operationMode == KFileDialog::Saving) 00790 { 00791 whatsThisText = "<qt>" + i18n("This is the name to save the file as.") + 00792 i18n (autocompletionWhatsThisText); 00793 } 00794 else if (ops->mode() & KFile::Files) 00795 { 00796 whatsThisText = "<qt>" + i18n("This is the list of files to open. More than " 00797 "one file can be specified by listing several " 00798 "files, separated by spaces.") + 00799 i18n (autocompletionWhatsThisText); 00800 } 00801 else 00802 { 00803 whatsThisText = "<qt>" + i18n("This is the name of the file to open.") + 00804 i18n (autocompletionWhatsThisText); 00805 } 00806 00807 QWhatsThis::add(d->locationLabel, whatsThisText); 00808 QWhatsThis::add(locationEdit, whatsThisText); 00809 } 00810 00811 void KFileDialog::init(const QString& startDir, const QString& filter, QWidget* widget) 00812 { 00813 initStatic(); 00814 d = new KFileDialogPrivate(); 00815 00816 d->boxLayout = 0; 00817 d->keepLocation = false; 00818 d->operationMode = Opening; 00819 d->bookmarkHandler = 0; 00820 d->hasDefaultFilter = false; 00821 d->hasView = false; 00822 d->mainWidget = new QWidget( this, "KFileDialog::mainWidget"); 00823 setMainWidget( d->mainWidget ); 00824 d->okButton = new KPushButton( KStdGuiItem::ok(), d->mainWidget ); 00825 d->okButton->setDefault( true ); 00826 d->cancelButton = new KPushButton(KStdGuiItem::cancel(), d->mainWidget); 00827 connect( d->okButton, SIGNAL( clicked() ), SLOT( slotOk() )); 00828 connect( d->cancelButton, SIGNAL( clicked() ), SLOT( slotCancel() )); 00829 d->customWidget = widget; 00830 d->autoSelectExtCheckBox = 0; // delayed loading 00831 d->autoSelectExtChecked = false; 00832 d->urlBar = 0; // delayed loading 00833 00834 QtMsgHandler oldHandler = qInstallMsgHandler( silenceQToolBar ); 00835 toolbar = new KToolBar( d->mainWidget, "KFileDialog::toolbar", true); 00836 toolbar->setFlat(true); 00837 qInstallMsgHandler( oldHandler ); 00838 00839 d->pathCombo = new KURLComboBox( KURLComboBox::Directories, true, 00840 toolbar, "path combo" ); 00841 QToolTip::add( d->pathCombo, i18n("Current location") ); 00842 QWhatsThis::add( d->pathCombo, "<qt>" + i18n("This is the currently listed location. " 00843 "The drop-down list also lists commonly used locations. " 00844 "This includes standard locations, such as your home folder, as well as " 00845 "locations that have been visited recently.") + i18n (autocompletionWhatsThisText)); 00846 00847 KURL u; 00848 u.setPath( QDir::rootDirPath() ); 00849 QString text = i18n("Root Folder: %1").arg( u.path() ); 00850 d->pathCombo->addDefaultURL( u, 00851 KMimeType::pixmapForURL( u, 0, KIcon::Small ), 00852 text ); 00853 00854 u.setPath( QDir::homeDirPath() ); 00855 text = i18n("Home Folder: %1").arg( u.path( +1 ) ); 00856 d->pathCombo->addDefaultURL( u, KMimeType::pixmapForURL( u, 0, KIcon::Small ), 00857 text ); 00858 00859 KURL docPath; 00860 docPath.setPath( KGlobalSettings::documentPath() ); 00861 if ( (u.path(+1) != docPath.path(+1)) && 00862 QDir(docPath.path(+1)).exists() ) 00863 { 00864 text = i18n("Documents: %1").arg( docPath.path( +1 ) ); 00865 d->pathCombo->addDefaultURL( docPath, 00866 KMimeType::pixmapForURL( docPath, 0, KIcon::Small ), 00867 text ); 00868 } 00869 00870 u.setPath( KGlobalSettings::desktopPath() ); 00871 text = i18n("Desktop: %1").arg( u.path( +1 ) ); 00872 d->pathCombo->addDefaultURL( u, 00873 KMimeType::pixmapForURL( u, 0, KIcon::Small ), 00874 text ); 00875 00876 d->url = getStartURL( startDir, d->fileClass ); 00877 d->selection = d->url.url(); 00878 00879 // If local, check it exists. If not, go up until it exists. 00880 if ( d->url.isLocalFile() ) 00881 { 00882 if ( !QFile::exists( d->url.path() ) ) 00883 { 00884 d->url = d->url.upURL(); 00885 QDir dir( d->url.path() ); 00886 while ( !dir.exists() ) 00887 { 00888 d->url = d->url.upURL(); 00889 dir.setPath( d->url.path() ); 00890 } 00891 } 00892 } 00893 00894 ops = new KDirOperator(d->url, d->mainWidget, "KFileDialog::ops"); 00895 ops->setOnlyDoubleClickSelectsFiles( true ); 00896 connect(ops, SIGNAL(urlEntered(const KURL&)), 00897 SLOT(urlEntered(const KURL&))); 00898 connect(ops, SIGNAL(fileHighlighted(const KFileItem *)), 00899 SLOT(fileHighlighted(const KFileItem *))); 00900 connect(ops, SIGNAL(fileSelected(const KFileItem *)), 00901 SLOT(fileSelected(const KFileItem *))); 00902 connect(ops, SIGNAL(finishedLoading()), 00903 SLOT(slotLoadingFinished())); 00904 00905 ops->setupMenu(KDirOperator::SortActions | 00906 KDirOperator::FileActions | 00907 KDirOperator::ViewActions); 00908 KActionCollection *coll = ops->actionCollection(); 00909 00910 // plug nav items into the toolbar 00911 coll->action( "up" )->plug( toolbar ); 00912 coll->action( "up" )->setWhatsThis(i18n("<qt>Click this button to enter the parent folder.<p>" 00913 "For instance, if the current location is file:/home/%1 clicking this " 00914 "button will take you to file:/home.</qt>").arg( KUser().loginName() )); 00915 coll->action( "back" )->plug( toolbar ); 00916 coll->action( "back" )->setWhatsThis(i18n("Click this button to move backwards one step in the browsing history.")); 00917 coll->action( "forward" )->plug( toolbar ); 00918 coll->action( "forward" )->setWhatsThis(i18n("Click this button to move forward one step in the browsing history.")); 00919 coll->action( "reload" )->plug( toolbar ); 00920 coll->action( "reload" )->setWhatsThis(i18n("Click this button to reload the contents of the current location.")); 00921 coll->action( "mkdir" )->setShortcut(Key_F10); 00922 coll->action( "mkdir" )->plug( toolbar ); 00923 coll->action( "mkdir" )->setWhatsThis(i18n("Click this button to create a new folder.")); 00924 00925 KToggleAction *showSidebarAction = 00926 new KToggleAction(i18n("Show Quick Access Navigation Panel"), Key_F9, coll,"toggleSpeedbar"); 00927 showSidebarAction->setCheckedState(i18n("Hide Quick Access Navigation Panel")); 00928 connect( showSidebarAction, SIGNAL( toggled( bool ) ), 00929 SLOT( toggleSpeedbar( bool )) ); 00930 00931 KToggleAction *showBookmarksAction = 00932 new KToggleAction(i18n("Show Bookmarks"), 0, coll, "toggleBookmarks"); 00933 showBookmarksAction->setCheckedState(i18n("Hide Bookmarks")); 00934 connect( showBookmarksAction, SIGNAL( toggled( bool ) ), 00935 SLOT( toggleBookmarks( bool )) ); 00936 00937 KActionMenu *menu = new KActionMenu( i18n("Configure"), "configure", this, "extra menu" ); 00938 menu->setWhatsThis(i18n("<qt>This is the configuration menu for the file dialog. " 00939 "Various options can be accessed from this menu including: <ul>" 00940 "<li>how files are sorted in the list</li>" 00941 "<li>types of view, including icon and list</li>" 00942 "<li>showing of hidden files</li>" 00943 "<li>the Quick Access navigation panel</li>" 00944 "<li>file previews</li>" 00945 "<li>separating folders from files</li></ul></qt>")); 00946 menu->insert( coll->action( "sorting menu" )); 00947 menu->insert( coll->action( "separator" )); 00948 coll->action( "short view" )->setShortcut(Key_F6); 00949 menu->insert( coll->action( "short view" )); 00950 coll->action( "detailed view" )->setShortcut(Key_F7); 00951 menu->insert( coll->action( "detailed view" )); 00952 menu->insert( coll->action( "separator" )); 00953 coll->action( "show hidden" )->setShortcut(Key_F8); 00954 menu->insert( coll->action( "show hidden" )); 00955 menu->insert( showSidebarAction ); 00956 menu->insert( showBookmarksAction ); 00957 coll->action( "preview" )->setShortcut(Key_F11); 00958 menu->insert( coll->action( "preview" )); 00959 coll->action( "separate dirs" )->setShortcut(Key_F12); 00960 menu->insert( coll->action( "separate dirs" )); 00961 00962 menu->setDelayed( false ); 00963 connect( menu->popupMenu(), SIGNAL( aboutToShow() ), 00964 ops, SLOT( updateSelectionDependentActions() )); 00965 menu->plug( toolbar ); 00966 00967 //Insert a separator. 00968 KToolBarSeparator* spacerWidget = new KToolBarSeparator(Horizontal, false /*no line*/, 00969 toolbar); 00970 d->m_pathComboIndex = toolbar->insertWidget(-1, -1, spacerWidget); 00971 toolbar->insertWidget(PATH_COMBO, 0, d->pathCombo); 00972 00973 00974 toolbar->setItemAutoSized (PATH_COMBO); 00975 toolbar->setIconText(KToolBar::IconOnly); 00976 toolbar->setBarPos(KToolBar::Top); 00977 toolbar->setMovingEnabled(false); 00978 toolbar->adjustSize(); 00979 00980 KURLCompletion *pathCompletionObj = new KURLCompletion( KURLCompletion::DirCompletion ); 00981 d->pathCombo->setCompletionObject( pathCompletionObj ); 00982 d->pathCombo->setAutoDeleteCompletionObject( true ); 00983 00984 connect( d->pathCombo, SIGNAL( urlActivated( const KURL& )), 00985 this, SLOT( enterURL( const KURL& ) )); 00986 connect( d->pathCombo, SIGNAL( returnPressed( const QString& )), 00987 this, SLOT( enterURL( const QString& ) )); 00988 00989 QString whatsThisText; 00990 00991 // the Location label/edit 00992 d->locationLabel = new QLabel(i18n("&Location:"), d->mainWidget); 00993 locationEdit = new KURLComboBox(KURLComboBox::Files, true, 00994 d->mainWidget, "LocationEdit"); 00995 connect( locationEdit, SIGNAL( textChanged( const QString& ) ), 00996 SLOT( slotLocationChanged( const QString& )) ); 00997 00998 updateLocationWhatsThis (); 00999 d->locationLabel->setBuddy(locationEdit); 01000 01001 locationEdit->setFocus(); 01002 KURLCompletion *fileCompletionObj = new KURLCompletion( KURLCompletion::FileCompletion ); 01003 QString dir = d->url.url(+1); 01004 pathCompletionObj->setDir( dir ); 01005 fileCompletionObj->setDir( dir ); 01006 locationEdit->setCompletionObject( fileCompletionObj ); 01007 locationEdit->setAutoDeleteCompletionObject( true ); 01008 connect( fileCompletionObj, SIGNAL( match( const QString& ) ), 01009 SLOT( fileCompletion( const QString& )) ); 01010 01011 connect( locationEdit, SIGNAL( returnPressed() ), 01012 this, SLOT( slotOk())); 01013 connect(locationEdit, SIGNAL( activated( const QString& )), 01014 this, SLOT( locationActivated( const QString& ) )); 01015 01016 // the Filter label/edit 01017 whatsThisText = i18n("<qt>This is the filter to apply to the file list. " 01018 "File names that do not match the filter will not be shown.<p>" 01019 "You may select from one of the preset filters in the " 01020 "drop down menu, or you may enter a custom filter " 01021 "directly into the text area.<p>" 01022 "Wildcards such as * and ? are allowed.</qt>"); 01023 d->filterLabel = new QLabel(i18n("&Filter:"), d->mainWidget); 01024 QWhatsThis::add(d->filterLabel, whatsThisText); 01025 filterWidget = new KFileFilterCombo(d->mainWidget, 01026 "KFileDialog::filterwidget"); 01027 QWhatsThis::add(filterWidget, whatsThisText); 01028 setFilter(filter); 01029 d->filterLabel->setBuddy(filterWidget); 01030 connect(filterWidget, SIGNAL(filterChanged()), SLOT(slotFilterChanged())); 01031 01032 // the Automatically Select Extension checkbox 01033 // (the text, visibility etc. is set in updateAutoSelectExtension(), which is called by readConfig()) 01034 d->autoSelectExtCheckBox = new QCheckBox (d->mainWidget); 01035 connect(d->autoSelectExtCheckBox, SIGNAL(clicked()), SLOT(slotAutoSelectExtClicked())); 01036 01037 initGUI(); // activate GM 01038 01039 KConfig* config = KGlobal::config(); 01040 readRecentFiles( config ); 01041 01042 adjustSize(); 01043 01044 ops->setViewConfig( config, ConfigGroup ); 01045 readConfig( config, ConfigGroup ); 01046 setSelection(d->selection); 01047 } 01048 01049 void KFileDialog::initSpeedbar() 01050 { 01051 d->urlBar = new KFileSpeedBar( d->mainWidget, "url bar" ); 01052 connect( d->urlBar, SIGNAL( activated( const KURL& )), 01053 SLOT( enterURL( const KURL& )) ); 01054 01055 // need to set the current url of the urlbar manually (not via urlEntered() 01056 // here, because the initial url of KDirOperator might be the same as the 01057 // one that will be set later (and then urlEntered() won't be emitted). 01058 // ### REMOVE THIS when KDirOperator's initial URL (in the c'tor) is gone. 01059 d->urlBar->setCurrentItem( d->url ); 01060 01061 d->urlBarLayout->insertWidget( 0, d->urlBar ); 01062 } 01063 01064 void KFileDialog::initGUI() 01065 { 01066 delete d->boxLayout; // deletes all sub layouts 01067 01068 d->boxLayout = new QVBoxLayout( d->mainWidget, 0, KDialog::spacingHint()); 01069 d->boxLayout->addWidget(toolbar, AlignTop); 01070 01071 d->urlBarLayout = new QHBoxLayout( d->boxLayout ); // needed for the urlBar that may appear 01072 QVBoxLayout *vbox = new QVBoxLayout( d->urlBarLayout ); 01073 01074 vbox->addWidget(ops, 4); 01075 vbox->addSpacing(3); 01076 01077 QGridLayout* lafBox= new QGridLayout(2, 3, KDialog::spacingHint()); 01078 01079 lafBox->addWidget(d->locationLabel, 0, 0, AlignVCenter); 01080 lafBox->addWidget(locationEdit, 0, 1, AlignVCenter); 01081 lafBox->addWidget(d->okButton, 0, 2, AlignVCenter); 01082 01083 lafBox->addWidget(d->filterLabel, 1, 0, AlignVCenter); 01084 lafBox->addWidget(filterWidget, 1, 1, AlignVCenter); 01085 lafBox->addWidget(d->cancelButton, 1, 2, AlignVCenter); 01086 01087 lafBox->setColStretch(1, 4); 01088 01089 vbox->addLayout(lafBox, 0); 01090 vbox->addSpacing(3); 01091 01092 // add the Automatically Select Extension checkbox 01093 vbox->addWidget (d->autoSelectExtCheckBox); 01094 vbox->addSpacing (3); 01095 01096 setTabOrder(ops, d->autoSelectExtCheckBox); 01097 setTabOrder (d->autoSelectExtCheckBox, locationEdit); 01098 setTabOrder(locationEdit, filterWidget); 01099 setTabOrder(filterWidget, d->okButton); 01100 setTabOrder(d->okButton, d->cancelButton); 01101 setTabOrder(d->cancelButton, d->pathCombo); 01102 setTabOrder(d->pathCombo, ops); 01103 01104 // If a custom widget was specified... 01105 if ( d->customWidget != 0 ) 01106 { 01107 // ...add it to the dialog, below the filter list box. 01108 01109 // Change the parent so that this widget is a child of the main widget 01110 d->customWidget->reparent( d->mainWidget, QPoint() ); 01111 01112 vbox->addWidget( d->customWidget ); 01113 vbox->addSpacing(3); 01114 01115 // FIXME: This should adjust the tab orders so that the custom widget 01116 // comes after the Cancel button. The code appears to do this, but the result 01117 // somehow screws up the tab order of the file path combo box. Not a major 01118 // problem, but ideally the tab order with a custom widget should be 01119 // the same as the order without one. 01120 setTabOrder(d->cancelButton, d->customWidget); 01121 setTabOrder(d->customWidget, d->pathCombo); 01122 } 01123 else 01124 { 01125 setTabOrder(d->cancelButton, d->pathCombo); 01126 } 01127 01128 setTabOrder(d->pathCombo, ops); 01129 } 01130 01131 void KFileDialog::slotFilterChanged() 01132 { 01133 QString filter = filterWidget->currentFilter(); 01134 ops->clearFilter(); 01135 01136 if ( filter.find( '/' ) > -1 ) { 01137 QStringList types = QStringList::split( " ", filter ); 01138 types.prepend( "inode/directory" ); 01139 ops->setMimeFilter( types ); 01140 } 01141 else 01142 ops->setNameFilter( filter ); 01143 01144 ops->updateDir(); 01145 01146 updateAutoSelectExtension (); 01147 01148 emit filterChanged( filter ); 01149 } 01150 01151 01152 void KFileDialog::setURL(const KURL& url, bool clearforward) 01153 { 01154 d->selection = QString::null; 01155 ops->setURL( url, clearforward); 01156 } 01157 01158 // Protected 01159 void KFileDialog::urlEntered(const KURL& url) 01160 { 01161 QString filename = locationEdit->currentText(); 01162 d->selection = QString::null; 01163 01164 if ( d->pathCombo->count() != 0 ) { // little hack 01165 d->pathCombo->setURL( url ); 01166 } 01167 01168 locationEdit->blockSignals( true ); 01169 locationEdit->setCurrentItem( 0 ); 01170 if ( d->keepLocation ) 01171 locationEdit->setEditText( filename ); 01172 01173 locationEdit->blockSignals( false ); 01174 01175 QString dir = url.url(+1); 01176 static_cast<KURLCompletion*>( d->pathCombo->completionObject() )->setDir( dir ); 01177 static_cast<KURLCompletion*>( locationEdit->completionObject() )->setDir( dir ); 01178 01179 if ( d->urlBar ) 01180 d->urlBar->setCurrentItem( url ); 01181 } 01182 01183 void KFileDialog::locationActivated( const QString& url ) 01184 { 01185 // This guard prevents any URL _typed_ by the user from being interpreted 01186 // twice (by returnPressed/slotOk and here, activated/locationActivated) 01187 // after the user presses Enter. Without this, _both_ setSelection and 01188 // slotOk would "u.addPath( url )" ...so instead we leave it up to just 01189 // slotOk.... 01190 if (!locationEdit->lineEdit()->edited()) 01191 setSelection( url ); 01192 } 01193 01194 void KFileDialog::enterURL( const KURL& url) 01195 { 01196 setURL( url ); 01197 } 01198 01199 void KFileDialog::enterURL( const QString& url ) 01200 { 01201 setURL( KURL::fromPathOrURL( KURLCompletion::replacedPath( url, true, true )) ); 01202 } 01203 01204 void KFileDialog::toolbarCallback(int) // SLOT 01205 { 01206 /* 01207 * yes, nothing uses this anymore. 01208 * it used to be used to show the configure dialog 01209 */ 01210 } 01211 01212 01213 void KFileDialog::setSelection(const QString& url) 01214 { 01215 kdDebug(kfile_area) << "setSelection " << url << endl; 01216 01217 if (url.isEmpty()) { 01218 d->selection = QString::null; 01219 return; 01220 } 01221 01222 KURL u = getCompleteURL(url); 01223 if (!u.isValid()) { // if it still is 01224 kdWarning() << url << " is not a correct argument for setSelection!" << endl; 01225 return; 01226 } 01227 01228 if (!KProtocolInfo::supportsListing(u)) { 01229 locationEdit->lineEdit()->setEdited( true ); 01230 return; 01231 } 01232 01233 /* we strip the first / from the path to avoid file://usr which means 01234 * / on host usr 01235 */ 01236 KFileItem i(KFileItem::Unknown, KFileItem::Unknown, u, true ); 01237 // KFileItem i(u.path()); 01238 if ( i.isDir() && u.isLocalFile() && QFile::exists( u.path() ) ) { 01239 // trust isDir() only if the file is 01240 // local (we cannot stat non-local urls) and if it exists! 01241 // (as KFileItem does not check if the file exists or not 01242 // -> the statbuffer is undefined -> isDir() is unreliable) (Simon) 01243 setURL(u, true); 01244 } 01245 else { 01246 QString filename = u.url(); 01247 int sep = filename.findRev('/'); 01248 if (sep >= 0) { // there is a / in it 01249 if ( KProtocolInfo::supportsListing( u )) { 01250 KURL dir(u); 01251 dir.setQuery( QString::null ); 01252 dir.setFileName( QString::null ); 01253 setURL(dir, true ); 01254 } 01255 01256 // filename must be decoded, or "name with space" would become 01257 // "name%20with%20space", so we use KURL::fileName() 01258 filename = u.fileName(); 01259 kdDebug(kfile_area) << "filename " << filename << endl; 01260 d->selection = filename; 01261 setLocationText( filename ); 01262 01263 // tell the line edit that it has been edited 01264 // otherwise we won't know this was set by the user 01265 // and it will be ignored if there has been an 01266 // auto completion. this caused bugs where automcompletion 01267 // would start, the user would pick something from the 01268 // history and then hit Ok only to get the autocompleted 01269 // selection. OOOPS. 01270 locationEdit->lineEdit()->setEdited( true ); 01271 } 01272 01273 d->url = ops->url(); 01274 d->url.addPath(filename); 01275 } 01276 } 01277 01278 void KFileDialog::slotLoadingFinished() 01279 { 01280 if ( !d->selection.isNull() ) 01281 ops->setCurrentItem( d->selection ); 01282 } 01283 01284 // ### remove in KDE4 01285 void KFileDialog::pathComboChanged( const QString& ) 01286 { 01287 } 01288 void KFileDialog::dirCompletion( const QString& ) // SLOT 01289 { 01290 } 01291 void KFileDialog::fileCompletion( const QString& match ) 01292 { 01293 if ( match.isEmpty() && ops->view() ) 01294 ops->view()->clearSelection(); 01295 else 01296 ops->setCurrentItem( match ); 01297 } 01298 01299 void KFileDialog::slotLocationChanged( const QString& text ) 01300 { 01301 if ( text.isEmpty() && ops->view() ) 01302 ops->view()->clearSelection(); 01303 01304 updateFilter(); 01305 } 01306 01307 void KFileDialog::updateStatusLine(int /* dirs */, int /* files */) 01308 { 01309 kdWarning() << "KFileDialog::updateStatusLine is deprecated! The status line no longer exists. Do not try and use it!" << endl; 01310 } 01311 01312 QString KFileDialog::getOpenFileName(const QString& startDir, 01313 const QString& filter, 01314 QWidget *parent, const QString& caption) 01315 { 01316 KFileDialog dlg(startDir, filter, parent, "filedialog", true); 01317 dlg.setOperationMode( Opening ); 01318 01319 dlg.setMode( KFile::File | KFile::LocalOnly ); 01320 dlg.setCaption(caption.isNull() ? i18n("Open") : caption); 01321 01322 dlg.ops->clearHistory(); 01323 dlg.exec(); 01324 01325 return dlg.selectedFile(); 01326 } 01327 01328 QString KFileDialog::getOpenFileNameWId(const QString& startDir, 01329 const QString& filter, 01330 WId parent_id, const QString& caption) 01331 { 01332 QWidget* parent = QWidget::find( parent_id ); 01333 KFileDialog dlg(startDir, filter, parent, "filedialog", true); 01334 #ifdef Q_WS_X11 01335 if( parent == NULL && parent_id != 0 ) 01336 XSetTransientForHint( qt_xdisplay(), dlg.winId(), parent_id ); 01337 #else 01338 // TODO 01339 #endif 01340 01341 dlg.setOperationMode( KFileDialog::Opening ); 01342 01343 dlg.setMode( KFile::File | KFile::LocalOnly ); 01344 dlg.setCaption(caption.isNull() ? i18n("Open") : caption); 01345 01346 dlg.ops->clearHistory(); 01347 dlg.exec(); 01348 01349 return dlg.selectedFile(); 01350 } 01351 01352 QStringList KFileDialog::getOpenFileNames(const QString& startDir, 01353 const QString& filter, 01354 QWidget *parent, 01355 const QString& caption) 01356 { 01357 KFileDialog dlg(startDir, filter, parent, "filedialog", true); 01358 dlg.setOperationMode( Opening ); 01359 01360 dlg.setCaption(caption.isNull() ? i18n("Open") : caption); 01361 dlg.setMode(KFile::Files | KFile::LocalOnly); 01362 dlg.ops->clearHistory(); 01363 dlg.exec(); 01364 01365 return dlg.selectedFiles(); 01366 } 01367 01368 KURL KFileDialog::getOpenURL(const QString& startDir, const QString& filter, 01369 QWidget *parent, const QString& caption) 01370 { 01371 KFileDialog dlg(startDir, filter, parent, "filedialog", true); 01372 dlg.setOperationMode( Opening ); 01373 01374 dlg.setCaption(caption.isNull() ? i18n("Open") : caption); 01375 dlg.setMode( KFile::File ); 01376 dlg.ops->clearHistory(); 01377 dlg.exec(); 01378 01379 return dlg.selectedURL(); 01380 } 01381 01382 KURL::List KFileDialog::getOpenURLs(const QString& startDir, 01383 const QString& filter, 01384 QWidget *parent, 01385 const QString& caption) 01386 { 01387 KFileDialog dlg(startDir, filter, parent, "filedialog", true); 01388 dlg.setOperationMode( Opening ); 01389 01390 dlg.setCaption(caption.isNull() ? i18n("Open") : caption); 01391 dlg.setMode(KFile::Files); 01392 dlg.ops->clearHistory(); 01393 dlg.exec(); 01394 01395 return dlg.selectedURLs(); 01396 } 01397 01398 KURL KFileDialog::getExistingURL(const QString& startDir, 01399 QWidget *parent, 01400 const QString& caption) 01401 { 01402 return KDirSelectDialog::selectDirectory(startDir, false, parent, caption); 01403 } 01404 01405 QString KFileDialog::getExistingDirectory(const QString& startDir, 01406 QWidget *parent, 01407 const QString& caption) 01408 { 01409 #ifdef Q_WS_WIN 01410 return QFileDialog::getExistingDirectory(startDir, parent, "getExistingDirectory", 01411 caption, true, true); 01412 #else 01413 KURL url = KDirSelectDialog::selectDirectory(startDir, true, parent, 01414 caption); 01415 if ( url.isValid() ) 01416 return url.path(); 01417 01418 return QString::null; 01419 #endif 01420 } 01421 01422 KURL KFileDialog::getImageOpenURL( const QString& startDir, QWidget *parent, 01423 const QString& caption) 01424 { 01425 QStringList mimetypes = KImageIO::mimeTypes( KImageIO::Reading ); 01426 KFileDialog dlg(startDir, 01427 mimetypes.join(" "), 01428 parent, "filedialog", true); 01429 dlg.setOperationMode( Opening ); 01430 dlg.setCaption( caption.isNull() ? i18n("Open") : caption ); 01431 dlg.setMode( KFile::File ); 01432 01433 KImageFilePreview *ip = new KImageFilePreview( &dlg ); 01434 dlg.setPreviewWidget( ip ); 01435 dlg.exec(); 01436 01437 return dlg.selectedURL(); 01438 } 01439 01440 KURL KFileDialog::selectedURL() const 01441 { 01442 if ( result() == QDialog::Accepted ) 01443 return d->url; 01444 else 01445 return KURL(); 01446 } 01447 01448 KURL::List KFileDialog::selectedURLs() const 01449 { 01450 KURL::List list; 01451 if ( result() == QDialog::Accepted ) { 01452 if ( (ops->mode() & KFile::Files) == KFile::Files ) 01453 list = parseSelectedURLs(); 01454 else 01455 list.append( d->url ); 01456 } 01457 return list; 01458 } 01459 01460 01461 KURL::List& KFileDialog::parseSelectedURLs() const 01462 { 01463 if ( d->filenames.isEmpty() ) { 01464 return d->urlList; 01465 } 01466 01467 d->urlList.clear(); 01468 if ( d->filenames.contains( '/' )) { // assume _one_ absolute filename 01469 static const QString &prot = KGlobal::staticQString(":/"); 01470 KURL u; 01471 if ( d->filenames.find( prot ) != -1 ) 01472 u = d->filenames; 01473 else 01474 u.setPath( d->filenames ); 01475 01476 if ( u.isValid() ) 01477 d->urlList.append( u ); 01478 else 01479 KMessageBox::error( d->mainWidget, 01480 i18n("The chosen filenames do not\n" 01481 "appear to be valid."), 01482 i18n("Invalid Filenames") ); 01483 } 01484 01485 else 01486 d->urlList = tokenize( d->filenames ); 01487 01488 d->filenames = QString::null; // indicate that we parsed that one 01489 01490 return d->urlList; 01491 } 01492 01493 01494 // FIXME: current implementation drawback: a filename can't contain quotes 01495 KURL::List KFileDialog::tokenize( const QString& line ) const 01496 { 01497 KURL::List urls; 01498 KURL u( ops->url() ); 01499 QString name; 01500 01501 int count = line.contains( '"' ); 01502 if ( count == 0 ) { // no " " -> assume one single file 01503 u.setFileName( line ); 01504 if ( u.isValid() ) 01505 urls.append( u ); 01506 01507 return urls; 01508 } 01509 01510 if ( (count % 2) == 1 ) { // odd number of " -> error 01511 QWidget *that = const_cast<KFileDialog *>(this); 01512 KMessageBox::sorry(that, i18n("The requested filenames\n" 01513 "%1\n" 01514 "do not appear to be valid;\n" 01515 "make sure every filename is enclosed in double quotes.").arg(line), 01516 i18n("Filename Error")); 01517 return urls; 01518 } 01519 01520 int start = 0; 01521 int index1 = -1, index2 = -1; 01522 while ( true ) { 01523 index1 = line.find( '"', start ); 01524 index2 = line.find( '"', index1 + 1 ); 01525 01526 if ( index1 < 0 ) 01527 break; 01528 01529 // get everything between the " " 01530 name = line.mid( index1 + 1, index2 - index1 - 1 ); 01531 u.setFileName( name ); 01532 if ( u.isValid() ) 01533 urls.append( u ); 01534 01535 start = index2 + 1; 01536 } 01537 return urls; 01538 } 01539 01540 01541 QString KFileDialog::selectedFile() const 01542 { 01543 if ( result() == QDialog::Accepted ) 01544 { 01545 KURL url = KIO::NetAccess::mostLocalURL(d->url,topLevelWidget()); 01546 if (url.isLocalFile()) 01547 return url.path(); 01548 else { 01549 KMessageBox::sorry( d->mainWidget, 01550 i18n("You can only select local files."), 01551 i18n("Remote Files Not Accepted") ); 01552 } 01553 } 01554 return QString::null; 01555 } 01556 01557 QStringList KFileDialog::selectedFiles() const 01558 { 01559 QStringList list; 01560 KURL url; 01561 01562 if ( result() == QDialog::Accepted ) { 01563 if ( (ops->mode() & KFile::Files) == KFile::Files ) { 01564 KURL::List urls = parseSelectedURLs(); 01565 QValueListConstIterator<KURL> it = urls.begin(); 01566 while ( it != urls.end() ) { 01567 url = KIO::NetAccess::mostLocalURL(*it,topLevelWidget()); 01568 if ( url.isLocalFile() ) 01569 list.append( url.path() ); 01570 ++it; 01571 } 01572 } 01573 01574 else { // single-selection mode 01575 if ( d->url.isLocalFile() ) 01576 list.append( d->url.path() ); 01577 } 01578 } 01579 01580 return list; 01581 } 01582 01583 KURL KFileDialog::baseURL() const 01584 { 01585 return ops->url(); 01586 } 01587 01588 QString KFileDialog::getSaveFileName(const QString& dir, const QString& filter, 01589 QWidget *parent, 01590 const QString& caption) 01591 { 01592 bool specialDir = dir.at(0) == ':'; 01593 KFileDialog dlg( specialDir ? dir : QString::null, filter, parent, "filedialog", true); 01594 if ( !specialDir ) 01595 dlg.setSelection( dir ); // may also be a filename 01596 01597 dlg.setOperationMode( Saving ); 01598 dlg.setCaption(caption.isNull() ? i18n("Save As") : caption); 01599 01600 dlg.exec(); 01601 01602 QString filename = dlg.selectedFile(); 01603 if (!filename.isEmpty()) 01604 KRecentDocument::add(filename); 01605 01606 return filename; 01607 } 01608 01609 QString KFileDialog::getSaveFileNameWId(const QString& dir, const QString& filter, 01610 WId parent_id, 01611 const QString& caption) 01612 { 01613 bool specialDir = dir.at(0) == ':'; 01614 QWidget* parent = QWidget::find( parent_id ); 01615 KFileDialog dlg( specialDir ? dir : QString::null, filter, parent, "filedialog", true); 01616 #ifdef Q_WS_X11 01617 if( parent == NULL && parent_id != 0 ) 01618 XSetTransientForHint(qt_xdisplay(), dlg.winId(), parent_id); 01619 #else 01620 // TODO 01621 #endif 01622 01623 if ( !specialDir ) 01624 dlg.setSelection( dir ); // may also be a filename 01625 01626 dlg.setOperationMode( KFileDialog::Saving); 01627 dlg.setCaption(caption.isNull() ? i18n("Save As") : caption); 01628 01629 dlg.exec(); 01630 01631 QString filename = dlg.selectedFile(); 01632 if (!filename.isEmpty()) 01633 KRecentDocument::add(filename); 01634 01635 return filename; 01636 } 01637 01638 KURL KFileDialog::getSaveURL(const QString& dir, const QString& filter, 01639 QWidget *parent, const QString& caption) 01640 { 01641 bool specialDir = dir.at(0) == ':'; 01642 KFileDialog dlg(specialDir ? dir : QString::null, filter, parent, "filedialog", true); 01643 if ( !specialDir ) 01644 dlg.setSelection( dir ); // may also be a filename 01645 01646 dlg.setCaption(caption.isNull() ? i18n("Save As") : caption); 01647 dlg.setOperationMode( Saving ); 01648 01649 dlg.exec(); 01650 01651 KURL url = dlg.selectedURL(); 01652 if (url.isValid()) 01653 KRecentDocument::add( url ); 01654 01655 return url; 01656 } 01657 01658 void KFileDialog::show() 01659 { 01660 if ( !d->hasView ) { // delayed view-creation 01661 ops->setView(KFile::Default); 01662 ops->clearHistory(); 01663 d->hasView = true; 01664 } 01665 01666 KDialogBase::show(); 01667 } 01668 01669 void KFileDialog::setMode( KFile::Mode m ) 01670 { 01671 ops->setMode(m); 01672 if ( ops->dirOnlyMode() ) { 01673 filterWidget->setDefaultFilter( i18n("*|All Folders") ); 01674 } 01675 else { 01676 filterWidget->setDefaultFilter( i18n("*|All Files") ); 01677 } 01678 01679 updateAutoSelectExtension (); 01680 } 01681 01682 void KFileDialog::setMode( unsigned int m ) 01683 { 01684 setMode(static_cast<KFile::Mode>( m )); 01685 } 01686 01687 KFile::Mode KFileDialog::mode() const 01688 { 01689 return ops->mode(); 01690 } 01691 01692 01693 void KFileDialog::readConfig( KConfig *kc, const QString& group ) 01694 { 01695 if ( !kc ) 01696 return; 01697 01698 QString oldGroup = kc->group(); 01699 if ( !group.isEmpty() ) 01700 kc->setGroup( group ); 01701 01702 ops->readConfig( kc, group ); 01703 01704 KURLComboBox *combo = d->pathCombo; 01705 combo->setURLs( kc->readPathListEntry( RecentURLs ), KURLComboBox::RemoveTop ); 01706 combo->setMaxItems( kc->readNumEntry( RecentURLsNumber, 01707 DefaultRecentURLsNumber ) ); 01708 combo->setURL( ops->url() ); 01709 autoDirectoryFollowing = kc->readBoolEntry( AutoDirectoryFollowing, 01710 DefaultDirectoryFollowing ); 01711 01712 KGlobalSettings::Completion cm = (KGlobalSettings::Completion) 01713 kc->readNumEntry( PathComboCompletionMode, 01714 KGlobalSettings::completionMode() ); 01715 if ( cm != KGlobalSettings::completionMode() ) 01716 combo->setCompletionMode( cm ); 01717 01718 cm = (KGlobalSettings::Completion) 01719 kc->readNumEntry( LocationComboCompletionMode, 01720 KGlobalSettings::completionMode() ); 01721 if ( cm != KGlobalSettings::completionMode() ) 01722 locationEdit->setCompletionMode( cm ); 01723 01724 // show or don't show the speedbar 01725 toggleSpeedbar( kc->readBoolEntry(ShowSpeedbar, true) ); 01726 01727 // show or don't show the bookmarks 01728 toggleBookmarks( kc->readBoolEntry(ShowBookmarks, false) ); 01729 01730 // does the user want Automatically Select Extension? 01731 d->autoSelectExtChecked = kc->readBoolEntry (AutoSelectExtChecked, DefaultAutoSelectExtChecked); 01732 updateAutoSelectExtension (); 01733 01734 int w1 = minimumSize().width(); 01735 int w2 = toolbar->sizeHint().width() + 10; 01736 if (w1 < w2) 01737 setMinimumWidth(w2); 01738 01739 QSize size = configDialogSize( group ); 01740 resize( size ); 01741 kc->setGroup( oldGroup ); 01742 } 01743 01744 void KFileDialog::writeConfig( KConfig *kc, const QString& group ) 01745 { 01746 if ( !kc ) 01747 return; 01748 01749 QString oldGroup = kc->group(); 01750 if ( !group.isEmpty() ) 01751 kc->setGroup( group ); 01752 01753 kc->writePathEntry( RecentURLs, d->pathCombo->urls() ); 01754 saveDialogSize( group, true ); 01755 kc->writeEntry( PathComboCompletionMode, static_cast<int>(d->pathCombo->completionMode()) ); 01756 kc->writeEntry( LocationComboCompletionMode, static_cast<int>(locationEdit->completionMode()) ); 01757 kc->writeEntry( ShowSpeedbar, d->urlBar && !d->urlBar->isHidden() ); 01758 kc->writeEntry( ShowBookmarks, d->bookmarkHandler != 0 ); 01759 kc->writeEntry( AutoSelectExtChecked, d->autoSelectExtChecked ); 01760 01761 ops->writeConfig( kc, group ); 01762 kc->setGroup( oldGroup ); 01763 } 01764 01765 01766 void KFileDialog::readRecentFiles( KConfig *kc ) 01767 { 01768 QString oldGroup = kc->group(); 01769 kc->setGroup( ConfigGroup ); 01770 01771 locationEdit->setMaxItems( kc->readNumEntry( RecentFilesNumber, 01772 DefaultRecentURLsNumber ) ); 01773 locationEdit->setURLs( kc->readPathListEntry( RecentFiles ), 01774 KURLComboBox::RemoveBottom ); 01775 locationEdit->insertItem( QString::null, 0 ); // dummy item without pixmap 01776 locationEdit->setCurrentItem( 0 ); 01777 01778 kc->setGroup( oldGroup ); 01779 } 01780 01781 void KFileDialog::saveRecentFiles( KConfig *kc ) 01782 { 01783 QString oldGroup = kc->group(); 01784 kc->setGroup( ConfigGroup ); 01785 01786 kc->writePathEntry( RecentFiles, locationEdit->urls() ); 01787 01788 kc->setGroup( oldGroup ); 01789 } 01790 01791 KPushButton * KFileDialog::okButton() const 01792 { 01793 return d->okButton; 01794 } 01795 01796 KPushButton * KFileDialog::cancelButton() const 01797 { 01798 return d->cancelButton; 01799 } 01800 01801 KURLBar * KFileDialog::speedBar() 01802 { 01803 return d->urlBar; 01804 } 01805 01806 void KFileDialog::slotCancel() 01807 { 01808 ops->close(); 01809 KDialogBase::slotCancel(); 01810 01811 KConfig *config = KGlobal::config(); 01812 config->setForceGlobal( true ); 01813 writeConfig( config, ConfigGroup ); 01814 config->setForceGlobal( false ); 01815 } 01816 01817 void KFileDialog::setKeepLocation( bool keep ) 01818 { 01819 d->keepLocation = keep; 01820 } 01821 01822 bool KFileDialog::keepsLocation() const 01823 { 01824 return d->keepLocation; 01825 } 01826 01827 void KFileDialog::setOperationMode( OperationMode mode ) 01828 { 01829 d->operationMode = mode; 01830 d->keepLocation = (mode == Saving); 01831 filterWidget->setEditable( !d->hasDefaultFilter || mode != Saving ); 01832 if ( mode == Opening ) 01833 d->okButton->setGuiItem( KGuiItem( i18n("&Open"), "fileopen") ); 01834 else if ( mode == Saving ) { 01835 d->okButton->setGuiItem( KStdGuiItem::save() ); 01836 setNonExtSelection(); 01837 } 01838 else 01839 d->okButton->setGuiItem( KStdGuiItem::ok() ); 01840 updateLocationWhatsThis (); 01841 updateAutoSelectExtension (); 01842 } 01843 01844 KFileDialog::OperationMode KFileDialog::operationMode() const 01845 { 01846 return d->operationMode; 01847 } 01848 01849 void KFileDialog::slotAutoSelectExtClicked() 01850 { 01851 kdDebug (kfile_area) << "slotAutoSelectExtClicked(): " 01852 << d->autoSelectExtCheckBox->isChecked () << endl; 01853 01854 // whether the _user_ wants it on/off 01855 d->autoSelectExtChecked = d->autoSelectExtCheckBox->isChecked (); 01856 01857 // update the current filename's extension 01858 updateLocationEditExtension (d->extension /* extension hasn't changed */); 01859 } 01860 01861 static QString getExtensionFromPatternList (const QStringList &patternList) 01862 { 01863 QString ret; 01864 kdDebug (kfile_area) << "\tgetExtension " << patternList << endl; 01865 01866 QStringList::ConstIterator patternListEnd = patternList.end (); 01867 for (QStringList::ConstIterator it = patternList.begin (); 01868 it != patternListEnd; 01869 it++) 01870 { 01871 kdDebug (kfile_area) << "\t\ttry: \'" << (*it) << "\'" << endl; 01872 01873 // is this pattern like "*.BMP" rather than useless things like: 01874 // 01875 // README 01876 // *. 01877 // *.* 01878 // *.JP*G 01879 // *.JP? 01880 if ((*it).startsWith ("*.") && 01881 (*it).length () > 2 && 01882 (*it).find ('*', 2) < 0 && (*it).find ('?', 2) < 0) 01883 { 01884 ret = (*it).mid (1); 01885 break; 01886 } 01887 } 01888 01889 return ret; 01890 } 01891 01892 static QString stripUndisplayable (const QString &string) 01893 { 01894 QString ret = string; 01895 01896 ret.remove (':'); 01897 ret.remove ('&'); 01898 01899 return ret; 01900 } 01901 01902 01903 QString KFileDialog::currentFilterExtension (void) 01904 { 01905 return d->extension; 01906 } 01907 01908 void KFileDialog::updateAutoSelectExtension (void) 01909 { 01910 if (!d->autoSelectExtCheckBox) return; 01911 01912 // 01913 // Figure out an extension for the Automatically Select Extension thing 01914 // (some Windows users apparently don't know what to do when confronted 01915 // with a text file called "COPYING" but do know what to do with 01916 // COPYING.txt ...) 01917 // 01918 01919 kdDebug (kfile_area) << "Figure out an extension: " << endl; 01920 QString lastExtension = d->extension; 01921 d->extension = QString::null; 01922 01923 // Automatically Select Extension is only valid if the user is _saving_ a _file_ 01924 if ((operationMode () == Saving) && (mode () & KFile::File)) 01925 { 01926 // 01927 // Get an extension from the filter 01928 // 01929 01930 QString filter = currentFilter (); 01931 if (!filter.isEmpty ()) 01932 { 01933 // e.g. "*.cpp" 01934 if (filter.find ('/') < 0) 01935 { 01936 d->extension = getExtensionFromPatternList (QStringList::split (" ", filter)).lower (); 01937 kdDebug (kfile_area) << "\tsetFilter-style: pattern ext=\'" 01938 << d->extension << "\'" << endl; 01939 } 01940 // e.g. "text/html" 01941 else 01942 { 01943 KMimeType::Ptr mime = KMimeType::mimeType (filter); 01944 01945 // first try X-KDE-NativeExtension 01946 QString nativeExtension = mime->property ("X-KDE-NativeExtension").toString (); 01947 if (nativeExtension.at (0) == '.') 01948 { 01949 d->extension = nativeExtension.lower (); 01950 kdDebug (kfile_area) << "\tsetMimeFilter-style: native ext=\'" 01951 << d->extension << "\'" << endl; 01952 } 01953 01954 // no X-KDE-NativeExtension 01955 if (d->extension.isEmpty ()) 01956 { 01957 d->extension = getExtensionFromPatternList (mime->patterns ()).lower (); 01958 kdDebug (kfile_area) << "\tsetMimeFilter-style: pattern ext=\'" 01959 << d->extension << "\'" << endl; 01960 } 01961 } 01962 } 01963 01964 01965 // 01966 // GUI: checkbox 01967 // 01968 01969 QString whatsThisExtension; 01970 if (!d->extension.isEmpty ()) 01971 { 01972 // remember: sync any changes to the string with below 01973 d->autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension (%1)").arg (d->extension)); 01974 whatsThisExtension = i18n ("the extension <b>%1</b>").arg (d->extension); 01975 01976 d->autoSelectExtCheckBox->setEnabled (true); 01977 d->autoSelectExtCheckBox->setChecked (d->autoSelectExtChecked); 01978 } 01979 else 01980 { 01981 // remember: sync any changes to the string with above 01982 d->autoSelectExtCheckBox->setText (i18n ("Automatically select filename e&xtension")); 01983 whatsThisExtension = i18n ("a suitable extension"); 01984 01985 d->autoSelectExtCheckBox->setChecked (false); 01986 d->autoSelectExtCheckBox->setEnabled (false); 01987 } 01988 01989 const QString locationLabelText = stripUndisplayable (d->locationLabel->text ()); 01990 const QString filterLabelText = stripUndisplayable (d->filterLabel->text ()); 01991 QWhatsThis::add (d->autoSelectExtCheckBox, 01992 "<qt>" + 01993 i18n ( 01994 "This option enables some convenient features for " 01995 "saving files with extensions:<br>" 01996 "<ol>" 01997 "<li>Any extension specified in the <b>%1</b> text " 01998 "area will be updated if you change the file type " 01999 "to save in.<br>" 02000 "<br></li>" 02001 "<li>If no extension is specified in the <b>%2</b> " 02002 "text area when you click " 02003 "<b>Save</b>, %3 will be added to the end of the " 02004 "filename (if the filename does not already exist). " 02005 "This extension is based on the file type that you " 02006 "have chosen to save in.<br>" 02007 "<br>" 02008 "If you do not want KDE to supply an extension for the " 02009 "filename, you can either turn this option off or you " 02010 "can suppress it by adding a period (.) to the end of " 02011 "the filename (the period will be automatically " 02012 "removed)." 02013 "</li>" 02014 "</ol>" 02015 "If unsure, keep this option enabled as it makes your " 02016 "files more manageable." 02017 ) 02018 .arg (locationLabelText) 02019 .arg (locationLabelText) 02020 .arg (whatsThisExtension) 02021 + "</qt>" 02022 ); 02023 02024 d->autoSelectExtCheckBox->show (); 02025 02026 02027 // update the current filename's extension 02028 updateLocationEditExtension (lastExtension); 02029 } 02030 // Automatically Select Extension not valid 02031 else 02032 { 02033 d->autoSelectExtCheckBox->setChecked (false); 02034 d->autoSelectExtCheckBox->hide (); 02035 } 02036 } 02037 02038 // Updates the extension of the filename specified in locationEdit if the 02039 // Automatically Select Extension feature is enabled. 02040 // (this prevents you from accidently saving "file.kwd" as RTF, for example) 02041 void KFileDialog::updateLocationEditExtension (const QString &lastExtension) 02042 { 02043 if (!d->autoSelectExtCheckBox->isChecked () || d->extension.isEmpty ()) 02044 return; 02045 02046 QString urlStr = locationEdit->currentText (); 02047 if (urlStr.isEmpty ()) 02048 return; 02049 02050 KURL url = getCompleteURL (urlStr); 02051 kdDebug (kfile_area) << "updateLocationEditExtension (" << url << ")" << endl; 02052 02053 const int fileNameOffset = urlStr.findRev ('/') + 1; 02054 QString fileName = urlStr.mid (fileNameOffset); 02055 02056 const int dot = fileName.findRev ('.'); 02057 const int len = fileName.length (); 02058 if (dot > 0 && // has an extension already and it's not a hidden file 02059 // like ".hidden" (but we do accept ".hidden.ext") 02060 dot != len - 1 // and not deliberately suppressing extension 02061 ) 02062 { 02063 // exists? 02064 KIO::UDSEntry t; 02065 if (KIO::NetAccess::stat (url, t, topLevelWidget())) 02066 { 02067 kdDebug (kfile_area) << "\tfile exists" << endl; 02068 02069 if (isDirectory (t)) 02070 { 02071 kdDebug (kfile_area) << "\tisDir - won't alter extension" << endl; 02072 return; 02073 } 02074 02075 // --- fall through --- 02076 } 02077 02078 02079 // 02080 // try to get rid of the current extension 02081 // 02082 02083 // catch "double extensions" like ".tar.gz" 02084 if (lastExtension.length () && fileName.endsWith (lastExtension)) 02085 fileName.truncate (len - lastExtension.length ()); 02086 // can only handle "single extensions" 02087 else 02088 fileName.truncate (dot); 02089 02090 // add extension 02091 const QString newText = urlStr.left (fileNameOffset) + fileName + d->extension; 02092 if ( newText != locationEdit->currentText() ) 02093 { 02094 locationEdit->setCurrentText (urlStr.left (fileNameOffset) + fileName + d->extension); 02095 locationEdit->lineEdit()->setEdited (true); 02096 } 02097 } 02098 } 02099 02100 // Updates the filter if the extension of the filename specified in locationEdit is changed 02101 // (this prevents you from accidently saving "file.kwd" as RTF, for example) 02102 void KFileDialog::updateFilter () 02103 { 02104 if ((operationMode() == Saving) && (mode() & KFile::File) ) { 02105 const QString urlStr = locationEdit->currentText (); 02106 if (urlStr.isEmpty ()) 02107 return; 02108 02109 KMimeType::Ptr mime = KMimeType::findByPath(urlStr, 0, true); 02110 if (mime && mime->name() != KMimeType::defaultMimeType()) { 02111 if (filterWidget->currentFilter() != mime->name() && 02112 filterWidget->filters.findIndex(mime->name()) != -1) { 02113 filterWidget->setCurrentFilter(mime->name()); 02114 } 02115 } 02116 } 02117 } 02118 02119 // applies only to a file that doesn't already exist 02120 void KFileDialog::appendExtension (KURL &url) 02121 { 02122 if (!d->autoSelectExtCheckBox->isChecked () || d->extension.isEmpty ()) 02123 return; 02124 02125 QString fileName = url.fileName (); 02126 if (fileName.isEmpty ()) 02127 return; 02128 02129 kdDebug (kfile_area) << "appendExtension(" << url << ")" << endl; 02130 02131 const int len = fileName.length (); 02132 const int dot = fileName.findRev ('.'); 02133 02134 const bool suppressExtension = (dot == len - 1); 02135 const bool unspecifiedExtension = (dot <= 0); 02136 02137 // don't KIO::NetAccess::Stat if unnecessary 02138 if (!(suppressExtension || unspecifiedExtension)) 02139 return; 02140 02141 // exists? 02142 KIO::UDSEntry t; 02143 if (KIO::NetAccess::stat (url, t, topLevelWidget())) 02144 { 02145 kdDebug (kfile_area) << "\tfile exists - won't append extension" << endl; 02146 return; 02147 } 02148 02149 // suppress automatically append extension? 02150 if (suppressExtension) 02151 { 02152 // 02153 // Strip trailing dot 02154 // This allows lazy people to have autoSelectExtCheckBox->isChecked 02155 // but don't want a file extension to be appended 02156 // e.g. "README." will make a file called "README" 02157 // 02158 // If you really want a name like "README.", then type "README.." 02159 // and the trailing dot will be removed (or just stop being lazy and 02160 // turn off this feature so that you can type "README.") 02161 // 02162 kdDebug (kfile_area) << "\tstrip trailing dot" << endl; 02163 url.setFileName (fileName.left (len - 1)); 02164 } 02165 // evilmatically append extension :) if the user hasn't specified one 02166 else if (unspecifiedExtension) 02167 { 02168 kdDebug (kfile_area) << "\tappending extension \'" << d->extension << "\'..." << endl; 02169 url.setFileName (fileName + d->extension); 02170 kdDebug (kfile_area) << "\tsaving as \'" << url << "\'" << endl; 02171 } 02172 } 02173 02174 02175 // adds the selected files/urls to 'recent documents' 02176 void KFileDialog::addToRecentDocuments() 02177 { 02178 int m = ops->mode(); 02179 02180 if ( m & KFile::LocalOnly ) { 02181 QStringList files = selectedFiles(); 02182 QStringList::ConstIterator it = files.begin(); 02183 for ( ; it != files.end(); ++it ) 02184 KRecentDocument::add( *it ); 02185 } 02186 02187 else { // urls 02188 KURL::List urls = selectedURLs(); 02189 KURL::List::ConstIterator it = urls.begin(); 02190 for ( ; it != urls.end(); ++it ) { 02191 if ( (*it).isValid() ) 02192 KRecentDocument::add( *it ); 02193 } 02194 } 02195 } 02196 02197 KActionCollection * KFileDialog::actionCollection() const 02198 { 02199 return ops->actionCollection(); 02200 } 02201 02202 void KFileDialog::keyPressEvent( QKeyEvent *e ) 02203 { 02204 if ( e->key() == Key_Escape ) 02205 { 02206 e->accept(); 02207 d->cancelButton->animateClick(); 02208 } 02209 else 02210 KDialogBase::keyPressEvent( e ); 02211 } 02212 02213 void KFileDialog::toggleSpeedbar( bool show ) 02214 { 02215 if ( show ) 02216 { 02217 if ( !d->urlBar ) 02218 initSpeedbar(); 02219 02220 d->urlBar->show(); 02221 02222 // check to see if they have a home item defined, if not show the home button 02223 KURLBarItem *urlItem = static_cast<KURLBarItem*>( d->urlBar->listBox()->firstItem() ); 02224 KURL homeURL; 02225 homeURL.setPath( QDir::homeDirPath() ); 02226 while ( urlItem ) 02227 { 02228 if ( homeURL.equals( urlItem->url(), true ) ) 02229 { 02230 ops->actionCollection()->action( "home" )->unplug( toolbar ); 02231 break; 02232 } 02233 02234 urlItem = static_cast<KURLBarItem*>( urlItem->next() ); 02235 } 02236 } 02237 else 02238 { 02239 if (d->urlBar) 02240 d->urlBar->hide(); 02241 02242 if ( !ops->actionCollection()->action( "home" )->isPlugged( toolbar ) ) 02243 ops->actionCollection()->action( "home" )->plug( toolbar, 3 ); 02244 } 02245 02246 static_cast<KToggleAction *>(actionCollection()->action("toggleSpeedbar"))->setChecked( show ); 02247 } 02248 02249 void KFileDialog::toggleBookmarks(bool show) 02250 { 02251 if (show) 02252 { 02253 if (d->bookmarkHandler) 02254 { 02255 return; 02256 } 02257 02258 d->bookmarkHandler = new KFileBookmarkHandler( this ); 02259 connect( d->bookmarkHandler, SIGNAL( openURL( const QString& )), 02260 SLOT( enterURL( const QString& ))); 02261 02262 toolbar->insertButton(QString::fromLatin1("bookmark"), 02263 (int)HOTLIST_BUTTON, true, 02264 i18n("Bookmarks"), 5); 02265 toolbar->getButton(HOTLIST_BUTTON)->setPopup(d->bookmarkHandler->menu(), 02266 true); 02267 QWhatsThis::add(toolbar->getButton(HOTLIST_BUTTON), 02268 i18n("<qt>This button allows you to bookmark specific locations. " 02269 "Click on this button to open the bookmark menu where you may add, " 02270 "edit or select a bookmark.<p>" 02271 "These bookmarks are specific to the file dialog, but otherwise operate " 02272 "like bookmarks elsewhere in KDE.</qt>")); 02273 } 02274 else if (d->bookmarkHandler) 02275 { 02276 delete d->bookmarkHandler; 02277 d->bookmarkHandler = 0; 02278 toolbar->removeItem(HOTLIST_BUTTON); 02279 } 02280 02281 static_cast<KToggleAction *>(actionCollection()->action("toggleBookmarks"))->setChecked( show ); 02282 } 02283 02284 int KFileDialog::pathComboIndex() 02285 { 02286 return d->m_pathComboIndex; 02287 } 02288 02289 // static 02290 void KFileDialog::initStatic() 02291 { 02292 if ( lastDirectory ) 02293 return; 02294 02295 lastDirectory = ldd.setObject(lastDirectory, new KURL()); 02296 } 02297 02298 // static 02299 KURL KFileDialog::getStartURL( const QString& startDir, 02300 QString& recentDirClass ) 02301 { 02302 initStatic(); 02303 02304 recentDirClass = QString::null; 02305 KURL ret; 02306 02307 bool useDefaultStartDir = startDir.isEmpty(); 02308 if ( !useDefaultStartDir ) 02309 { 02310 if (startDir[0] == ':') 02311 { 02312 recentDirClass = startDir; 02313 ret = KURL::fromPathOrURL( KRecentDirs::dir(recentDirClass) ); 02314 } 02315 else 02316 { 02317 ret = KCmdLineArgs::makeURL( QFile::encodeName(startDir) ); 02318 // If we won't be able to list it (e.g. http), then use default 02319 if ( !KProtocolInfo::supportsListing( ret ) ) 02320 useDefaultStartDir = true; 02321 } 02322 } 02323 02324 if ( useDefaultStartDir ) 02325 { 02326 if (lastDirectory->isEmpty()) { 02327 lastDirectory->setPath(KGlobalSettings::documentPath()); 02328 KURL home; 02329 home.setPath( QDir::homeDirPath() ); 02330 // if there is no docpath set (== home dir), we prefer the current 02331 // directory over it. We also prefer the homedir when our CWD is 02332 // different from our homedirectory or when the document dir 02333 // does not exist 02334 if ( lastDirectory->path(+1) == home.path(+1) || 02335 QDir::currentDirPath() != QDir::homeDirPath() || 02336 !QDir(lastDirectory->path(+1)).exists() ) 02337 lastDirectory->setPath(QDir::currentDirPath()); 02338 } 02339 ret = *lastDirectory; 02340 } 02341 02342 return ret; 02343 } 02344 02345 void KFileDialog::setStartDir( const KURL& directory ) 02346 { 02347 initStatic(); 02348 if ( directory.isValid() ) 02349 *lastDirectory = directory; 02350 } 02351 02352 void KFileDialog::setNonExtSelection() 02353 { 02354 // Enhanced rename: Don't highlight the file extension. 02355 QString pattern, filename = locationEdit->currentText().stripWhiteSpace(); 02356 KServiceTypeFactory::self()->findFromPattern( filename, &pattern ); 02357 02358 if ( !pattern.isEmpty() && pattern.at( 0 ) == '*' && pattern.find( '*' , 1 ) == -1 ) 02359 locationEdit->lineEdit()->setSelection( 0, filename.length() - pattern.stripWhiteSpace().length()+1 ); 02360 else 02361 { 02362 int lastDot = filename.findRev( '.' ); 02363 if ( lastDot > 0 ) 02364 locationEdit->lineEdit()->setSelection( 0, lastDot ); 02365 } 02366 } 02367 02368 void KFileDialog::virtual_hook( int id, void* data ) 02369 { KDialogBase::virtual_hook( id, data ); } 02370 02371 02372 #include "kfiledialog.moc"