kopenwith.cpp
00001 /* This file is part of the KDE libraries 00002 00003 Copyright (C) 1997 Torben Weis <weis@stud.uni-frankfurt.de> 00004 Copyright (C) 1999 Dirk Mueller <mueller@kde.org> 00005 Portions copyright (C) 1999 Preston Brown <pbrown@kde.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00020 Boston, MA 02110-1301, USA. 00021 */ 00022 00023 #include <qfile.h> 00024 #include <qdir.h> 00025 #include <qdialog.h> 00026 #include <qimage.h> 00027 #include <qpixmap.h> 00028 #include <qlabel.h> 00029 #include <qlayout.h> 00030 #include <qpushbutton.h> 00031 #include <qtoolbutton.h> 00032 #include <qcheckbox.h> 00033 #include <qtooltip.h> 00034 #include <qstyle.h> 00035 #include <qwhatsthis.h> 00036 00037 #include <kapplication.h> 00038 #include <kbuttonbox.h> 00039 #include <kcombobox.h> 00040 #include <kdesktopfile.h> 00041 #include <kdialog.h> 00042 #include <kglobal.h> 00043 #include <klineedit.h> 00044 #include <klocale.h> 00045 #include <kiconloader.h> 00046 #include <kmimemagic.h> 00047 #include <krun.h> 00048 #include <kstandarddirs.h> 00049 #include <kstringhandler.h> 00050 #include <kuserprofile.h> 00051 #include <kurlcompletion.h> 00052 #include <kurlrequester.h> 00053 #include <dcopclient.h> 00054 #include <kmimetype.h> 00055 #include <kservicegroup.h> 00056 #include <klistview.h> 00057 #include <ksycoca.h> 00058 #include <kstdguiitem.h> 00059 00060 #include "kopenwith.h" 00061 #include "kopenwith_p.h" 00062 00063 #include <kdebug.h> 00064 #include <assert.h> 00065 #include <stdlib.h> 00066 00067 #define SORT_SPEC (QDir::DirsFirst | QDir::Name | QDir::IgnoreCase) 00068 00069 00070 // ---------------------------------------------------------------------- 00071 00072 KAppTreeListItem::KAppTreeListItem( KListView* parent, const QString & name, 00073 const QPixmap& pixmap, bool parse, bool dir, const QString &p, const QString &c ) 00074 : QListViewItem( parent, name ) 00075 { 00076 init(pixmap, parse, dir, p, c); 00077 } 00078 00079 00080 // ---------------------------------------------------------------------- 00081 00082 KAppTreeListItem::KAppTreeListItem( QListViewItem* parent, const QString & name, 00083 const QPixmap& pixmap, bool parse, bool dir, const QString &p, const QString &c ) 00084 : QListViewItem( parent, name ) 00085 { 00086 init(pixmap, parse, dir, p, c); 00087 } 00088 00089 00090 // ---------------------------------------------------------------------- 00091 00092 void KAppTreeListItem::init(const QPixmap& pixmap, bool parse, bool dir, const QString &_path, const QString &_exec) 00093 { 00094 setPixmap(0, pixmap); 00095 parsed = parse; 00096 directory = dir; 00097 path = _path; // relative path 00098 exec = _exec; 00099 } 00100 00101 00102 /* Ensures that directories sort before non-directories */ 00103 int KAppTreeListItem::compare(QListViewItem *i, int col, bool ascending) const 00104 { 00105 KAppTreeListItem *other = dynamic_cast<KAppTreeListItem *>(i); 00106 00107 // Directories sort first 00108 if (directory && !other->directory) 00109 return -1; 00110 00111 else if (!directory && other->directory) 00112 return 1; 00113 00114 else // both directories or both not 00115 return QListViewItem::compare(i, col, ascending); 00116 } 00117 00118 // ---------------------------------------------------------------------- 00119 // Ensure that case is ignored 00120 QString KAppTreeListItem::key(int column, bool /*ascending*/) const 00121 { 00122 return text(column).upper(); 00123 } 00124 00125 void KAppTreeListItem::activate() 00126 { 00127 if ( directory ) 00128 setOpen(!isOpen()); 00129 } 00130 00131 void KAppTreeListItem::setOpen( bool o ) 00132 { 00133 if( o && !parsed ) { // fill the children before opening 00134 ((KApplicationTree *) parent())->addDesktopGroup( path, this ); 00135 parsed = true; 00136 } 00137 QListViewItem::setOpen( o ); 00138 } 00139 00140 bool KAppTreeListItem::isDirectory() 00141 { 00142 return directory; 00143 } 00144 00145 // ---------------------------------------------------------------------- 00146 00147 KApplicationTree::KApplicationTree( QWidget *parent ) 00148 : KListView( parent ), currentitem(0) 00149 { 00150 addColumn( i18n("Known Applications") ); 00151 setRootIsDecorated( true ); 00152 00153 addDesktopGroup( QString::null ); 00154 cleanupTree(); 00155 00156 connect( this, SIGNAL( currentChanged(QListViewItem*) ), 00157 SLOT( slotItemHighlighted(QListViewItem*) ) ); 00158 connect( this, SIGNAL( selectionChanged(QListViewItem*) ), 00159 SLOT( slotSelectionChanged(QListViewItem*) ) ); 00160 } 00161 00162 // ---------------------------------------------------------------------- 00163 00164 bool KApplicationTree::isDirSel() 00165 { 00166 if (!currentitem) return false; // if currentitem isn't set 00167 return currentitem->isDirectory(); 00168 } 00169 00170 // ---------------------------------------------------------------------- 00171 00172 static QPixmap appIcon(const QString &iconName) 00173 { 00174 QPixmap normal = KGlobal::iconLoader()->loadIcon(iconName, KIcon::Small, 0, KIcon::DefaultState, 0L, true); 00175 // make sure they are not larger than 20x20 00176 if (normal.width() > 20 || normal.height() > 20) 00177 { 00178 QImage tmp = normal.convertToImage(); 00179 tmp = tmp.smoothScale(20, 20); 00180 normal.convertFromImage(tmp); 00181 } 00182 return normal; 00183 } 00184 00185 void KApplicationTree::addDesktopGroup( const QString &relPath, KAppTreeListItem *item) 00186 { 00187 KServiceGroup::Ptr root = KServiceGroup::group(relPath); 00188 if (!root || !root->isValid()) return; 00189 00190 KServiceGroup::List list = root->entries(); 00191 00192 KAppTreeListItem * newItem; 00193 for( KServiceGroup::List::ConstIterator it = list.begin(); 00194 it != list.end(); it++) 00195 { 00196 QString icon; 00197 QString text; 00198 QString relPath; 00199 QString exec; 00200 bool isDir = false; 00201 KSycocaEntry *p = (*it); 00202 if (p->isType(KST_KService)) 00203 { 00204 KService *service = static_cast<KService *>(p); 00205 00206 if (service->noDisplay()) 00207 continue; 00208 00209 icon = service->icon(); 00210 text = service->name(); 00211 exec = service->exec(); 00212 } 00213 else if (p->isType(KST_KServiceGroup)) 00214 { 00215 KServiceGroup *serviceGroup = static_cast<KServiceGroup *>(p); 00216 00217 if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0) 00218 continue; 00219 00220 icon = serviceGroup->icon(); 00221 text = serviceGroup->caption(); 00222 relPath = serviceGroup->relPath(); 00223 isDir = true; 00224 } 00225 else 00226 { 00227 kdWarning(250) << "KServiceGroup: Unexpected object in list!" << endl; 00228 continue; 00229 } 00230 00231 QPixmap pixmap = appIcon( icon ); 00232 00233 if (item) 00234 newItem = new KAppTreeListItem( item, text, pixmap, false, isDir, 00235 relPath, exec ); 00236 else 00237 newItem = new KAppTreeListItem( this, text, pixmap, false, isDir, 00238 relPath, exec ); 00239 if (isDir) 00240 newItem->setExpandable( true ); 00241 } 00242 } 00243 00244 00245 // ---------------------------------------------------------------------- 00246 00247 void KApplicationTree::slotItemHighlighted(QListViewItem* i) 00248 { 00249 // i may be 0 (see documentation) 00250 if(!i) 00251 return; 00252 00253 KAppTreeListItem *item = (KAppTreeListItem *) i; 00254 00255 currentitem = item; 00256 00257 if( (!item->directory ) && (!item->exec.isEmpty()) ) 00258 emit highlighted( item->text(0), item->exec ); 00259 } 00260 00261 00262 // ---------------------------------------------------------------------- 00263 00264 void KApplicationTree::slotSelectionChanged(QListViewItem* i) 00265 { 00266 // i may be 0 (see documentation) 00267 if(!i) 00268 return; 00269 00270 KAppTreeListItem *item = (KAppTreeListItem *) i; 00271 00272 currentitem = item; 00273 00274 if( ( !item->directory ) && (!item->exec.isEmpty() ) ) 00275 emit selected( item->text(0), item->exec ); 00276 } 00277 00278 // ---------------------------------------------------------------------- 00279 00280 void KApplicationTree::resizeEvent( QResizeEvent * e) 00281 { 00282 setColumnWidth(0, width()-QApplication::style().pixelMetric(QStyle::PM_ScrollBarExtent) 00283 -2*QApplication::style().pixelMetric(QStyle::PM_DefaultFrameWidth)); 00284 KListView::resizeEvent(e); 00285 } 00286 00287 // Prune empty directories from the tree 00288 void KApplicationTree::cleanupTree() 00289 { 00290 QListViewItem *item=firstChild(); 00291 while(item!=0) 00292 { 00293 if(item->isExpandable()) 00294 { 00295 QListViewItem *temp=item->itemBelow(); 00296 if(item->text(0)!=i18n("Applications")) 00297 item->setOpen(false); 00298 item=temp; 00299 continue; 00300 } 00301 item=item->itemBelow(); 00302 } 00303 } 00304 00305 /*************************************************************** 00306 * 00307 * KOpenWithDlg 00308 * 00309 ***************************************************************/ 00310 class KOpenWithDlgPrivate 00311 { 00312 public: 00313 KOpenWithDlgPrivate() : saveNewApps(false) { }; 00314 QPushButton* ok; 00315 bool saveNewApps; 00316 KService::Ptr curService; 00317 }; 00318 00319 KOpenWithDlg::KOpenWithDlg( const KURL::List& _urls, QWidget* parent ) 00320 :QDialog( parent, "openwith", true ) 00321 { 00322 setCaption( i18n( "Open With" ) ); 00323 QString text; 00324 if( _urls.count() == 1 ) 00325 { 00326 text = i18n("<qt>Select the program that should be used to open <b>%1</b>. " 00327 "If the program is not listed, enter the name or click " 00328 "the browse button.</qt>").arg( _urls.first().fileName() ); 00329 } 00330 else 00331 // Should never happen ?? 00332 text = i18n( "Choose the name of the program with which to open the selected files." ); 00333 setServiceType( _urls ); 00334 init( text, QString() ); 00335 } 00336 00337 KOpenWithDlg::KOpenWithDlg( const KURL::List& _urls, const QString&_text, 00338 const QString& _value, QWidget *parent) 00339 :QDialog( parent, "openwith", true ) 00340 { 00341 QString caption = KStringHandler::csqueeze( _urls.first().prettyURL() ); 00342 if (_urls.count() > 1) 00343 caption += QString::fromLatin1("..."); 00344 setCaption(caption); 00345 setServiceType( _urls ); 00346 init( _text, _value ); 00347 } 00348 00349 KOpenWithDlg::KOpenWithDlg( const QString &serviceType, const QString& value, 00350 QWidget *parent) 00351 :QDialog( parent, "openwith", true ) 00352 { 00353 setCaption(i18n("Choose Application for %1").arg(serviceType)); 00354 QString text = i18n("<qt>Select the program for the file type: <b>%1</b>. " 00355 "If the program is not listed, enter the name or click " 00356 "the browse button.</qt>").arg(serviceType); 00357 qServiceType = serviceType; 00358 init( text, value ); 00359 if (remember) 00360 remember->hide(); 00361 } 00362 00363 KOpenWithDlg::KOpenWithDlg( QWidget *parent) 00364 :QDialog( parent, "openwith", true ) 00365 { 00366 setCaption(i18n("Choose Application")); 00367 QString text = i18n("<qt>Select a program. " 00368 "If the program is not listed, enter the name or click " 00369 "the browse button.</qt>"); 00370 qServiceType = QString::null; 00371 init( text, QString::null ); 00372 } 00373 00374 void KOpenWithDlg::setServiceType( const KURL::List& _urls ) 00375 { 00376 if ( _urls.count() == 1 ) 00377 { 00378 qServiceType = KMimeType::findByURL( _urls.first())->name(); 00379 if (qServiceType == QString::fromLatin1("application/octet-stream")) 00380 qServiceType = QString::null; 00381 } 00382 else 00383 qServiceType = QString::null; 00384 } 00385 00386 void KOpenWithDlg::init( const QString& _text, const QString& _value ) 00387 { 00388 d = new KOpenWithDlgPrivate; 00389 bool bReadOnly = kapp && !kapp->authorize("shell_access"); 00390 m_terminaldirty = false; 00391 m_pTree = 0L; 00392 m_pService = 0L; 00393 d->curService = 0L; 00394 00395 QBoxLayout *topLayout = new QVBoxLayout( this, KDialog::marginHint(), 00396 KDialog::spacingHint() ); 00397 label = new QLabel( _text, this ); 00398 topLayout->addWidget(label); 00399 00400 QHBoxLayout* hbox = new QHBoxLayout(topLayout); 00401 00402 QToolButton *clearButton = new QToolButton( this ); 00403 clearButton->setIconSet( BarIcon( "locationbar_erase" ) ); 00404 clearButton->setFixedSize( clearButton->sizeHint() ); 00405 connect( clearButton, SIGNAL( clicked() ), SLOT( slotClear() ) ); 00406 QToolTip::add( clearButton, i18n( "Clear input field" ) ); 00407 00408 hbox->addWidget( clearButton ); 00409 00410 if (!bReadOnly) 00411 { 00412 // init the history combo and insert it into the URL-Requester 00413 KHistoryCombo *combo = new KHistoryCombo(); 00414 combo->setDuplicatesEnabled( false ); 00415 KConfig *kc = KGlobal::config(); 00416 KConfigGroupSaver ks( kc, QString::fromLatin1("Open-with settings") ); 00417 int max = kc->readNumEntry( QString::fromLatin1("Maximum history"), 15 ); 00418 combo->setMaxCount( max ); 00419 int mode = kc->readNumEntry(QString::fromLatin1("CompletionMode"), 00420 KGlobalSettings::completionMode()); 00421 combo->setCompletionMode((KGlobalSettings::Completion)mode); 00422 QStringList list = kc->readListEntry( QString::fromLatin1("History") ); 00423 combo->setHistoryItems( list, true ); 00424 edit = new KURLRequester( combo, this ); 00425 } 00426 else 00427 { 00428 clearButton->hide(); 00429 edit = new KURLRequester( this ); 00430 edit->lineEdit()->setReadOnly(true); 00431 edit->button()->hide(); 00432 } 00433 00434 edit->setURL( _value ); 00435 QWhatsThis::add(edit,i18n( 00436 "Following the command, you can have several place holders which will be replaced " 00437 "with the actual values when the actual program is run:\n" 00438 "%f - a single file name\n" 00439 "%F - a list of files; use for applications that can open several local files at once\n" 00440 "%u - a single URL\n" 00441 "%U - a list of URLs\n" 00442 "%d - the directory of the file to open\n" 00443 "%D - a list of directories\n" 00444 "%i - the icon\n" 00445 "%m - the mini-icon\n" 00446 "%c - the comment")); 00447 00448 hbox->addWidget(edit); 00449 00450 if ( edit->comboBox() ) { 00451 KURLCompletion *comp = new KURLCompletion( KURLCompletion::ExeCompletion ); 00452 edit->comboBox()->setCompletionObject( comp ); 00453 edit->comboBox()->setAutoDeleteCompletionObject( true ); 00454 } 00455 00456 connect ( edit, SIGNAL(returnPressed()), SLOT(slotOK()) ); 00457 connect ( edit, SIGNAL(textChanged(const QString&)), SLOT(slotTextChanged()) ); 00458 00459 m_pTree = new KApplicationTree( this ); 00460 topLayout->addWidget(m_pTree); 00461 00462 connect( m_pTree, SIGNAL( selected( const QString&, const QString& ) ), 00463 SLOT( slotSelected( const QString&, const QString& ) ) ); 00464 connect( m_pTree, SIGNAL( highlighted( const QString&, const QString& ) ), 00465 SLOT( slotHighlighted( const QString&, const QString& ) ) ); 00466 connect( m_pTree, SIGNAL( doubleClicked(QListViewItem*) ), 00467 SLOT( slotDbClick() ) ); 00468 00469 terminal = new QCheckBox( i18n("Run in &terminal"), this ); 00470 if (bReadOnly) 00471 terminal->hide(); 00472 connect(terminal, SIGNAL(toggled(bool)), SLOT(slotTerminalToggled(bool))); 00473 00474 topLayout->addWidget(terminal); 00475 00476 QBoxLayout* nocloseonexitLayout = new QHBoxLayout( 0, 0, KDialog::spacingHint() ); 00477 QSpacerItem* spacer = new QSpacerItem( 20, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ); 00478 nocloseonexitLayout->addItem( spacer ); 00479 00480 nocloseonexit = new QCheckBox( i18n("&Do not close when command exits"), this ); 00481 nocloseonexit->setChecked( false ); 00482 nocloseonexit->setDisabled( true ); 00483 00484 // check to see if we use konsole if not disable the nocloseonexit 00485 // because we don't know how to do this on other terminal applications 00486 KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") ); 00487 QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); 00488 00489 if (bReadOnly || preferredTerminal != "konsole") 00490 nocloseonexit->hide(); 00491 00492 nocloseonexitLayout->addWidget( nocloseonexit ); 00493 topLayout->addLayout( nocloseonexitLayout ); 00494 00495 if (!qServiceType.isNull()) 00496 { 00497 remember = new QCheckBox(i18n("&Remember application association for this type of file"), this); 00498 // remember->setChecked(true); 00499 topLayout->addWidget(remember); 00500 } 00501 else 00502 remember = 0L; 00503 00504 // Use KButtonBox for the aligning pushbuttons nicely 00505 KButtonBox* b = new KButtonBox( this ); 00506 b->addStretch( 2 ); 00507 00508 d->ok = b->addButton( KStdGuiItem::ok() ); 00509 d->ok->setDefault( true ); 00510 connect( d->ok, SIGNAL( clicked() ), SLOT( slotOK() ) ); 00511 00512 QPushButton* cancel = b->addButton( KStdGuiItem::cancel() ); 00513 connect( cancel, SIGNAL( clicked() ), SLOT( reject() ) ); 00514 00515 b->layout(); 00516 topLayout->addWidget( b ); 00517 00518 //edit->setText( _value ); 00519 // This is what caused "can't click on items before clicking on Name header". 00520 // Probably due to the resizeEvent handler using width(). 00521 //resize( minimumWidth(), sizeHint().height() ); 00522 edit->setFocus(); 00523 slotTextChanged(); 00524 } 00525 00526 00527 // ---------------------------------------------------------------------- 00528 00529 KOpenWithDlg::~KOpenWithDlg() 00530 { 00531 delete d; 00532 d = 0; 00533 } 00534 00535 // ---------------------------------------------------------------------- 00536 00537 void KOpenWithDlg::slotClear() 00538 { 00539 edit->setURL(QString::null); 00540 edit->setFocus(); 00541 } 00542 00543 00544 // ---------------------------------------------------------------------- 00545 00546 void KOpenWithDlg::slotSelected( const QString& /*_name*/, const QString& _exec ) 00547 { 00548 kdDebug(250)<<"KOpenWithDlg::slotSelected"<<endl; 00549 KService::Ptr pService = d->curService; 00550 edit->setURL( _exec ); // calls slotTextChanged :( 00551 d->curService = pService; 00552 } 00553 00554 00555 // ---------------------------------------------------------------------- 00556 00557 void KOpenWithDlg::slotHighlighted( const QString& _name, const QString& ) 00558 { 00559 kdDebug(250)<<"KOpenWithDlg::slotHighlighted"<<endl; 00560 qName = _name; 00561 d->curService = KService::serviceByName( qName ); 00562 if (!m_terminaldirty) 00563 { 00564 // ### indicate that default value was restored 00565 terminal->setChecked(d->curService->terminal()); 00566 QString terminalOptions = d->curService->terminalOptions(); 00567 nocloseonexit->setChecked( (terminalOptions.contains( "--noclose" ) > 0) ); 00568 m_terminaldirty = false; // slotTerminalToggled changed it 00569 } 00570 } 00571 00572 // ---------------------------------------------------------------------- 00573 00574 void KOpenWithDlg::slotTextChanged() 00575 { 00576 kdDebug(250)<<"KOpenWithDlg::slotTextChanged"<<endl; 00577 // Forget about the service 00578 d->curService = 0L; 00579 d->ok->setEnabled( !edit->url().isEmpty()); 00580 } 00581 00582 // ---------------------------------------------------------------------- 00583 00584 void KOpenWithDlg::slotTerminalToggled(bool) 00585 { 00586 // ### indicate that default value was overridden 00587 m_terminaldirty = true; 00588 nocloseonexit->setDisabled( ! terminal->isChecked() ); 00589 } 00590 00591 // ---------------------------------------------------------------------- 00592 00593 void KOpenWithDlg::slotDbClick() 00594 { 00595 if (m_pTree->isDirSel() ) return; // check if a directory is selected 00596 slotOK(); 00597 } 00598 00599 void KOpenWithDlg::setSaveNewApplications(bool b) 00600 { 00601 d->saveNewApps = b; 00602 } 00603 00604 void KOpenWithDlg::slotOK() 00605 { 00606 QString typedExec(edit->url()); 00607 QString fullExec(typedExec); 00608 00609 QString serviceName; 00610 QString initialServiceName; 00611 QString preferredTerminal; 00612 m_pService = d->curService; 00613 if (!m_pService) { 00614 // No service selected - check the command line 00615 00616 // Find out the name of the service from the command line, removing args and paths 00617 serviceName = KRun::binaryName( typedExec, true ); 00618 if (serviceName.isEmpty()) 00619 { 00620 // TODO add a KMessageBox::error here after the end of the message freeze 00621 return; 00622 } 00623 initialServiceName = serviceName; 00624 kdDebug(250) << "initialServiceName=" << initialServiceName << endl; 00625 int i = 1; // We have app, app-2, app-3... Looks better for the user. 00626 bool ok = false; 00627 // Check if there's already a service by that name, with the same Exec line 00628 do { 00629 kdDebug(250) << "looking for service " << serviceName << endl; 00630 KService::Ptr serv = KService::serviceByDesktopName( serviceName ); 00631 ok = !serv; // ok if no such service yet 00632 // also ok if we find the exact same service (well, "kwrite" == "kwrite %U" 00633 if ( serv && serv->type() == "Application") 00634 { 00635 QString exec = serv->exec(); 00636 fullExec = exec; 00637 exec.replace("%u", "", false); 00638 exec.replace("%f", "", false); 00639 exec.replace("-caption %c", ""); 00640 exec.replace("-caption \"%c\"", ""); 00641 exec.replace("%i", ""); 00642 exec.replace("%m", ""); 00643 exec = exec.simplifyWhiteSpace(); 00644 if (exec == typedExec) 00645 { 00646 ok = true; 00647 m_pService = serv; 00648 kdDebug(250) << k_funcinfo << "OK, found identical service: " << serv->desktopEntryPath() << endl; 00649 } 00650 } 00651 if (!ok) // service was found, but it was different -> keep looking 00652 { 00653 ++i; 00654 serviceName = initialServiceName + "-" + QString::number(i); 00655 } 00656 } 00657 while (!ok); 00658 } 00659 if ( m_pService ) 00660 { 00661 // Existing service selected 00662 serviceName = m_pService->name(); 00663 initialServiceName = serviceName; 00664 fullExec = m_pService->exec(); 00665 } 00666 00667 if (terminal->isChecked()) 00668 { 00669 KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") ); 00670 preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); 00671 m_command = preferredTerminal; 00672 // only add --noclose when we are sure it is konsole we're using 00673 if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) 00674 m_command += QString::fromLatin1(" --noclose"); 00675 m_command += QString::fromLatin1(" -e "); 00676 m_command += edit->url(); 00677 kdDebug(250) << "Setting m_command to " << m_command << endl; 00678 } 00679 if ( m_pService && terminal->isChecked() != m_pService->terminal() ) 00680 m_pService = 0L; // It's not exactly this service we're running 00681 00682 bool bRemember = remember && remember->isChecked(); 00683 00684 if ( !bRemember && m_pService) 00685 { 00686 accept(); 00687 return; 00688 } 00689 00690 if (!bRemember && !d->saveNewApps) 00691 { 00692 // Create temp service 00693 m_pService = new KService(initialServiceName, fullExec, QString::null); 00694 if (terminal->isChecked()) 00695 { 00696 m_pService->setTerminal(true); 00697 // only add --noclose when we are sure it is konsole we're using 00698 if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) 00699 m_pService->setTerminalOptions("--noclose"); 00700 } 00701 accept(); 00702 return; 00703 } 00704 00705 // if we got here, we can't seem to find a service for what they 00706 // wanted. The other possibility is that they have asked for the 00707 // association to be remembered. Create/update service. 00708 00709 QString newPath; 00710 QString oldPath; 00711 QString menuId; 00712 if (m_pService) 00713 { 00714 oldPath = m_pService->desktopEntryPath(); 00715 newPath = m_pService->locateLocal(); 00716 menuId = m_pService->menuId(); 00717 kdDebug(250) << "Updating exitsing service " << m_pService->desktopEntryPath() << " ( " << newPath << " ) " << endl; 00718 } 00719 else 00720 { 00721 newPath = KService::newServicePath(false /* hidden */, serviceName, &menuId); 00722 kdDebug(250) << "Creating new service " << serviceName << " ( " << newPath << " ) " << endl; 00723 } 00724 00725 int maxPreference = 1; 00726 if (!qServiceType.isEmpty()) 00727 { 00728 KServiceTypeProfile::OfferList offerList = KServiceTypeProfile::offers( qServiceType ); 00729 if (!offerList.isEmpty()) 00730 maxPreference = offerList.first().preference(); 00731 } 00732 00733 KDesktopFile *desktop = 0; 00734 if (!oldPath.isEmpty() && (oldPath != newPath)) 00735 { 00736 KDesktopFile orig(oldPath, true); 00737 desktop = orig.copyTo(newPath); 00738 } 00739 else 00740 { 00741 desktop = new KDesktopFile(newPath); 00742 } 00743 desktop->writeEntry("Type", QString::fromLatin1("Application")); 00744 desktop->writeEntry("Name", initialServiceName); 00745 desktop->writePathEntry("Exec", fullExec); 00746 if (terminal->isChecked()) 00747 { 00748 desktop->writeEntry("Terminal", true); 00749 // only add --noclose when we are sure it is konsole we're using 00750 if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) 00751 desktop->writeEntry("TerminalOptions", "--noclose"); 00752 } 00753 else 00754 { 00755 desktop->writeEntry("Terminal", false); 00756 } 00757 desktop->writeEntry("InitialPreference", maxPreference + 1); 00758 00759 00760 if (bRemember || d->saveNewApps) 00761 { 00762 QStringList mimeList = desktop->readListEntry("MimeType", ';'); 00763 if (!qServiceType.isEmpty() && !mimeList.contains(qServiceType)) 00764 mimeList.append(qServiceType); 00765 desktop->writeEntry("MimeType", mimeList, ';'); 00766 00767 if ( !qServiceType.isEmpty() ) 00768 { 00769 // Also make sure the "auto embed" setting for this mimetype is off 00770 KDesktopFile mimeDesktop( locateLocal( "mime", qServiceType + ".desktop" ) ); 00771 mimeDesktop.writeEntry( "X-KDE-AutoEmbed", false ); 00772 mimeDesktop.sync(); 00773 } 00774 } 00775 00776 // write it all out to the file 00777 desktop->sync(); 00778 delete desktop; 00779 00780 KService::rebuildKSycoca(this); 00781 00782 m_pService = KService::serviceByMenuId( menuId ); 00783 00784 Q_ASSERT( m_pService ); 00785 00786 accept(); 00787 } 00788 00789 QString KOpenWithDlg::text() const 00790 { 00791 if (!m_command.isEmpty()) 00792 return m_command; 00793 else 00794 return edit->url(); 00795 } 00796 00797 void KOpenWithDlg::hideNoCloseOnExit() 00798 { 00799 // uncheck the checkbox because the value could be used when "Run in Terminal" is selected 00800 nocloseonexit->setChecked( false ); 00801 nocloseonexit->hide(); 00802 } 00803 00804 void KOpenWithDlg::hideRunInTerminal() 00805 { 00806 terminal->hide(); 00807 hideNoCloseOnExit(); 00808 } 00809 00810 void KOpenWithDlg::accept() 00811 { 00812 KHistoryCombo *combo = static_cast<KHistoryCombo*>( edit->comboBox() ); 00813 if ( combo ) { 00814 combo->addToHistory( edit->url() ); 00815 00816 KConfig *kc = KGlobal::config(); 00817 KConfigGroupSaver ks( kc, QString::fromLatin1("Open-with settings") ); 00818 kc->writeEntry( QString::fromLatin1("History"), combo->historyItems() ); 00819 kc->writeEntry(QString::fromLatin1("CompletionMode"), 00820 combo->completionMode()); 00821 // don't store the completion-list, as it contains all of KURLCompletion's 00822 // executables 00823 kc->sync(); 00824 } 00825 00826 QDialog::accept(); 00827 } 00828 00829 00831 00832 #ifndef KDE_NO_COMPAT 00833 bool KFileOpenWithHandler::displayOpenWithDialog( const KURL::List& urls ) 00834 { 00835 KOpenWithDlg l( urls, i18n("Open with:"), QString::null, 0L ); 00836 if ( l.exec() ) 00837 { 00838 KService::Ptr service = l.service(); 00839 if ( !!service ) 00840 return KRun::run( *service, urls ); 00841 00842 kdDebug(250) << "No service set, running " << l.text() << endl; 00843 return KRun::run( l.text(), urls ); 00844 } 00845 return false; 00846 } 00847 #endif 00848 00849 #include "kopenwith.moc" 00850 #include "kopenwith_p.moc" 00851