• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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 <kiconloader.h>
00041 #include <kmessagebox.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     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 
00463     bool saveNewApps;
00464     bool m_terminaldirty;
00465     KService::Ptr curService;
00466     KApplicationView *view;
00467     KUrlRequester *edit;
00468     QString m_command;
00469     QLabel *label;
00470     QString qMimeType;
00471     QCheckBox *terminal;
00472     QCheckBox *remember;
00473     QCheckBox *nocloseonexit;
00474     KService::Ptr m_pService;
00475 };
00476 
00477 KOpenWithDialog::KOpenWithDialog( const KUrl::List& _urls, QWidget* parent )
00478     : KDialog(parent), d(new KOpenWithDialogPrivate(this))
00479 {
00480     setObjectName( QLatin1String( "openwith" ) );
00481     setModal( true );
00482     setCaption( i18n( "Open With" ) );
00483 
00484     QString text;
00485     if( _urls.count() == 1 )
00486     {
00487         text = i18n("<qt>Select the program that should be used to open <b>%1</b>. "
00488                      "If the program is not listed, enter the name or click "
00489                      "the browse button.</qt>",  _urls.first().fileName() );
00490     }
00491     else
00492         // Should never happen ??
00493         text = i18n( "Choose the name of the program with which to open the selected files." );
00494     d->setMimeType(_urls);
00495     d->init(text, QString());
00496 }
00497 
00498 KOpenWithDialog::KOpenWithDialog( const KUrl::List& _urls, const QString&_text,
00499                             const QString& _value, QWidget *parent)
00500     : KDialog(parent), d(new KOpenWithDialogPrivate(this))
00501 {
00502   setObjectName( QLatin1String( "openwith" ) );
00503   setModal( true );
00504   QString caption;
00505   if (_urls.count()>0 && !_urls.first().isEmpty())
00506      caption = KStringHandler::csqueeze( _urls.first().prettyUrl() );
00507   if (_urls.count() > 1)
00508       caption += QString::fromLatin1("...");
00509   setCaption(caption);
00510     d->setMimeType(_urls);
00511     d->init(_text, _value);
00512 }
00513 
00514 KOpenWithDialog::KOpenWithDialog( const QString &mimeType, const QString& value,
00515                             QWidget *parent)
00516     : KDialog(parent), d(new KOpenWithDialogPrivate(this))
00517 {
00518   setObjectName( QLatin1String( "openwith" ) );
00519   setModal( true );
00520   setCaption(i18n("Choose Application for %1", mimeType));
00521   QString text = i18n("<qt>Select the program for the file type: <b>%1</b>. "
00522                       "If the program is not listed, enter the name or click "
00523                       "the browse button.</qt>", mimeType);
00524     d->qMimeType = mimeType;
00525     d->init(text, value);
00526     if (d->remember) {
00527         d->remember->hide();
00528     }
00529 }
00530 
00531 KOpenWithDialog::KOpenWithDialog( QWidget *parent)
00532     : KDialog(parent), d(new KOpenWithDialogPrivate(this))
00533 {
00534   setObjectName( QLatin1String( "openwith" ) );
00535   setModal( true );
00536   setCaption(i18n("Choose Application"));
00537   QString text = i18n("<qt>Select a program. "
00538                       "If the program is not listed, enter the name or click "
00539                       "the browse button.</qt>");
00540     d->qMimeType.clear();
00541     d->init(text, QString());
00542 }
00543 
00544 void KOpenWithDialogPrivate::setMimeType(const KUrl::List &_urls)
00545 {
00546   if ( _urls.count() == 1 )
00547   {
00548     qMimeType = KMimeType::findByUrl( _urls.first())->name();
00549     if (qMimeType == QLatin1String("application/octet-stream"))
00550       qMimeType.clear();
00551   }
00552   else
00553       qMimeType.clear();
00554 }
00555 
00556 void KOpenWithDialogPrivate::init(const QString &_text, const QString &_value)
00557 {
00558   bool bReadOnly = !KAuthorized::authorize("shell_access");
00559   m_terminaldirty = false;
00560     view = 0;
00561     m_pService = 0;
00562     curService = 0;
00563 
00564     q->setButtons(KDialog::Ok | KDialog::Cancel);
00565 
00566     QWidget *mainWidget = q->mainWidget();
00567 
00568   QBoxLayout *topLayout = new QVBoxLayout( mainWidget );
00569   topLayout->setMargin(0);
00570     label = new QLabel(_text, q);
00571   label->setWordWrap(true);
00572   label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
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     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 
00623     view = new KApplicationView(mainWidget);
00624     view->setModel(new KApplicationModel(view));
00625     topLayout->addWidget(view);
00626     topLayout->setStretchFactor(view, 1);
00627 
00628     QObject::connect(view, SIGNAL(selected(QString, QString)),
00629                      q, SLOT(slotSelected(QString, QString)));
00630     QObject::connect(view, SIGNAL(highlighted(QString, QString)),
00631                      q, SLOT(slotHighlighted(QString, QString)));
00632     QObject::connect(view, SIGNAL(doubleClicked(QModelIndex)),
00633                      q, SLOT(_k_slotDbClick()));
00634 
00635   terminal = new QCheckBox( i18n("Run in &terminal"), mainWidget );
00636   if (bReadOnly)
00637      terminal->hide();
00638     QObject::connect(terminal, SIGNAL(toggled(bool)), q, SLOT(slotTerminalToggled(bool)));
00639 
00640   topLayout->addWidget(terminal);
00641 
00642   QStyleOptionButton checkBoxOption;
00643   checkBoxOption.initFrom(terminal);
00644   int checkBoxIndentation = terminal->style()->pixelMetric( QStyle::PM_IndicatorWidth, &checkBoxOption, terminal );
00645   checkBoxIndentation += terminal->style()->pixelMetric( QStyle::PM_CheckBoxLabelSpacing, &checkBoxOption, terminal );
00646 
00647   QBoxLayout* nocloseonexitLayout = new QHBoxLayout();
00648   nocloseonexitLayout->setMargin( 0 );
00649   QSpacerItem* spacer = new QSpacerItem( checkBoxIndentation, 0, QSizePolicy::Fixed, QSizePolicy::Minimum );
00650   nocloseonexitLayout->addItem( spacer );
00651 
00652   nocloseonexit = new QCheckBox( i18n("&Do not close when command exits"), mainWidget );
00653   nocloseonexit->setChecked( false );
00654   nocloseonexit->setDisabled( true );
00655 
00656   // check to see if we use konsole if not disable the nocloseonexit
00657   // because we don't know how to do this on other terminal applications
00658   KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
00659   QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole"));
00660 
00661   if (bReadOnly || preferredTerminal != "konsole")
00662      nocloseonexit->hide();
00663 
00664   nocloseonexitLayout->addWidget( nocloseonexit );
00665   topLayout->addLayout( nocloseonexitLayout );
00666 
00667   if (!qMimeType.isNull())
00668   {
00669     remember = new QCheckBox(i18n("&Remember application association for this type of file"), mainWidget);
00670     //    remember->setChecked(true);
00671     topLayout->addWidget(remember);
00672   }
00673   else
00674     remember = 0L;
00675 
00676   //edit->setText( _value );
00677   // This is what caused "can't click on items before clicking on Name header".
00678   // Probably due to the resizeEvent handler using width().
00679   //resize( minimumWidth(), sizeHint().height() );
00680   edit->setFocus();
00681     q->slotTextChanged();
00682 }
00683 
00684 
00685 // ----------------------------------------------------------------------
00686 
00687 KOpenWithDialog::~KOpenWithDialog()
00688 {
00689     delete d;
00690 }
00691 
00692 
00693 // ----------------------------------------------------------------------
00694 
00695 void KOpenWithDialog::slotSelected( const QString& /*_name*/, const QString& _exec )
00696 {
00697     KService::Ptr pService = d->curService;
00698     d->edit->setText(_exec); // calls slotTextChanged :(
00699     d->curService = pService;
00700 }
00701 
00702 
00703 // ----------------------------------------------------------------------
00704 
00705 void KOpenWithDialog::slotHighlighted(const QString& entryPath, const QString&)
00706 {
00707     d->curService = KService::serviceByDesktopPath(entryPath);
00708     if (!d->m_terminaldirty)
00709     {
00710         // ### indicate that default value was restored
00711         d->terminal->setChecked(d->curService->terminal());
00712         QString terminalOptions = d->curService->terminalOptions();
00713         d->nocloseonexit->setChecked((terminalOptions.contains(QLatin1String("--noclose")) > 0));
00714         d->m_terminaldirty = false; // slotTerminalToggled changed it
00715     }
00716 }
00717 
00718 // ----------------------------------------------------------------------
00719 
00720 void KOpenWithDialog::slotTextChanged()
00721 {
00722     // Forget about the service
00723     d->curService = 0L;
00724     enableButton(Ok, !d->edit->text().isEmpty());
00725 }
00726 
00727 // ----------------------------------------------------------------------
00728 
00729 void KOpenWithDialog::slotTerminalToggled(bool)
00730 {
00731     // ### indicate that default value was overridden
00732     d->m_terminaldirty = true;
00733     d->nocloseonexit->setDisabled(!d->terminal->isChecked());
00734 }
00735 
00736 // ----------------------------------------------------------------------
00737 
00738 void KOpenWithDialogPrivate::_k_slotDbClick()
00739 {
00740     // check if a directory is selected
00741     if (view->isDirSel()) {
00742         return;
00743     }
00744     q->accept();
00745 }
00746 
00747 void KOpenWithDialog::setSaveNewApplications(bool b)
00748 {
00749   d->saveNewApps = b;
00750 }
00751 
00752 static QString simplifiedExecLineFromService(const QString& fullExec)
00753 {
00754     QString exec = fullExec;
00755     exec.remove("%u", Qt::CaseInsensitive);
00756     exec.remove("%f", Qt::CaseInsensitive);
00757     exec.remove("-caption %c");
00758     exec.remove("-caption \"%c\"");
00759     exec.remove("%i");
00760     exec.remove("%m");
00761     return exec.simplified();
00762 }
00763 
00764 void KOpenWithDialogPrivate::addToMimeAppsList(const QString& serviceId /*menu id or storage id*/)
00765 {
00766     KSharedConfig::Ptr profile = KSharedConfig::openConfig("mimeapps.list", KConfig::NoGlobals, "xdgdata-apps");
00767     KConfigGroup addedApps(profile, "Added Associations");
00768     QStringList apps = addedApps.readXdgListEntry(qMimeType);
00769     apps.removeAll(serviceId);
00770     apps.prepend(serviceId); // make it the preferred app
00771     addedApps.writeXdgListEntry(qMimeType, apps);
00772     addedApps.sync();
00773 
00774     // Also make sure the "auto embed" setting for this mimetype is off
00775     KSharedConfig::Ptr fileTypesConfig = KSharedConfig::openConfig("filetypesrc", KConfig::NoGlobals);
00776     fileTypesConfig->group("EmbedSettings").writeEntry(QString("embed-")+qMimeType, false);
00777     fileTypesConfig->sync();
00778 
00779     kDebug(250) << "rebuilding ksycoca...";
00780 
00781     // kbuildsycoca is the one reading mimeapps.list, so we need to run it now
00782     KBuildSycocaProgressDialog::rebuildKSycoca(q);
00783 
00784     m_pService = KService::serviceByStorageId(serviceId);
00785     Q_ASSERT( m_pService );
00786 }
00787 
00788 bool KOpenWithDialogPrivate::checkAccept()
00789 {
00790     const QString typedExec(edit->text());
00791     if (typedExec.isEmpty())
00792         return false;
00793     QString fullExec(typedExec);
00794 
00795     QString serviceName;
00796     QString initialServiceName;
00797     QString preferredTerminal;
00798     QString binaryName;
00799     m_pService = curService;
00800     if (!m_pService) {
00801         // No service selected - check the command line
00802 
00803         // Find out the name of the service from the command line, removing args and paths
00804         serviceName = KRun::binaryName( typedExec, true );
00805         if (serviceName.isEmpty()) {
00806             KMessageBox::error(q, i18n("Could not extract executable name from '%1', please type a valid program name.", serviceName));
00807             return false;
00808         }
00809         initialServiceName = serviceName;
00810         // Also remember the binaryName with a path, if any, for the
00811         // check that the binary exists.
00812         binaryName = KRun::binaryName(typedExec, false);
00813         kDebug(250) << "initialServiceName=" << initialServiceName << "binaryName=" << binaryName;
00814         int i = 1; // We have app, app-2, app-3... Looks better for the user.
00815         bool ok = false;
00816         // Check if there's already a service by that name, with the same Exec line
00817         do {
00818             kDebug(250) << "looking for service" << serviceName;
00819             KService::Ptr serv = KService::serviceByDesktopName( serviceName );
00820             ok = !serv; // ok if no such service yet
00821             // also ok if we find the exact same service (well, "kwrite" == "kwrite %U")
00822             if (serv) {
00823                 if (serv->isApplication()) {
00824                     /*kDebug(250) << "typedExec=" << typedExec
00825                       << "serv->exec=" << serv->exec()
00826                       << "simplifiedExecLineFromService=" << simplifiedExecLineFromService(fullExec);*/
00827                     if (typedExec == simplifiedExecLineFromService(serv->exec())) {
00828                         ok = true;
00829                         m_pService = serv;
00830                         kDebug(250) << "OK, found identical service: " << serv->entryPath();
00831                     } else {
00832                         kDebug(250) << "Exec line differs, service says:" << simplifiedExecLineFromService(fullExec);
00833                     }
00834                 } else {
00835                     kDebug(250) << "Found, but not an application:" << serv->entryPath();
00836                 }
00837             }
00838             if (!ok) { // service was found, but it was different -> keep looking
00839                 ++i;
00840                 serviceName = initialServiceName + '-' + QString::number(i);
00841             }
00842         } while (!ok);
00843     }
00844     if ( m_pService ) {
00845         // Existing service selected
00846         serviceName = m_pService->name();
00847         initialServiceName = serviceName;
00848         fullExec = m_pService->exec();
00849     } else {
00850         // Ensure that the typed binary name actually exists (#81190)
00851         if (KStandardDirs::findExe(binaryName).isEmpty()) {
00852             KMessageBox::error(q, i18n("'%1' not found, please type a valid program name.", binaryName));
00853             return false;
00854         }
00855     }
00856 
00857     if (terminal->isChecked()) {
00858         KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
00859         preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole"));
00860         m_command = preferredTerminal;
00861         // only add --noclose when we are sure it is konsole we're using
00862         if (preferredTerminal == "konsole" && nocloseonexit->isChecked())
00863             m_command += QString::fromLatin1(" --noclose");
00864         m_command += QString::fromLatin1(" -e ");
00865         m_command += edit->text();
00866         kDebug(250) << "Setting m_command to" << m_command;
00867     }
00868     if ( m_pService && terminal->isChecked() != m_pService->terminal() )
00869         m_pService = 0; // It's not exactly this service we're running
00870 
00871 
00872     const bool bRemember = remember && remember->isChecked();
00873     kDebug(250) << "bRemember=" << bRemember << "service found=" << m_pService;
00874     if (m_pService) {
00875         if (bRemember) {
00876             // Associate this app with qMimeType in mimeapps.list
00877             Q_ASSERT(!qMimeType.isEmpty()); // we don't show the remember checkbox otherwise
00878             addToMimeAppsList(m_pService->storageId());
00879         }
00880     } else {
00881         const bool createDesktopFile = bRemember || saveNewApps;
00882         if (!createDesktopFile) {
00883             // Create temp service
00884             m_pService = new KService(initialServiceName, fullExec, QString());
00885             if (terminal->isChecked()) {
00886                 m_pService->setTerminal(true);
00887                 // only add --noclose when we are sure it is konsole we're using
00888                 if (preferredTerminal == "konsole" && nocloseonexit->isChecked())
00889                     m_pService->setTerminalOptions("--noclose");
00890             }
00891         } else {
00892             // If we got here, we can't seem to find a service for what they wanted. Create one.
00893 
00894             QString menuId;
00895             QString newPath = KService::newServicePath(false /* ignored argument */, serviceName, &menuId);
00896             kDebug(250) << "Creating new service" << serviceName << "(" << newPath << ")" << "menuId=" << menuId;
00897 
00898             KDesktopFile desktopFile(newPath);
00899             KConfigGroup cg = desktopFile.desktopGroup();
00900             cg.writeEntry("Type", "Application");
00901             cg.writeEntry("Name", initialServiceName);
00902             cg.writeEntry("Exec", fullExec);
00903             cg.writeEntry("NoDisplay", true); // don't make it appear in the K menu
00904             if (terminal->isChecked()) {
00905                 cg.writeEntry("Terminal", true);
00906                 // only add --noclose when we are sure it is konsole we're using
00907                 if (preferredTerminal == "konsole" && nocloseonexit->isChecked())
00908                     cg.writeEntry("TerminalOptions", "--noclose");
00909             }
00910             cg.writeXdgListEntry("MimeType", QStringList() << qMimeType);
00911             cg.sync();
00912 
00913             addToMimeAppsList(menuId);
00914         }
00915     }
00916 
00917     saveComboboxHistory();
00918     return true;
00919 }
00920 
00921 void KOpenWithDialog::accept()
00922 {
00923     if (d->checkAccept())
00924         KDialog::accept();
00925 }
00926 
00927 QString KOpenWithDialog::text() const
00928 {
00929     if (!d->m_command.isEmpty())
00930         return d->m_command;
00931     else
00932         return d->edit->text();
00933 }
00934 
00935 void KOpenWithDialog::hideNoCloseOnExit()
00936 {
00937     // uncheck the checkbox because the value could be used when "Run in Terminal" is selected
00938     d->nocloseonexit->setChecked(false);
00939     d->nocloseonexit->hide();
00940 }
00941 
00942 void KOpenWithDialog::hideRunInTerminal()
00943 {
00944     d->terminal->hide();
00945     hideNoCloseOnExit();
00946 }
00947 
00948 KService::Ptr KOpenWithDialog::service() const
00949 {
00950     return d->m_pService;
00951 }
00952 
00953 void KOpenWithDialogPrivate::saveComboboxHistory()
00954 {
00955     KHistoryComboBox *combo = static_cast<KHistoryComboBox*>(edit->comboBox());
00956     if (combo) {
00957         combo->addToHistory(edit->text());
00958 
00959         KConfigGroup cg( KGlobal::config(), QString::fromLatin1("Open-with settings") );
00960         cg.writeEntry( "History", combo->historyItems() );
00961         writeEntry( cg, "CompletionMode", combo->completionMode() );
00962         // don't store the completion-list, as it contains all of KUrlCompletion's
00963         // executables
00964         cg.sync();
00965     }
00966 }
00967 
00968 #include "kopenwithdialog.moc"
00969 #include "kopenwithdialog_p.moc"

KIO

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.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