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