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

KDE's Doxygen guidelines are available online.

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-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal