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