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

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.5
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