• Skip to content
  • Skip to link menu
KDE 4.8 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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 &current, 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->setDuplicatesEnabled( false );
00583     KConfigGroup cg( KGlobal::config(), QString::fromLatin1("Open-with settings") );
00584     int max = cg.readEntry( "Maximum history", 15 );
00585     combo->setMaxCount( max );
00586     int mode = cg.readEntry( "CompletionMode", int(KGlobalSettings::completionMode()));
00587     combo->setCompletionMode((KGlobalSettings::Completion)mode);
00588     const QStringList list = cg.readEntry( "History", QStringList() );
00589     combo->setHistoryItems( list, true );
00590     edit = new KUrlRequester( combo, mainWidget );
00591   }
00592   else
00593   {
00594     edit = new KUrlRequester( mainWidget );
00595     edit->lineEdit()->setReadOnly(true);
00596     edit->button()->hide();
00597   }
00598 
00599   edit->setText( _value );
00600   edit->setWhatsThis(i18n(
00601     "Following the command, you can have several place holders which will be replaced "
00602     "with the actual values when the actual program is run:\n"
00603     "%f - a single file name\n"
00604     "%F - a list of files; use for applications that can open several local files at once\n"
00605     "%u - a single URL\n"
00606     "%U - a list of URLs\n"
00607     "%d - the directory of the file to open\n"
00608     "%D - a list of directories\n"
00609     "%i - the icon\n"
00610     "%m - the mini-icon\n"
00611     "%c - the comment"));
00612 
00613   topLayout->addWidget(edit);
00614 
00615   if ( edit->comboBox() ) {
00616     KUrlCompletion *comp = new KUrlCompletion( KUrlCompletion::ExeCompletion );
00617     edit->comboBox()->setCompletionObject( comp );
00618     edit->comboBox()->setAutoDeleteCompletionObject( true );
00619   }
00620 
00621     QObject::connect(edit, SIGNAL(textChanged(QString)), q, SLOT(slotTextChanged()));
00622     QObject::connect(edit, SIGNAL(urlSelected(KUrl)), q, SLOT(_k_slotFileSelected()));
00623 
00624     view = new KApplicationView(mainWidget);
00625     view->setModel(new KApplicationModel(view));
00626     topLayout->addWidget(view);
00627     topLayout->setStretchFactor(view, 1);
00628 
00629     QObject::connect(view, SIGNAL(selected(QString, QString)),
00630                      q, SLOT(slotSelected(QString, QString)));
00631     QObject::connect(view, SIGNAL(highlighted(QString, QString)),
00632                      q, SLOT(slotHighlighted(QString, QString)));
00633     QObject::connect(view, SIGNAL(doubleClicked(QModelIndex)),
00634                      q, SLOT(_k_slotDbClick()));
00635 
00636   terminal = new QCheckBox( i18n("Run in &terminal"), mainWidget );
00637   if (bReadOnly)
00638      terminal->hide();
00639     QObject::connect(terminal, SIGNAL(toggled(bool)), q, SLOT(slotTerminalToggled(bool)));
00640 
00641   topLayout->addWidget(terminal);
00642 
00643   QStyleOptionButton checkBoxOption;
00644   checkBoxOption.initFrom(terminal);
00645   int checkBoxIndentation = terminal->style()->pixelMetric( QStyle::PM_IndicatorWidth, &checkBoxOption, terminal );
00646   checkBoxIndentation += terminal->style()->pixelMetric( QStyle::PM_CheckBoxLabelSpacing, &checkBoxOption, terminal );
00647 
00648   QBoxLayout* nocloseonexitLayout = new QHBoxLayout();
00649   nocloseonexitLayout->setMargin( 0 );
00650   QSpacerItem* spacer = new QSpacerItem( checkBoxIndentation, 0, QSizePolicy::Fixed, QSizePolicy::Minimum );
00651   nocloseonexitLayout->addItem( spacer );
00652 
00653   nocloseonexit = new QCheckBox( i18n("&Do not close when command exits"), mainWidget );
00654   nocloseonexit->setChecked( false );
00655   nocloseonexit->setDisabled( true );
00656 
00657   // check to see if we use konsole if not disable the nocloseonexit
00658   // because we don't know how to do this on other terminal applications
00659   KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
00660   QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole"));
00661 
00662   if (bReadOnly || preferredTerminal != "konsole")
00663      nocloseonexit->hide();
00664 
00665   nocloseonexitLayout->addWidget( nocloseonexit );
00666   topLayout->addLayout( nocloseonexitLayout );
00667 
00668   if (!qMimeType.isNull())
00669   {
00670     remember = new QCheckBox(i18n("&Remember application association for this type of file"), mainWidget);
00671     //    remember->setChecked(true);
00672     topLayout->addWidget(remember);
00673   }
00674   else
00675     remember = 0L;
00676 
00677   q->setMinimumSize(q->minimumSizeHint());
00678   //edit->setText( _value );
00679   // This is what caused "can't click on items before clicking on Name header".
00680   // Probably due to the resizeEvent handler using width().
00681   //resize( minimumWidth(), sizeHint().height() );
00682   edit->setFocus();
00683     q->slotTextChanged();
00684 }
00685 
00686 
00687 // ----------------------------------------------------------------------
00688 
00689 KOpenWithDialog::~KOpenWithDialog()
00690 {
00691     delete d;
00692 }
00693 
00694 
00695 // ----------------------------------------------------------------------
00696 
00697 void KOpenWithDialog::slotSelected( const QString& /*_name*/, const QString& _exec )
00698 {
00699     KService::Ptr pService = d->curService;
00700     d->edit->setText(_exec); // calls slotTextChanged :(
00701     d->curService = pService;
00702 }
00703 
00704 
00705 // ----------------------------------------------------------------------
00706 
00707 void KOpenWithDialog::slotHighlighted(const QString& entryPath, const QString&)
00708 {
00709     d->curService = KService::serviceByDesktopPath(entryPath);
00710     if (!d->m_terminaldirty)
00711     {
00712         // ### indicate that default value was restored
00713         d->terminal->setChecked(d->curService->terminal());
00714         QString terminalOptions = d->curService->terminalOptions();
00715         d->nocloseonexit->setChecked((terminalOptions.contains(QLatin1String("--noclose")) > 0));
00716         d->m_terminaldirty = false; // slotTerminalToggled changed it
00717     }
00718 }
00719 
00720 // ----------------------------------------------------------------------
00721 
00722 void KOpenWithDialog::slotTextChanged()
00723 {
00724     // Forget about the service
00725     d->curService = 0L;
00726     enableButton(Ok, !d->edit->text().isEmpty());
00727 }
00728 
00729 // ----------------------------------------------------------------------
00730 
00731 void KOpenWithDialog::slotTerminalToggled(bool)
00732 {
00733     // ### indicate that default value was overridden
00734     d->m_terminaldirty = true;
00735     d->nocloseonexit->setDisabled(!d->terminal->isChecked());
00736 }
00737 
00738 // ----------------------------------------------------------------------
00739 
00740 void KOpenWithDialogPrivate::_k_slotDbClick()
00741 {
00742     // check if a directory is selected
00743     if (view->isDirSel()) {
00744         return;
00745     }
00746     q->accept();
00747 }
00748 
00749 void KOpenWithDialogPrivate::_k_slotFileSelected()
00750 {
00751     // quote the path to avoid unescaped whitespace, backslashes, etc.
00752     edit->setText(KShell::quoteArg(edit->text()));
00753 }
00754 
00755 void KOpenWithDialog::setSaveNewApplications(bool b)
00756 {
00757   d->saveNewApps = b;
00758 }
00759 
00760 static QString simplifiedExecLineFromService(const QString& fullExec)
00761 {
00762     QString exec = fullExec;
00763     exec.remove("%u", Qt::CaseInsensitive);
00764     exec.remove("%f", Qt::CaseInsensitive);
00765     exec.remove("-caption %c");
00766     exec.remove("-caption \"%c\"");
00767     exec.remove("%i");
00768     exec.remove("%m");
00769     return exec.simplified();
00770 }
00771 
00772 void KOpenWithDialogPrivate::addToMimeAppsList(const QString& serviceId /*menu id or storage id*/)
00773 {
00774     KSharedConfig::Ptr profile = KSharedConfig::openConfig("mimeapps.list", KConfig::NoGlobals, "xdgdata-apps");
00775     KConfigGroup addedApps(profile, "Added Associations");
00776     QStringList apps = addedApps.readXdgListEntry(qMimeType);
00777     apps.removeAll(serviceId);
00778     apps.prepend(serviceId); // make it the preferred app
00779     addedApps.writeXdgListEntry(qMimeType, apps);
00780     addedApps.sync();
00781 
00782     // Also make sure the "auto embed" setting for this mimetype is off
00783     KSharedConfig::Ptr fileTypesConfig = KSharedConfig::openConfig("filetypesrc", KConfig::NoGlobals);
00784     fileTypesConfig->group("EmbedSettings").writeEntry(QString("embed-")+qMimeType, false);
00785     fileTypesConfig->sync();
00786 
00787     kDebug(250) << "rebuilding ksycoca...";
00788 
00789     // kbuildsycoca is the one reading mimeapps.list, so we need to run it now
00790     KBuildSycocaProgressDialog::rebuildKSycoca(q);
00791 
00792     m_pService = KService::serviceByStorageId(serviceId);
00793     Q_ASSERT( m_pService );
00794 }
00795 
00796 bool KOpenWithDialogPrivate::checkAccept()
00797 {
00798     const QString typedExec(edit->text());
00799     if (typedExec.isEmpty())
00800         return false;
00801     QString fullExec(typedExec);
00802 
00803     QString serviceName;
00804     QString initialServiceName;
00805     QString preferredTerminal;
00806     QString binaryName;
00807     m_pService = curService;
00808     if (!m_pService) {
00809         // No service selected - check the command line
00810 
00811         // Find out the name of the service from the command line, removing args and paths
00812         serviceName = KRun::binaryName( typedExec, true );
00813         if (serviceName.isEmpty()) {
00814             KMessageBox::error(q, i18n("Could not extract executable name from '%1', please type a valid program name.", serviceName));
00815             return false;
00816         }
00817         initialServiceName = serviceName;
00818         // Also remember the binaryName with a path, if any, for the
00819         // check that the binary exists.
00820         binaryName = KRun::binaryName(typedExec, false);
00821         kDebug(250) << "initialServiceName=" << initialServiceName << "binaryName=" << binaryName;
00822         int i = 1; // We have app, app-2, app-3... Looks better for the user.
00823         bool ok = false;
00824         // Check if there's already a service by that name, with the same Exec line
00825         do {
00826             kDebug(250) << "looking for service" << serviceName;
00827             KService::Ptr serv = KService::serviceByDesktopName( serviceName );
00828             ok = !serv; // ok if no such service yet
00829             // also ok if we find the exact same service (well, "kwrite" == "kwrite %U")
00830             if (serv) {
00831                 if (serv->isApplication()) {
00832                     /*kDebug(250) << "typedExec=" << typedExec
00833                       << "serv->exec=" << serv->exec()
00834                       << "simplifiedExecLineFromService=" << simplifiedExecLineFromService(fullExec);*/
00835                     if (typedExec == simplifiedExecLineFromService(serv->exec())) {
00836                         ok = true;
00837                         m_pService = serv;
00838                         kDebug(250) << "OK, found identical service: " << serv->entryPath();
00839                     } else {
00840                         kDebug(250) << "Exec line differs, service says:" << simplifiedExecLineFromService(fullExec);
00841                     }
00842                 } else {
00843                     kDebug(250) << "Found, but not an application:" << serv->entryPath();
00844                 }
00845             }
00846             if (!ok) { // service was found, but it was different -> keep looking
00847                 ++i;
00848                 serviceName = initialServiceName + '-' + QString::number(i);
00849             }
00850         } while (!ok);
00851     }
00852     if ( m_pService ) {
00853         // Existing service selected
00854         serviceName = m_pService->name();
00855         initialServiceName = serviceName;
00856         fullExec = m_pService->exec();
00857     } else {
00858         // Ensure that the typed binary name actually exists (#81190)
00859         if (KStandardDirs::findExe(binaryName).isEmpty()) {
00860             KMessageBox::error(q, i18n("'%1' not found, please type a valid program name.", binaryName));
00861             return false;
00862         }
00863     }
00864 
00865     if (terminal->isChecked()) {
00866         KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
00867         preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole"));
00868         m_command = preferredTerminal;
00869         // only add --noclose when we are sure it is konsole we're using
00870         if (preferredTerminal == "konsole" && nocloseonexit->isChecked())
00871             m_command += QString::fromLatin1(" --noclose");
00872         m_command += QString::fromLatin1(" -e ");
00873         m_command += edit->text();
00874         kDebug(250) << "Setting m_command to" << m_command;
00875     }
00876     if ( m_pService && terminal->isChecked() != m_pService->terminal() )
00877         m_pService = 0; // It's not exactly this service we're running
00878 
00879 
00880     const bool bRemember = remember && remember->isChecked();
00881     kDebug(250) << "bRemember=" << bRemember << "service found=" << m_pService;
00882     if (m_pService) {
00883         if (bRemember) {
00884             // Associate this app with qMimeType in mimeapps.list
00885             Q_ASSERT(!qMimeType.isEmpty()); // we don't show the remember checkbox otherwise
00886             addToMimeAppsList(m_pService->storageId());
00887         }
00888     } else {
00889         const bool createDesktopFile = bRemember || saveNewApps;
00890         if (!createDesktopFile) {
00891             // Create temp service
00892             m_pService = new KService(initialServiceName, fullExec, QString());
00893             if (terminal->isChecked()) {
00894                 m_pService->setTerminal(true);
00895                 // only add --noclose when we are sure it is konsole we're using
00896                 if (preferredTerminal == "konsole" && nocloseonexit->isChecked())
00897                     m_pService->setTerminalOptions("--noclose");
00898             }
00899         } else {
00900             // If we got here, we can't seem to find a service for what they wanted. Create one.
00901 
00902             QString menuId;
00903             QString newPath = KService::newServicePath(false /* ignored argument */, serviceName, &menuId);
00904             kDebug(250) << "Creating new service" << serviceName << "(" << newPath << ")" << "menuId=" << menuId;
00905 
00906             KDesktopFile desktopFile(newPath);
00907             KConfigGroup cg = desktopFile.desktopGroup();
00908             cg.writeEntry("Type", "Application");
00909             cg.writeEntry("Name", initialServiceName);
00910             cg.writeEntry("Exec", fullExec);
00911             cg.writeEntry("NoDisplay", true); // don't make it appear in the K menu
00912             if (terminal->isChecked()) {
00913                 cg.writeEntry("Terminal", true);
00914                 // only add --noclose when we are sure it is konsole we're using
00915                 if (preferredTerminal == "konsole" && nocloseonexit->isChecked())
00916                     cg.writeEntry("TerminalOptions", "--noclose");
00917             }
00918             cg.writeXdgListEntry("MimeType", QStringList() << qMimeType);
00919             cg.sync();
00920 
00921             addToMimeAppsList(menuId);
00922         }
00923     }
00924 
00925     saveComboboxHistory();
00926     return true;
00927 }
00928 
00929 void KOpenWithDialog::accept()
00930 {
00931     if (d->checkAccept())
00932         KDialog::accept();
00933 }
00934 
00935 QString KOpenWithDialog::text() const
00936 {
00937     if (!d->m_command.isEmpty())
00938         return d->m_command;
00939     else
00940         return d->edit->text();
00941 }
00942 
00943 void KOpenWithDialog::hideNoCloseOnExit()
00944 {
00945     // uncheck the checkbox because the value could be used when "Run in Terminal" is selected
00946     d->nocloseonexit->setChecked(false);
00947     d->nocloseonexit->hide();
00948 }
00949 
00950 void KOpenWithDialog::hideRunInTerminal()
00951 {
00952     d->terminal->hide();
00953     hideNoCloseOnExit();
00954 }
00955 
00956 KService::Ptr KOpenWithDialog::service() const
00957 {
00958     return d->m_pService;
00959 }
00960 
00961 void KOpenWithDialogPrivate::saveComboboxHistory()
00962 {
00963     KHistoryComboBox *combo = static_cast<KHistoryComboBox*>(edit->comboBox());
00964     if (combo) {
00965         combo->addToHistory(edit->text());
00966 
00967         KConfigGroup cg( KGlobal::config(), QString::fromLatin1("Open-with settings") );
00968         cg.writeEntry( "History", combo->historyItems() );
00969         writeEntry( cg, "CompletionMode", combo->completionMode() );
00970         // don't store the completion-list, as it contains all of KUrlCompletion's
00971         // executables
00972         cg.sync();
00973     }
00974 }
00975 
00976 #include "kopenwithdialog.moc"
00977 #include "kopenwithdialog_p.moc"

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal