KIO
kopenwithdialog.cpp
Go to the documentation of this file.
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 Copyright (C) 2007 Pino Toscano <pino@kde.org> 00007 00008 This library is free software; you can redistribute it and/or 00009 modify it under the terms of the GNU Library General Public 00010 License as published by the Free Software Foundation; either 00011 version 2 of the License, or (at your option) any later version. 00012 00013 This library is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 Library General Public License for more details. 00017 00018 You should have received a copy of the GNU Library General Public License 00019 along with this library; see the file COPYING.LIB. If not, write to 00020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 Boston, MA 02110-1301, USA. 00022 */ 00023 00024 #include "kopenwithdialog.h" 00025 #include "kopenwithdialog_p.h" 00026 00027 #include <QtCore/QtAlgorithms> 00028 #include <QtCore/QList> 00029 #include <QtGui/QLabel> 00030 #include <QtGui/QLayout> 00031 #include <QtGui/QCheckBox> 00032 #include <QtGui/QStyle> 00033 #include <QtGui/QStyleOptionButton> 00034 00035 #include <kauthorized.h> 00036 #include <khistorycombobox.h> 00037 #include <kdesktopfile.h> 00038 #include <klineedit.h> 00039 #include <klocale.h> 00040 #include <kmessagebox.h> 00041 #include <krun.h> 00042 #include <kstandarddirs.h> 00043 #include <kstringhandler.h> 00044 #include <kurlcompletion.h> 00045 #include <kurlrequester.h> 00046 #include <kmimetype.h> 00047 #include <kservicegroup.h> 00048 #include <kserviceoffer.h> 00049 #include <kdebug.h> 00050 00051 #include <assert.h> 00052 #include <stdlib.h> 00053 #include <kbuildsycocaprogressdialog.h> 00054 #include <kconfiggroup.h> 00055 00056 inline void writeEntry( KConfigGroup& group, const char* key, 00057 const KGlobalSettings::Completion& aValue, 00058 KConfigBase::WriteConfigFlags flags = KConfigBase::Normal ) 00059 { 00060 group.writeEntry(key, int(aValue), flags); 00061 } 00062 00063 namespace KDEPrivate { 00064 00065 class AppNode 00066 { 00067 public: 00068 AppNode() 00069 : isDir(false), parent(0), fetched(false) 00070 { 00071 } 00072 ~AppNode() 00073 { 00074 qDeleteAll(children); 00075 } 00076 00077 QString icon; 00078 QString text; 00079 QString entryPath; 00080 QString exec; 00081 bool isDir; 00082 00083 AppNode *parent; 00084 bool fetched; 00085 00086 QList<AppNode*> children; 00087 }; 00088 00089 bool AppNodeLessThan(KDEPrivate::AppNode *n1, KDEPrivate::AppNode *n2) 00090 { 00091 if (n1->isDir) { 00092 if (n2->isDir) { 00093 return n1->text.compare(n2->text, Qt::CaseInsensitive) < 0; 00094 } else { 00095 return true; 00096 } 00097 } else { 00098 if (n2->isDir) { 00099 return false; 00100 } else { 00101 return n1->text.compare(n2->text, Qt::CaseInsensitive) < 0; 00102 } 00103 } 00104 return true; 00105 } 00106 00107 } 00108 00109 00110 class KApplicationModelPrivate 00111 { 00112 public: 00113 KApplicationModelPrivate(KApplicationModel *qq) 00114 : q(qq), root(new KDEPrivate::AppNode()) 00115 { 00116 } 00117 ~KApplicationModelPrivate() 00118 { 00119 delete root; 00120 } 00121 00122 void fillNode(const QString &entryPath, KDEPrivate::AppNode *node); 00123 00124 KApplicationModel *q; 00125 00126 KDEPrivate::AppNode *root; 00127 }; 00128 00129 void KApplicationModelPrivate::fillNode(const QString &_entryPath, KDEPrivate::AppNode *node) 00130 { 00131 KServiceGroup::Ptr root = KServiceGroup::group(_entryPath); 00132 if (!root || !root->isValid()) return; 00133 00134 const KServiceGroup::List list = root->entries(); 00135 00136 for( KServiceGroup::List::ConstIterator it = list.begin(); 00137 it != list.end(); ++it) 00138 { 00139 QString icon; 00140 QString text; 00141 QString entryPath; 00142 QString exec; 00143 bool isDir = false; 00144 const KSycocaEntry::Ptr p = (*it); 00145 if (p->isType(KST_KService)) 00146 { 00147 const KService::Ptr service = KService::Ptr::staticCast(p); 00148 00149 if (service->noDisplay()) 00150 continue; 00151 00152 icon = service->icon(); 00153 text = service->name(); 00154 exec = service->exec(); 00155 entryPath = service->entryPath(); 00156 } 00157 else if (p->isType(KST_KServiceGroup)) 00158 { 00159 const KServiceGroup::Ptr serviceGroup = KServiceGroup::Ptr::staticCast(p); 00160 00161 if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0) 00162 continue; 00163 00164 icon = serviceGroup->icon(); 00165 text = serviceGroup->caption(); 00166 entryPath = serviceGroup->entryPath(); 00167 isDir = true; 00168 } 00169 else 00170 { 00171 kWarning(250) << "KServiceGroup: Unexpected object in list!"; 00172 continue; 00173 } 00174 00175 KDEPrivate::AppNode *newnode = new KDEPrivate::AppNode(); 00176 newnode->icon = icon; 00177 newnode->text = text; 00178 newnode->entryPath = entryPath; 00179 newnode->exec = exec; 00180 newnode->isDir = isDir; 00181 newnode->parent = node; 00182 node->children.append(newnode); 00183 } 00184 qStableSort(node->children.begin(), node->children.end(), KDEPrivate::AppNodeLessThan); 00185 } 00186 00187 00188 00189 KApplicationModel::KApplicationModel(QObject *parent) 00190 : QAbstractItemModel(parent), d(new KApplicationModelPrivate(this)) 00191 { 00192 d->fillNode(QString(), d->root); 00193 } 00194 00195 KApplicationModel::~KApplicationModel() 00196 { 00197 delete d; 00198 } 00199 00200 bool KApplicationModel::canFetchMore(const QModelIndex &parent) const 00201 { 00202 if (!parent.isValid()) 00203 return false; 00204 00205 KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer()); 00206 return node->isDir && !node->fetched; 00207 } 00208 00209 int KApplicationModel::columnCount(const QModelIndex &parent) const 00210 { 00211 Q_UNUSED(parent) 00212 return 1; 00213 } 00214 00215 QVariant KApplicationModel::data(const QModelIndex &index, int role) const 00216 { 00217 if (!index.isValid()) 00218 return QVariant(); 00219 00220 KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer()); 00221 00222 switch (role) { 00223 case Qt::DisplayRole: 00224 return node->text; 00225 break; 00226 case Qt::DecorationRole: 00227 if (!node->icon.isEmpty()) { 00228 return KIcon(node->icon); 00229 } 00230 break; 00231 default: 00232 ; 00233 } 00234 return QVariant(); 00235 } 00236 00237 void KApplicationModel::fetchMore(const QModelIndex &parent) 00238 { 00239 if (!parent.isValid()) 00240 return; 00241 00242 KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer()); 00243 if (!node->isDir) 00244 return; 00245 00246 emit layoutAboutToBeChanged(); 00247 d->fillNode(node->entryPath, node); 00248 node->fetched = true; 00249 emit layoutChanged(); 00250 } 00251 00252 bool KApplicationModel::hasChildren(const QModelIndex &parent) const 00253 { 00254 if (!parent.isValid()) 00255 return true; 00256 00257 KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer()); 00258 return node->isDir; 00259 } 00260 00261 QVariant KApplicationModel::headerData(int section, Qt::Orientation orientation, int role) const 00262 { 00263 if (orientation != Qt::Horizontal || section != 0) 00264 return QVariant(); 00265 00266 switch (role) { 00267 case Qt::DisplayRole: 00268 return i18n("Known Applications"); 00269 break; 00270 default: 00271 return QVariant(); 00272 } 00273 } 00274 00275 QModelIndex KApplicationModel::index(int row, int column, const QModelIndex &parent) const 00276 { 00277 if (row < 0 || column != 0) 00278 return QModelIndex(); 00279 00280 KDEPrivate::AppNode *node = d->root; 00281 if (parent.isValid()) 00282 node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer()); 00283 00284 if (row >= node->children.count()) 00285 return QModelIndex(); 00286 else 00287 return createIndex(row, 0, node->children.at(row)); 00288 } 00289 00290 QModelIndex KApplicationModel::parent(const QModelIndex &index) const 00291 { 00292 if (!index.isValid()) 00293 return QModelIndex(); 00294 00295 KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer()); 00296 if (node->parent->parent) { 00297 int id = node->parent->parent->children.indexOf(node->parent); 00298 00299 if (id >= 0 && id < node->parent->parent->children.count()) 00300 return createIndex(id, 0, node->parent); 00301 else 00302 return QModelIndex(); 00303 } 00304 else 00305 return QModelIndex(); 00306 } 00307 00308 int KApplicationModel::rowCount(const QModelIndex &parent) const 00309 { 00310 if (!parent.isValid()) 00311 return d->root->children.count(); 00312 00313 KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer()); 00314 return node->children.count(); 00315 } 00316 00317 QString KApplicationModel::entryPathFor(const QModelIndex &index) const 00318 { 00319 if (!index.isValid()) 00320 return QString(); 00321 00322 KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer()); 00323 return node->entryPath; 00324 } 00325 00326 QString KApplicationModel::execFor(const QModelIndex &index) const 00327 { 00328 if (!index.isValid()) 00329 return QString(); 00330 00331 KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer()); 00332 return node->exec; 00333 } 00334 00335 bool KApplicationModel::isDirectory(const QModelIndex &index) const 00336 { 00337 if (!index.isValid()) 00338 return false; 00339 00340 KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer()); 00341 return node->isDir; 00342 } 00343 00344 class KApplicationViewPrivate 00345 { 00346 public: 00347 KApplicationViewPrivate() 00348 : appModel(0) 00349 { 00350 } 00351 00352 KApplicationModel *appModel; 00353 }; 00354 00355 KApplicationView::KApplicationView(QWidget *parent) 00356 : QTreeView(parent), d(new KApplicationViewPrivate) 00357 { 00358 } 00359 00360 KApplicationView::~KApplicationView() 00361 { 00362 delete d; 00363 } 00364 00365 void KApplicationView::setModel(QAbstractItemModel *model) 00366 { 00367 if (d->appModel) { 00368 disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), 00369 this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection))); 00370 } 00371 00372 QTreeView::setModel(model); 00373 00374 d->appModel = qobject_cast<KApplicationModel*>(model); 00375 if (d->appModel) { 00376 connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), 00377 this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection))); 00378 } 00379 } 00380 00381 bool KApplicationView::isDirSel() const 00382 { 00383 if (d->appModel) { 00384 QModelIndex index = selectionModel()->currentIndex(); 00385 return d->appModel->isDirectory(index); 00386 } 00387 return false; 00388 } 00389 00390 void KApplicationView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) 00391 { 00392 QTreeView::currentChanged(current, previous); 00393 00394 if (d->appModel && !d->appModel->isDirectory(current)) { 00395 QString exec = d->appModel->execFor(current); 00396 if (!exec.isEmpty()) { 00397 emit highlighted(d->appModel->entryPathFor(current), exec); 00398 } 00399 } 00400 } 00401 00402 void KApplicationView::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) 00403 { 00404 Q_UNUSED(deselected) 00405 00406 const QModelIndexList indexes = selected.indexes(); 00407 if (indexes.count() == 1 && !d->appModel->isDirectory(indexes.at(0))) { 00408 QString exec = d->appModel->execFor(indexes.at(0)); 00409 if (!exec.isEmpty()) { 00410 emit this->selected(d->appModel->entryPathFor(indexes.at(0)), exec); 00411 } 00412 } 00413 } 00414 00415 00416 00417 /*************************************************************** 00418 * 00419 * KOpenWithDialog 00420 * 00421 ***************************************************************/ 00422 class KOpenWithDialogPrivate 00423 { 00424 public: 00425 KOpenWithDialogPrivate(KOpenWithDialog *qq) 00426 : q(qq), saveNewApps(false) 00427 { 00428 } 00429 00430 KOpenWithDialog *q; 00431 00435 void setMimeType(const KUrl::List &_urls); 00436 00437 void addToMimeAppsList(const QString& serviceId); 00438 00446 void init(const QString &text, const QString &value); 00447 00451 void saveComboboxHistory(); 00452 00457 bool checkAccept(); 00458 00459 // slots 00460 void _k_slotDbClick(); 00461 00462 bool saveNewApps; 00463 bool m_terminaldirty; 00464 KService::Ptr curService; 00465 KApplicationView *view; 00466 KUrlRequester *edit; 00467 QString m_command; 00468 QLabel *label; 00469 QString qMimeType; 00470 QCheckBox *terminal; 00471 QCheckBox *remember; 00472 QCheckBox *nocloseonexit; 00473 KService::Ptr m_pService; 00474 }; 00475 00476 KOpenWithDialog::KOpenWithDialog( const KUrl::List& _urls, QWidget* parent ) 00477 : KDialog(parent), d(new KOpenWithDialogPrivate(this)) 00478 { 00479 setObjectName( QLatin1String( "openwith" ) ); 00480 setModal( true ); 00481 setCaption( i18n( "Open With" ) ); 00482 00483 QString text; 00484 if( _urls.count() == 1 ) 00485 { 00486 text = i18n("<qt>Select the program that should be used to open <b>%1</b>. " 00487 "If the program is not listed, enter the name or click " 00488 "the browse button.</qt>", _urls.first().fileName() ); 00489 } 00490 else 00491 // Should never happen ?? 00492 text = i18n( "Choose the name of the program with which to open the selected files." ); 00493 d->setMimeType(_urls); 00494 d->init(text, QString()); 00495 } 00496 00497 KOpenWithDialog::KOpenWithDialog( const KUrl::List& _urls, const QString&_text, 00498 const QString& _value, QWidget *parent) 00499 : KDialog(parent), d(new KOpenWithDialogPrivate(this)) 00500 { 00501 setObjectName( QLatin1String( "openwith" ) ); 00502 setModal( true ); 00503 QString caption; 00504 if (_urls.count()>0 && !_urls.first().isEmpty()) 00505 caption = KStringHandler::csqueeze( _urls.first().prettyUrl() ); 00506 if (_urls.count() > 1) 00507 caption += QString::fromLatin1("..."); 00508 setCaption(caption); 00509 d->setMimeType(_urls); 00510 d->init(_text, _value); 00511 } 00512 00513 KOpenWithDialog::KOpenWithDialog( const QString &mimeType, const QString& value, 00514 QWidget *parent) 00515 : KDialog(parent), d(new KOpenWithDialogPrivate(this)) 00516 { 00517 setObjectName( QLatin1String( "openwith" ) ); 00518 setModal( true ); 00519 setCaption(i18n("Choose Application for %1", mimeType)); 00520 QString text = i18n("<qt>Select the program for the file type: <b>%1</b>. " 00521 "If the program is not listed, enter the name or click " 00522 "the browse button.</qt>", mimeType); 00523 d->qMimeType = mimeType; 00524 d->init(text, value); 00525 if (d->remember) { 00526 d->remember->hide(); 00527 } 00528 } 00529 00530 KOpenWithDialog::KOpenWithDialog( QWidget *parent) 00531 : KDialog(parent), d(new KOpenWithDialogPrivate(this)) 00532 { 00533 setObjectName( QLatin1String( "openwith" ) ); 00534 setModal( true ); 00535 setCaption(i18n("Choose Application")); 00536 QString text = i18n("<qt>Select a program. " 00537 "If the program is not listed, enter the name or click " 00538 "the browse button.</qt>"); 00539 d->qMimeType.clear(); 00540 d->init(text, QString()); 00541 } 00542 00543 void KOpenWithDialogPrivate::setMimeType(const KUrl::List &_urls) 00544 { 00545 if ( _urls.count() == 1 ) 00546 { 00547 qMimeType = KMimeType::findByUrl( _urls.first())->name(); 00548 if (qMimeType == QLatin1String("application/octet-stream")) 00549 qMimeType.clear(); 00550 } 00551 else 00552 qMimeType.clear(); 00553 } 00554 00555 void KOpenWithDialogPrivate::init(const QString &_text, const QString &_value) 00556 { 00557 bool bReadOnly = !KAuthorized::authorize("shell_access"); 00558 m_terminaldirty = false; 00559 view = 0; 00560 m_pService = 0; 00561 curService = 0; 00562 00563 q->setButtons(KDialog::Ok | KDialog::Cancel); 00564 00565 QWidget *mainWidget = q->mainWidget(); 00566 00567 QBoxLayout *topLayout = new QVBoxLayout( mainWidget ); 00568 topLayout->setMargin(0); 00569 label = new QLabel(_text, q); 00570 label->setWordWrap(true); 00571 topLayout->addWidget(label); 00572 00573 if (!bReadOnly) 00574 { 00575 // init the history combo and insert it into the URL-Requester 00576 KHistoryComboBox *combo = new KHistoryComboBox(); 00577 KLineEdit *lineEdit = new KLineEdit(q); 00578 lineEdit->setClearButtonShown(true); 00579 combo->setLineEdit(lineEdit); 00580 combo->setDuplicatesEnabled( false ); 00581 KConfigGroup cg( KGlobal::config(), QString::fromLatin1("Open-with settings") ); 00582 int max = cg.readEntry( "Maximum history", 15 ); 00583 combo->setMaxCount( max ); 00584 int mode = cg.readEntry( "CompletionMode", int(KGlobalSettings::completionMode())); 00585 combo->setCompletionMode((KGlobalSettings::Completion)mode); 00586 const QStringList list = cg.readEntry( "History", QStringList() ); 00587 combo->setHistoryItems( list, true ); 00588 edit = new KUrlRequester( combo, mainWidget ); 00589 } 00590 else 00591 { 00592 edit = new KUrlRequester( mainWidget ); 00593 edit->lineEdit()->setReadOnly(true); 00594 edit->button()->hide(); 00595 } 00596 00597 edit->setText( _value ); 00598 edit->setWhatsThis(i18n( 00599 "Following the command, you can have several place holders which will be replaced " 00600 "with the actual values when the actual program is run:\n" 00601 "%f - a single file name\n" 00602 "%F - a list of files; use for applications that can open several local files at once\n" 00603 "%u - a single URL\n" 00604 "%U - a list of URLs\n" 00605 "%d - the directory of the file to open\n" 00606 "%D - a list of directories\n" 00607 "%i - the icon\n" 00608 "%m - the mini-icon\n" 00609 "%c - the comment")); 00610 00611 topLayout->addWidget(edit); 00612 00613 if ( edit->comboBox() ) { 00614 KUrlCompletion *comp = new KUrlCompletion( KUrlCompletion::ExeCompletion ); 00615 edit->comboBox()->setCompletionObject( comp ); 00616 edit->comboBox()->setAutoDeleteCompletionObject( true ); 00617 } 00618 00619 QObject::connect(edit, SIGNAL(textChanged(QString)), q, SLOT(slotTextChanged())); 00620 00621 view = new KApplicationView(mainWidget); 00622 view->setModel(new KApplicationModel(view)); 00623 topLayout->addWidget(view); 00624 topLayout->setStretchFactor(view, 1); 00625 00626 QObject::connect(view, SIGNAL(selected(QString, QString)), 00627 q, SLOT(slotSelected(QString, QString))); 00628 QObject::connect(view, SIGNAL(highlighted(QString, QString)), 00629 q, SLOT(slotHighlighted(QString, QString))); 00630 QObject::connect(view, SIGNAL(doubleClicked(QModelIndex)), 00631 q, SLOT(_k_slotDbClick())); 00632 00633 terminal = new QCheckBox( i18n("Run in &terminal"), mainWidget ); 00634 if (bReadOnly) 00635 terminal->hide(); 00636 QObject::connect(terminal, SIGNAL(toggled(bool)), q, SLOT(slotTerminalToggled(bool))); 00637 00638 topLayout->addWidget(terminal); 00639 00640 QStyleOptionButton checkBoxOption; 00641 checkBoxOption.initFrom(terminal); 00642 int checkBoxIndentation = terminal->style()->pixelMetric( QStyle::PM_IndicatorWidth, &checkBoxOption, terminal ); 00643 checkBoxIndentation += terminal->style()->pixelMetric( QStyle::PM_CheckBoxLabelSpacing, &checkBoxOption, terminal ); 00644 00645 QBoxLayout* nocloseonexitLayout = new QHBoxLayout(); 00646 nocloseonexitLayout->setMargin( 0 ); 00647 QSpacerItem* spacer = new QSpacerItem( checkBoxIndentation, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ); 00648 nocloseonexitLayout->addItem( spacer ); 00649 00650 nocloseonexit = new QCheckBox( i18n("&Do not close when command exits"), mainWidget ); 00651 nocloseonexit->setChecked( false ); 00652 nocloseonexit->setDisabled( true ); 00653 00654 // check to see if we use konsole if not disable the nocloseonexit 00655 // because we don't know how to do this on other terminal applications 00656 KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") ); 00657 QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); 00658 00659 if (bReadOnly || preferredTerminal != "konsole") 00660 nocloseonexit->hide(); 00661 00662 nocloseonexitLayout->addWidget( nocloseonexit ); 00663 topLayout->addLayout( nocloseonexitLayout ); 00664 00665 if (!qMimeType.isNull()) 00666 { 00667 remember = new QCheckBox(i18n("&Remember application association for this type of file"), mainWidget); 00668 // remember->setChecked(true); 00669 topLayout->addWidget(remember); 00670 } 00671 else 00672 remember = 0L; 00673 00674 q->setMinimumSize(q->minimumSizeHint()); 00675 //edit->setText( _value ); 00676 // This is what caused "can't click on items before clicking on Name header". 00677 // Probably due to the resizeEvent handler using width(). 00678 //resize( minimumWidth(), sizeHint().height() ); 00679 edit->setFocus(); 00680 q->slotTextChanged(); 00681 } 00682 00683 00684 // ---------------------------------------------------------------------- 00685 00686 KOpenWithDialog::~KOpenWithDialog() 00687 { 00688 delete d; 00689 } 00690 00691 00692 // ---------------------------------------------------------------------- 00693 00694 void KOpenWithDialog::slotSelected( const QString& /*_name*/, const QString& _exec ) 00695 { 00696 KService::Ptr pService = d->curService; 00697 d->edit->setText(_exec); // calls slotTextChanged :( 00698 d->curService = pService; 00699 } 00700 00701 00702 // ---------------------------------------------------------------------- 00703 00704 void KOpenWithDialog::slotHighlighted(const QString& entryPath, const QString&) 00705 { 00706 d->curService = KService::serviceByDesktopPath(entryPath); 00707 if (!d->m_terminaldirty) 00708 { 00709 // ### indicate that default value was restored 00710 d->terminal->setChecked(d->curService->terminal()); 00711 QString terminalOptions = d->curService->terminalOptions(); 00712 d->nocloseonexit->setChecked((terminalOptions.contains(QLatin1String("--noclose")) > 0)); 00713 d->m_terminaldirty = false; // slotTerminalToggled changed it 00714 } 00715 } 00716 00717 // ---------------------------------------------------------------------- 00718 00719 void KOpenWithDialog::slotTextChanged() 00720 { 00721 // Forget about the service 00722 d->curService = 0L; 00723 enableButton(Ok, !d->edit->text().isEmpty()); 00724 } 00725 00726 // ---------------------------------------------------------------------- 00727 00728 void KOpenWithDialog::slotTerminalToggled(bool) 00729 { 00730 // ### indicate that default value was overridden 00731 d->m_terminaldirty = true; 00732 d->nocloseonexit->setDisabled(!d->terminal->isChecked()); 00733 } 00734 00735 // ---------------------------------------------------------------------- 00736 00737 void KOpenWithDialogPrivate::_k_slotDbClick() 00738 { 00739 // check if a directory is selected 00740 if (view->isDirSel()) { 00741 return; 00742 } 00743 q->accept(); 00744 } 00745 00746 void KOpenWithDialog::setSaveNewApplications(bool b) 00747 { 00748 d->saveNewApps = b; 00749 } 00750 00751 static QString simplifiedExecLineFromService(const QString& fullExec) 00752 { 00753 QString exec = fullExec; 00754 exec.remove("%u", Qt::CaseInsensitive); 00755 exec.remove("%f", Qt::CaseInsensitive); 00756 exec.remove("-caption %c"); 00757 exec.remove("-caption \"%c\""); 00758 exec.remove("%i"); 00759 exec.remove("%m"); 00760 return exec.simplified(); 00761 } 00762 00763 void KOpenWithDialogPrivate::addToMimeAppsList(const QString& serviceId /*menu id or storage id*/) 00764 { 00765 KSharedConfig::Ptr profile = KSharedConfig::openConfig("mimeapps.list", KConfig::NoGlobals, "xdgdata-apps"); 00766 KConfigGroup addedApps(profile, "Added Associations"); 00767 QStringList apps = addedApps.readXdgListEntry(qMimeType); 00768 apps.removeAll(serviceId); 00769 apps.prepend(serviceId); // make it the preferred app 00770 addedApps.writeXdgListEntry(qMimeType, apps); 00771 addedApps.sync(); 00772 00773 // Also make sure the "auto embed" setting for this mimetype is off 00774 KSharedConfig::Ptr fileTypesConfig = KSharedConfig::openConfig("filetypesrc", KConfig::NoGlobals); 00775 fileTypesConfig->group("EmbedSettings").writeEntry(QString("embed-")+qMimeType, false); 00776 fileTypesConfig->sync(); 00777 00778 kDebug(250) << "rebuilding ksycoca..."; 00779 00780 // kbuildsycoca is the one reading mimeapps.list, so we need to run it now 00781 KBuildSycocaProgressDialog::rebuildKSycoca(q); 00782 00783 m_pService = KService::serviceByStorageId(serviceId); 00784 Q_ASSERT( m_pService ); 00785 } 00786 00787 bool KOpenWithDialogPrivate::checkAccept() 00788 { 00789 const QString typedExec(edit->text()); 00790 if (typedExec.isEmpty()) 00791 return false; 00792 QString fullExec(typedExec); 00793 00794 QString serviceName; 00795 QString initialServiceName; 00796 QString preferredTerminal; 00797 QString binaryName; 00798 m_pService = curService; 00799 if (!m_pService) { 00800 // No service selected - check the command line 00801 00802 // Find out the name of the service from the command line, removing args and paths 00803 serviceName = KRun::binaryName( typedExec, true ); 00804 if (serviceName.isEmpty()) { 00805 KMessageBox::error(q, i18n("Could not extract executable name from '%1', please type a valid program name.", serviceName)); 00806 return false; 00807 } 00808 initialServiceName = serviceName; 00809 // Also remember the binaryName with a path, if any, for the 00810 // check that the binary exists. 00811 binaryName = KRun::binaryName(typedExec, false); 00812 kDebug(250) << "initialServiceName=" << initialServiceName << "binaryName=" << binaryName; 00813 int i = 1; // We have app, app-2, app-3... Looks better for the user. 00814 bool ok = false; 00815 // Check if there's already a service by that name, with the same Exec line 00816 do { 00817 kDebug(250) << "looking for service" << serviceName; 00818 KService::Ptr serv = KService::serviceByDesktopName( serviceName ); 00819 ok = !serv; // ok if no such service yet 00820 // also ok if we find the exact same service (well, "kwrite" == "kwrite %U") 00821 if (serv) { 00822 if (serv->isApplication()) { 00823 /*kDebug(250) << "typedExec=" << typedExec 00824 << "serv->exec=" << serv->exec() 00825 << "simplifiedExecLineFromService=" << simplifiedExecLineFromService(fullExec);*/ 00826 if (typedExec == simplifiedExecLineFromService(serv->exec())) { 00827 ok = true; 00828 m_pService = serv; 00829 kDebug(250) << "OK, found identical service: " << serv->entryPath(); 00830 } else { 00831 kDebug(250) << "Exec line differs, service says:" << simplifiedExecLineFromService(fullExec); 00832 } 00833 } else { 00834 kDebug(250) << "Found, but not an application:" << serv->entryPath(); 00835 } 00836 } 00837 if (!ok) { // service was found, but it was different -> keep looking 00838 ++i; 00839 serviceName = initialServiceName + '-' + QString::number(i); 00840 } 00841 } while (!ok); 00842 } 00843 if ( m_pService ) { 00844 // Existing service selected 00845 serviceName = m_pService->name(); 00846 initialServiceName = serviceName; 00847 fullExec = m_pService->exec(); 00848 } else { 00849 // Ensure that the typed binary name actually exists (#81190) 00850 if (KStandardDirs::findExe(binaryName).isEmpty()) { 00851 KMessageBox::error(q, i18n("'%1' not found, please type a valid program name.", binaryName)); 00852 return false; 00853 } 00854 } 00855 00856 if (terminal->isChecked()) { 00857 KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") ); 00858 preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); 00859 m_command = preferredTerminal; 00860 // only add --noclose when we are sure it is konsole we're using 00861 if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) 00862 m_command += QString::fromLatin1(" --noclose"); 00863 m_command += QString::fromLatin1(" -e "); 00864 m_command += edit->text(); 00865 kDebug(250) << "Setting m_command to" << m_command; 00866 } 00867 if ( m_pService && terminal->isChecked() != m_pService->terminal() ) 00868 m_pService = 0; // It's not exactly this service we're running 00869 00870 00871 const bool bRemember = remember && remember->isChecked(); 00872 kDebug(250) << "bRemember=" << bRemember << "service found=" << m_pService; 00873 if (m_pService) { 00874 if (bRemember) { 00875 // Associate this app with qMimeType in mimeapps.list 00876 Q_ASSERT(!qMimeType.isEmpty()); // we don't show the remember checkbox otherwise 00877 addToMimeAppsList(m_pService->storageId()); 00878 } 00879 } else { 00880 const bool createDesktopFile = bRemember || saveNewApps; 00881 if (!createDesktopFile) { 00882 // Create temp service 00883 m_pService = new KService(initialServiceName, fullExec, QString()); 00884 if (terminal->isChecked()) { 00885 m_pService->setTerminal(true); 00886 // only add --noclose when we are sure it is konsole we're using 00887 if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) 00888 m_pService->setTerminalOptions("--noclose"); 00889 } 00890 } else { 00891 // If we got here, we can't seem to find a service for what they wanted. Create one. 00892 00893 QString menuId; 00894 QString newPath = KService::newServicePath(false /* ignored argument */, serviceName, &menuId); 00895 kDebug(250) << "Creating new service" << serviceName << "(" << newPath << ")" << "menuId=" << menuId; 00896 00897 KDesktopFile desktopFile(newPath); 00898 KConfigGroup cg = desktopFile.desktopGroup(); 00899 cg.writeEntry("Type", "Application"); 00900 cg.writeEntry("Name", initialServiceName); 00901 cg.writeEntry("Exec", fullExec); 00902 cg.writeEntry("NoDisplay", true); // don't make it appear in the K menu 00903 if (terminal->isChecked()) { 00904 cg.writeEntry("Terminal", true); 00905 // only add --noclose when we are sure it is konsole we're using 00906 if (preferredTerminal == "konsole" && nocloseonexit->isChecked()) 00907 cg.writeEntry("TerminalOptions", "--noclose"); 00908 } 00909 cg.writeXdgListEntry("MimeType", QStringList() << qMimeType); 00910 cg.sync(); 00911 00912 addToMimeAppsList(menuId); 00913 } 00914 } 00915 00916 saveComboboxHistory(); 00917 return true; 00918 } 00919 00920 void KOpenWithDialog::accept() 00921 { 00922 if (d->checkAccept()) 00923 KDialog::accept(); 00924 } 00925 00926 QString KOpenWithDialog::text() const 00927 { 00928 if (!d->m_command.isEmpty()) 00929 return d->m_command; 00930 else 00931 return d->edit->text(); 00932 } 00933 00934 void KOpenWithDialog::hideNoCloseOnExit() 00935 { 00936 // uncheck the checkbox because the value could be used when "Run in Terminal" is selected 00937 d->nocloseonexit->setChecked(false); 00938 d->nocloseonexit->hide(); 00939 } 00940 00941 void KOpenWithDialog::hideRunInTerminal() 00942 { 00943 d->terminal->hide(); 00944 hideNoCloseOnExit(); 00945 } 00946 00947 KService::Ptr KOpenWithDialog::service() const 00948 { 00949 return d->m_pService; 00950 } 00951 00952 void KOpenWithDialogPrivate::saveComboboxHistory() 00953 { 00954 KHistoryComboBox *combo = static_cast<KHistoryComboBox*>(edit->comboBox()); 00955 if (combo) { 00956 combo->addToHistory(edit->text()); 00957 00958 KConfigGroup cg( KGlobal::config(), QString::fromLatin1("Open-with settings") ); 00959 cg.writeEntry( "History", combo->historyItems() ); 00960 writeEntry( cg, "CompletionMode", combo->completionMode() ); 00961 // don't store the completion-list, as it contains all of KUrlCompletion's 00962 // executables 00963 cg.sync(); 00964 } 00965 } 00966 00967 #include "kopenwithdialog.moc" 00968 #include "kopenwithdialog_p.moc"
KDE 4.7 API Reference