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

KIO

  • sources
  • kde-4.14
  • kdelibs
  • kio
  • kfile
kopenwithdialog.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2 
3  Copyright (C) 1997 Torben Weis <weis@stud.uni-frankfurt.de>
4  Copyright (C) 1999 Dirk Mueller <mueller@kde.org>
5  Portions copyright (C) 1999 Preston Brown <pbrown@kde.org>
6  Copyright (C) 2007 Pino Toscano <pino@kde.org>
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Library General Public
10  License as published by the Free Software Foundation; either
11  version 2 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Library General Public License for more details.
17 
18  You should have received a copy of the GNU Library General Public License
19  along with this library; see the file COPYING.LIB. If not, write to
20  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  Boston, MA 02110-1301, USA.
22 */
23 
24 #include "kopenwithdialog.h"
25 #include "kopenwithdialog_p.h"
26 
27 #include <QtCore/QtAlgorithms>
28 #include <QtCore/QList>
29 #include <QtGui/QLabel>
30 #include <QtGui/QLayout>
31 #include <QtGui/QCheckBox>
32 #include <QtGui/QStyle>
33 #include <QtGui/QStyleOptionButton>
34 
35 #include <kauthorized.h>
36 #include <khistorycombobox.h>
37 #include <kdesktopfile.h>
38 #include <klineedit.h>
39 #include <klocale.h>
40 #include <kmessagebox.h>
41 #include <kshell.h>
42 #include <krun.h>
43 #include <kstandarddirs.h>
44 #include <kstringhandler.h>
45 #include <kurlcompletion.h>
46 #include <kurlrequester.h>
47 #include <kmimetype.h>
48 #include <kservicegroup.h>
49 #include <kserviceoffer.h>
50 #include <kdebug.h>
51 
52 #include <assert.h>
53 #include <stdlib.h>
54 #include <kbuildsycocaprogressdialog.h>
55 #include <kconfiggroup.h>
56 
57 inline void writeEntry( KConfigGroup& group, const char* key,
58  const KGlobalSettings::Completion& aValue,
59  KConfigBase::WriteConfigFlags flags = KConfigBase::Normal )
60 {
61  group.writeEntry(key, int(aValue), flags);
62 }
63 
64 namespace KDEPrivate {
65 
66 class AppNode
67 {
68 public:
69  AppNode()
70  : isDir(false), parent(0), fetched(false)
71  {
72  }
73  ~AppNode()
74  {
75  qDeleteAll(children);
76  }
77 
78  QString icon;
79  QString text;
80  QString entryPath;
81  QString exec;
82  bool isDir;
83 
84  AppNode *parent;
85  bool fetched;
86 
87  QList<AppNode*> children;
88 };
89 
90 bool AppNodeLessThan(KDEPrivate::AppNode *n1, KDEPrivate::AppNode *n2)
91 {
92  if (n1->isDir) {
93  if (n2->isDir) {
94  return n1->text.compare(n2->text, Qt::CaseInsensitive) < 0;
95  } else {
96  return true;
97  }
98  } else {
99  if (n2->isDir) {
100  return false;
101  } else {
102  return n1->text.compare(n2->text, Qt::CaseInsensitive) < 0;
103  }
104  }
105  return true;
106 }
107 
108 }
109 
110 
111 class KApplicationModelPrivate
112 {
113 public:
114  KApplicationModelPrivate(KApplicationModel *qq)
115  : q(qq), root(new KDEPrivate::AppNode())
116  {
117  }
118  ~KApplicationModelPrivate()
119  {
120  delete root;
121  }
122 
123  void fillNode(const QString &entryPath, KDEPrivate::AppNode *node);
124 
125  KApplicationModel *q;
126 
127  KDEPrivate::AppNode *root;
128 };
129 
130 void KApplicationModelPrivate::fillNode(const QString &_entryPath, KDEPrivate::AppNode *node)
131 {
132  KServiceGroup::Ptr root = KServiceGroup::group(_entryPath);
133  if (!root || !root->isValid()) return;
134 
135  const KServiceGroup::List list = root->entries();
136 
137  for( KServiceGroup::List::ConstIterator it = list.begin();
138  it != list.end(); ++it)
139  {
140  QString icon;
141  QString text;
142  QString entryPath;
143  QString exec;
144  bool isDir = false;
145  const KSycocaEntry::Ptr p = (*it);
146  if (p->isType(KST_KService))
147  {
148  const KService::Ptr service = KService::Ptr::staticCast(p);
149 
150  if (service->noDisplay())
151  continue;
152 
153  icon = service->icon();
154  text = service->name();
155  exec = service->exec();
156  entryPath = service->entryPath();
157  }
158  else if (p->isType(KST_KServiceGroup))
159  {
160  const KServiceGroup::Ptr serviceGroup = KServiceGroup::Ptr::staticCast(p);
161 
162  if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0)
163  continue;
164 
165  icon = serviceGroup->icon();
166  text = serviceGroup->caption();
167  entryPath = serviceGroup->entryPath();
168  isDir = true;
169  }
170  else
171  {
172  kWarning(250) << "KServiceGroup: Unexpected object in list!";
173  continue;
174  }
175 
176  KDEPrivate::AppNode *newnode = new KDEPrivate::AppNode();
177  newnode->icon = icon;
178  newnode->text = text;
179  newnode->entryPath = entryPath;
180  newnode->exec = exec;
181  newnode->isDir = isDir;
182  newnode->parent = node;
183  node->children.append(newnode);
184  }
185  qStableSort(node->children.begin(), node->children.end(), KDEPrivate::AppNodeLessThan);
186 }
187 
188 
189 
190 KApplicationModel::KApplicationModel(QObject *parent)
191  : QAbstractItemModel(parent), d(new KApplicationModelPrivate(this))
192 {
193  d->fillNode(QString(), d->root);
194 }
195 
196 KApplicationModel::~KApplicationModel()
197 {
198  delete d;
199 }
200 
201 bool KApplicationModel::canFetchMore(const QModelIndex &parent) const
202 {
203  if (!parent.isValid())
204  return false;
205 
206  KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
207  return node->isDir && !node->fetched;
208 }
209 
210 int KApplicationModel::columnCount(const QModelIndex &parent) const
211 {
212  Q_UNUSED(parent)
213  return 1;
214 }
215 
216 QVariant KApplicationModel::data(const QModelIndex &index, int role) const
217 {
218  if (!index.isValid())
219  return QVariant();
220 
221  KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer());
222 
223  switch (role) {
224  case Qt::DisplayRole:
225  return node->text;
226  break;
227  case Qt::DecorationRole:
228  if (!node->icon.isEmpty()) {
229  return KIcon(node->icon);
230  }
231  break;
232  default:
233  ;
234  }
235  return QVariant();
236 }
237 
238 void KApplicationModel::fetchMore(const QModelIndex &parent)
239 {
240  if (!parent.isValid())
241  return;
242 
243  KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
244  if (!node->isDir)
245  return;
246 
247  emit layoutAboutToBeChanged();
248  d->fillNode(node->entryPath, node);
249  node->fetched = true;
250  emit layoutChanged();
251 }
252 
253 bool KApplicationModel::hasChildren(const QModelIndex &parent) const
254 {
255  if (!parent.isValid())
256  return true;
257 
258  KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
259  return node->isDir;
260 }
261 
262 QVariant KApplicationModel::headerData(int section, Qt::Orientation orientation, int role) const
263 {
264  if (orientation != Qt::Horizontal || section != 0)
265  return QVariant();
266 
267  switch (role) {
268  case Qt::DisplayRole:
269  return i18n("Known Applications");
270  break;
271  default:
272  return QVariant();
273  }
274 }
275 
276 QModelIndex KApplicationModel::index(int row, int column, const QModelIndex &parent) const
277 {
278  if (row < 0 || column != 0)
279  return QModelIndex();
280 
281  KDEPrivate::AppNode *node = d->root;
282  if (parent.isValid())
283  node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
284 
285  if (row >= node->children.count())
286  return QModelIndex();
287  else
288  return createIndex(row, 0, node->children.at(row));
289 }
290 
291 QModelIndex KApplicationModel::parent(const QModelIndex &index) const
292 {
293  if (!index.isValid())
294  return QModelIndex();
295 
296  KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer());
297  if (node->parent->parent) {
298  int id = node->parent->parent->children.indexOf(node->parent);
299 
300  if (id >= 0 && id < node->parent->parent->children.count())
301  return createIndex(id, 0, node->parent);
302  else
303  return QModelIndex();
304  }
305  else
306  return QModelIndex();
307 }
308 
309 int KApplicationModel::rowCount(const QModelIndex &parent) const
310 {
311  if (!parent.isValid())
312  return d->root->children.count();
313 
314  KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
315  return node->children.count();
316 }
317 
318 QString KApplicationModel::entryPathFor(const QModelIndex &index) const
319 {
320  if (!index.isValid())
321  return QString();
322 
323  KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer());
324  return node->entryPath;
325 }
326 
327 QString KApplicationModel::execFor(const QModelIndex &index) const
328 {
329  if (!index.isValid())
330  return QString();
331 
332  KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer());
333  return node->exec;
334 }
335 
336 bool KApplicationModel::isDirectory(const QModelIndex &index) const
337 {
338  if (!index.isValid())
339  return false;
340 
341  KDEPrivate::AppNode *node = static_cast<KDEPrivate::AppNode*>(index.internalPointer());
342  return node->isDir;
343 }
344 
345 class KApplicationViewPrivate
346 {
347 public:
348  KApplicationViewPrivate()
349  : appModel(0)
350  {
351  }
352 
353  KApplicationModel *appModel;
354 };
355 
356 KApplicationView::KApplicationView(QWidget *parent)
357  : QTreeView(parent), d(new KApplicationViewPrivate)
358 {
359 }
360 
361 KApplicationView::~KApplicationView()
362 {
363  delete d;
364 }
365 
366 void KApplicationView::setModel(QAbstractItemModel *model)
367 {
368  if (d->appModel) {
369  disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
370  this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection)));
371  }
372 
373  QTreeView::setModel(model);
374 
375  d->appModel = qobject_cast<KApplicationModel*>(model);
376  if (d->appModel) {
377  connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
378  this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection)));
379  }
380 }
381 
382 bool KApplicationView::isDirSel() const
383 {
384  if (d->appModel) {
385  QModelIndex index = selectionModel()->currentIndex();
386  return d->appModel->isDirectory(index);
387  }
388  return false;
389 }
390 
391 void KApplicationView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
392 {
393  QTreeView::currentChanged(current, previous);
394 
395  if (d->appModel && !d->appModel->isDirectory(current)) {
396  QString exec = d->appModel->execFor(current);
397  if (!exec.isEmpty()) {
398  emit highlighted(d->appModel->entryPathFor(current), exec);
399  }
400  }
401 }
402 
403 void KApplicationView::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
404 {
405  Q_UNUSED(deselected)
406 
407  const QModelIndexList indexes = selected.indexes();
408  if (indexes.count() == 1 && !d->appModel->isDirectory(indexes.at(0))) {
409  QString exec = d->appModel->execFor(indexes.at(0));
410  if (!exec.isEmpty()) {
411  emit this->selected(d->appModel->entryPathFor(indexes.at(0)), exec);
412  }
413  }
414 }
415 
416 
417 
418 /***************************************************************
419  *
420  * KOpenWithDialog
421  *
422  ***************************************************************/
423 class KOpenWithDialogPrivate
424 {
425 public:
426  KOpenWithDialogPrivate(KOpenWithDialog *qq)
427  : q(qq), saveNewApps(false)
428  {
429  }
430 
431  KOpenWithDialog *q;
432 
436  void setMimeType(const KUrl::List &_urls);
437 
438  void addToMimeAppsList(const QString& serviceId);
439 
447  void init(const QString &text, const QString &value);
448 
452  void saveComboboxHistory();
453 
458  bool checkAccept();
459 
460  // slots
461  void _k_slotDbClick();
462  void _k_slotFileSelected();
463 
464  bool saveNewApps;
465  bool m_terminaldirty;
466  KService::Ptr curService;
467  KApplicationView *view;
468  KUrlRequester *edit;
469  QString m_command;
470  QLabel *label;
471  QString qMimeType;
472  QCheckBox *terminal;
473  QCheckBox *remember;
474  QCheckBox *nocloseonexit;
475  KService::Ptr m_pService;
476 };
477 
478 KOpenWithDialog::KOpenWithDialog( const KUrl::List& _urls, QWidget* parent )
479  : KDialog(parent), d(new KOpenWithDialogPrivate(this))
480 {
481  setObjectName( QLatin1String( "openwith" ) );
482  setModal( true );
483  setCaption( i18n( "Open With" ) );
484 
485  QString text;
486  if( _urls.count() == 1 )
487  {
488  text = i18n("<qt>Select the program that should be used to open <b>%1</b>. "
489  "If the program is not listed, enter the name or click "
490  "the browse button.</qt>", _urls.first().fileName() );
491  }
492  else
493  // Should never happen ??
494  text = i18n( "Choose the name of the program with which to open the selected files." );
495  d->setMimeType(_urls);
496  d->init(text, QString());
497 }
498 
499 KOpenWithDialog::KOpenWithDialog( const KUrl::List& _urls, const QString&_text,
500  const QString& _value, QWidget *parent)
501  : KDialog(parent), d(new KOpenWithDialogPrivate(this))
502 {
503  setObjectName( QLatin1String( "openwith" ) );
504  setModal( true );
505  QString caption;
506  if (_urls.count()>0 && !_urls.first().isEmpty())
507  caption = KStringHandler::csqueeze( _urls.first().prettyUrl() );
508  if (_urls.count() > 1)
509  caption += QString::fromLatin1("...");
510  setCaption(caption);
511  d->setMimeType(_urls);
512  d->init(_text, _value);
513 }
514 
515 KOpenWithDialog::KOpenWithDialog( const QString &mimeType, const QString& value,
516  QWidget *parent)
517  : KDialog(parent), d(new KOpenWithDialogPrivate(this))
518 {
519  setObjectName( QLatin1String( "openwith" ) );
520  setModal( true );
521  setCaption(i18n("Choose Application for %1", mimeType));
522  QString text = i18n("<qt>Select the program for the file type: <b>%1</b>. "
523  "If the program is not listed, enter the name or click "
524  "the browse button.</qt>", mimeType);
525  d->qMimeType = mimeType;
526  d->init(text, value);
527  if (d->remember) {
528  d->remember->hide();
529  }
530 }
531 
532 KOpenWithDialog::KOpenWithDialog( QWidget *parent)
533  : KDialog(parent), d(new KOpenWithDialogPrivate(this))
534 {
535  setObjectName( QLatin1String( "openwith" ) );
536  setModal( true );
537  setCaption(i18n("Choose Application"));
538  QString text = i18n("<qt>Select a program. "
539  "If the program is not listed, enter the name or click "
540  "the browse button.</qt>");
541  d->qMimeType.clear();
542  d->init(text, QString());
543 }
544 
545 void KOpenWithDialogPrivate::setMimeType(const KUrl::List &_urls)
546 {
547  if ( _urls.count() == 1 )
548  {
549  qMimeType = KMimeType::findByUrl( _urls.first())->name();
550  if (qMimeType == QLatin1String("application/octet-stream"))
551  qMimeType.clear();
552  }
553  else
554  qMimeType.clear();
555 }
556 
557 void KOpenWithDialogPrivate::init(const QString &_text, const QString &_value)
558 {
559  bool bReadOnly = !KAuthorized::authorize("shell_access");
560  m_terminaldirty = false;
561  view = 0;
562  m_pService = 0;
563  curService = 0;
564 
565  q->setButtons(KDialog::Ok | KDialog::Cancel);
566 
567  QWidget *mainWidget = q->mainWidget();
568 
569  QBoxLayout *topLayout = new QVBoxLayout( mainWidget );
570  topLayout->setMargin(0);
571  label = new QLabel(_text, q);
572  label->setWordWrap(true);
573  topLayout->addWidget(label);
574 
575  if (!bReadOnly)
576  {
577  // init the history combo and insert it into the URL-Requester
578  KHistoryComboBox *combo = new KHistoryComboBox();
579  KLineEdit *lineEdit = new KLineEdit(q);
580  lineEdit->setClearButtonShown(true);
581  combo->setLineEdit(lineEdit);
582  combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
583  combo->setDuplicatesEnabled( false );
584  KConfigGroup cg( KGlobal::config(), QString::fromLatin1("Open-with settings") );
585  int max = cg.readEntry( "Maximum history", 15 );
586  combo->setMaxCount( max );
587  int mode = cg.readEntry( "CompletionMode", int(KGlobalSettings::completionMode()));
588  combo->setCompletionMode((KGlobalSettings::Completion)mode);
589  const QStringList list = cg.readEntry( "History", QStringList() );
590  combo->setHistoryItems( list, true );
591  edit = new KUrlRequester( combo, mainWidget );
592  }
593  else
594  {
595  edit = new KUrlRequester( mainWidget );
596  edit->lineEdit()->setReadOnly(true);
597  edit->button()->hide();
598  }
599 
600  edit->setText( _value );
601  edit->setWhatsThis(i18n(
602  "Following the command, you can have several place holders which will be replaced "
603  "with the actual values when the actual program is run:\n"
604  "%f - a single file name\n"
605  "%F - a list of files; use for applications that can open several local files at once\n"
606  "%u - a single URL\n"
607  "%U - a list of URLs\n"
608  "%d - the directory of the file to open\n"
609  "%D - a list of directories\n"
610  "%i - the icon\n"
611  "%m - the mini-icon\n"
612  "%c - the comment"));
613 
614  topLayout->addWidget(edit);
615 
616  if ( edit->comboBox() ) {
617  KUrlCompletion *comp = new KUrlCompletion( KUrlCompletion::ExeCompletion );
618  edit->comboBox()->setCompletionObject( comp );
619  edit->comboBox()->setAutoDeleteCompletionObject( true );
620  }
621 
622  QObject::connect(edit, SIGNAL(textChanged(QString)), q, SLOT(slotTextChanged()));
623  QObject::connect(edit, SIGNAL(urlSelected(KUrl)), q, SLOT(_k_slotFileSelected()));
624 
625  view = new KApplicationView(mainWidget);
626  view->setModel(new KApplicationModel(view));
627  topLayout->addWidget(view);
628  topLayout->setStretchFactor(view, 1);
629 
630  QObject::connect(view, SIGNAL(selected(QString,QString)),
631  q, SLOT(slotSelected(QString,QString)));
632  QObject::connect(view, SIGNAL(highlighted(QString,QString)),
633  q, SLOT(slotHighlighted(QString,QString)));
634  QObject::connect(view, SIGNAL(doubleClicked(QModelIndex)),
635  q, SLOT(_k_slotDbClick()));
636 
637  terminal = new QCheckBox( i18n("Run in &terminal"), mainWidget );
638  if (bReadOnly)
639  terminal->hide();
640  QObject::connect(terminal, SIGNAL(toggled(bool)), q, SLOT(slotTerminalToggled(bool)));
641 
642  topLayout->addWidget(terminal);
643 
644  QStyleOptionButton checkBoxOption;
645  checkBoxOption.initFrom(terminal);
646  int checkBoxIndentation = terminal->style()->pixelMetric( QStyle::PM_IndicatorWidth, &checkBoxOption, terminal );
647  checkBoxIndentation += terminal->style()->pixelMetric( QStyle::PM_CheckBoxLabelSpacing, &checkBoxOption, terminal );
648 
649  QBoxLayout* nocloseonexitLayout = new QHBoxLayout();
650  nocloseonexitLayout->setMargin( 0 );
651  QSpacerItem* spacer = new QSpacerItem( checkBoxIndentation, 0, QSizePolicy::Fixed, QSizePolicy::Minimum );
652  nocloseonexitLayout->addItem( spacer );
653 
654  nocloseonexit = new QCheckBox( i18n("&Do not close when command exits"), mainWidget );
655  nocloseonexit->setChecked( false );
656  nocloseonexit->setDisabled( true );
657 
658  // check to see if we use konsole if not disable the nocloseonexit
659  // because we don't know how to do this on other terminal applications
660  KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
661  QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole"));
662 
663  if (bReadOnly || preferredTerminal != "konsole")
664  nocloseonexit->hide();
665 
666  nocloseonexitLayout->addWidget( nocloseonexit );
667  topLayout->addLayout( nocloseonexitLayout );
668 
669  if (!qMimeType.isNull())
670  {
671  remember = new QCheckBox(i18n("&Remember application association for this type of file"), mainWidget);
672  // remember->setChecked(true);
673  topLayout->addWidget(remember);
674  }
675  else
676  remember = 0L;
677 
678  q->setMinimumSize(q->minimumSizeHint());
679  //edit->setText( _value );
680  // This is what caused "can't click on items before clicking on Name header".
681  // Probably due to the resizeEvent handler using width().
682  //resize( minimumWidth(), sizeHint().height() );
683  edit->setFocus();
684  q->slotTextChanged();
685 }
686 
687 
688 // ----------------------------------------------------------------------
689 
690 KOpenWithDialog::~KOpenWithDialog()
691 {
692  delete d;
693 }
694 
695 
696 // ----------------------------------------------------------------------
697 
698 void KOpenWithDialog::slotSelected( const QString& /*_name*/, const QString& _exec )
699 {
700  KService::Ptr pService = d->curService;
701  d->edit->setText(_exec); // calls slotTextChanged :(
702  d->curService = pService;
703 }
704 
705 
706 // ----------------------------------------------------------------------
707 
708 void KOpenWithDialog::slotHighlighted(const QString& entryPath, const QString&)
709 {
710  d->curService = KService::serviceByDesktopPath(entryPath);
711  if (d->curService && !d->m_terminaldirty)
712  {
713  // ### indicate that default value was restored
714  d->terminal->setChecked(d->curService->terminal());
715  QString terminalOptions = d->curService->terminalOptions();
716  d->nocloseonexit->setChecked((terminalOptions.contains(QLatin1String("--noclose")) > 0));
717  d->m_terminaldirty = false; // slotTerminalToggled changed it
718  }
719 }
720 
721 // ----------------------------------------------------------------------
722 
723 void KOpenWithDialog::slotTextChanged()
724 {
725  // Forget about the service
726  d->curService = 0L;
727  enableButton(Ok, !d->edit->text().isEmpty());
728 }
729 
730 // ----------------------------------------------------------------------
731 
732 void KOpenWithDialog::slotTerminalToggled(bool)
733 {
734  // ### indicate that default value was overridden
735  d->m_terminaldirty = true;
736  d->nocloseonexit->setDisabled(!d->terminal->isChecked());
737 }
738 
739 // ----------------------------------------------------------------------
740 
741 void KOpenWithDialogPrivate::_k_slotDbClick()
742 {
743  // check if a directory is selected
744  if (view->isDirSel()) {
745  return;
746  }
747  q->accept();
748 }
749 
750 void KOpenWithDialogPrivate::_k_slotFileSelected()
751 {
752  // quote the path to avoid unescaped whitespace, backslashes, etc.
753  edit->setText(KShell::quoteArg(edit->text()));
754 }
755 
756 void KOpenWithDialog::setSaveNewApplications(bool b)
757 {
758  d->saveNewApps = b;
759 }
760 
761 static QString simplifiedExecLineFromService(const QString& fullExec)
762 {
763  QString exec = fullExec;
764  exec.remove("%u", Qt::CaseInsensitive);
765  exec.remove("%f", Qt::CaseInsensitive);
766  exec.remove("-caption %c");
767  exec.remove("-caption \"%c\"");
768  exec.remove("%i");
769  exec.remove("%m");
770  return exec.simplified();
771 }
772 
773 void KOpenWithDialogPrivate::addToMimeAppsList(const QString& serviceId /*menu id or storage id*/)
774 {
775  KSharedConfig::Ptr profile = KSharedConfig::openConfig("mimeapps.list", KConfig::NoGlobals, "xdgdata-apps");
776  KConfigGroup addedApps(profile, "Added Associations");
777  QStringList apps = addedApps.readXdgListEntry(qMimeType);
778  apps.removeAll(serviceId);
779  apps.prepend(serviceId); // make it the preferred app
780  addedApps.writeXdgListEntry(qMimeType, apps);
781  addedApps.sync();
782 
783  // Also make sure the "auto embed" setting for this mimetype is off
784  KSharedConfig::Ptr fileTypesConfig = KSharedConfig::openConfig("filetypesrc", KConfig::NoGlobals);
785  fileTypesConfig->group("EmbedSettings").writeEntry(QString("embed-")+qMimeType, false);
786  fileTypesConfig->sync();
787 
788  kDebug(250) << "rebuilding ksycoca...";
789 
790  // kbuildsycoca is the one reading mimeapps.list, so we need to run it now
791  KBuildSycocaProgressDialog::rebuildKSycoca(q);
792 
793  m_pService = KService::serviceByStorageId(serviceId);
794  Q_ASSERT( m_pService );
795 }
796 
797 bool KOpenWithDialogPrivate::checkAccept()
798 {
799  const QString typedExec(edit->text());
800  if (typedExec.isEmpty())
801  return false;
802  QString fullExec(typedExec);
803 
804  QString serviceName;
805  QString initialServiceName;
806  QString preferredTerminal;
807  QString configPath;
808  QString serviceExec;
809  m_pService = curService;
810  if (!m_pService) {
811  // No service selected - check the command line
812 
813  // Find out the name of the service from the command line, removing args and paths
814  serviceName = KRun::binaryName( typedExec, true );
815  if (serviceName.isEmpty()) {
816  KMessageBox::error(q, i18n("Could not extract executable name from '%1', please type a valid program name.", serviceName));
817  return false;
818  }
819  initialServiceName = serviceName;
820  // Also remember the binaryName with a path, if any, for the
821  // check that the binary exists.
822  kDebug(250) << "initialServiceName=" << initialServiceName;
823  int i = 1; // We have app, app-2, app-3... Looks better for the user.
824  bool ok = false;
825  // Check if there's already a service by that name, with the same Exec line
826  do {
827  kDebug(250) << "looking for service" << serviceName;
828  KService::Ptr serv = KService::serviceByDesktopName( serviceName );
829  ok = !serv; // ok if no such service yet
830  // also ok if we find the exact same service (well, "kwrite" == "kwrite %U")
831  if (serv && !serv->noDisplay() /* #297720 */) {
832  if (serv->isApplication()) {
833  /*kDebug(250) << "typedExec=" << typedExec
834  << "serv->exec=" << serv->exec()
835  << "simplifiedExecLineFromService=" << simplifiedExecLineFromService(fullExec);*/
836  serviceExec = simplifiedExecLineFromService(serv->exec());
837  if (typedExec == serviceExec){
838  ok = true;
839  m_pService = serv;
840  kDebug(250) << "OK, found identical service: " << serv->entryPath();
841  } else {
842  kDebug(250) << "Exec line differs, service says:" << serviceExec;
843  configPath = serv->entryPath();
844  serviceExec = serv->exec();
845  }
846  } else {
847  kDebug(250) << "Found, but not an application:" << serv->entryPath();
848  }
849  }
850  if (!ok) { // service was found, but it was different -> keep looking
851  ++i;
852  serviceName = initialServiceName + '-' + QString::number(i);
853  }
854  } while (!ok);
855  }
856  if ( m_pService ) {
857  // Existing service selected
858  serviceName = m_pService->name();
859  initialServiceName = serviceName;
860  fullExec = m_pService->exec();
861  } else {
862  const QString binaryName = KRun::binaryName(typedExec, false);
863  kDebug(250) << "binaryName=" << binaryName;
864  // Ensure that the typed binary name actually exists (#81190)
865  if (KStandardDirs::findExe(binaryName).isEmpty()) {
866  KMessageBox::error(q, i18n("'%1' not found, please type a valid program name.", binaryName));
867  return false;
868  }
869  }
870 
871  if (terminal->isChecked()) {
872  KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
873  preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole"));
874  m_command = preferredTerminal;
875  // only add --noclose when we are sure it is konsole we're using
876  if (preferredTerminal == "konsole" && nocloseonexit->isChecked())
877  m_command += QString::fromLatin1(" --noclose");
878  m_command += QString::fromLatin1(" -e ");
879  m_command += edit->text();
880  kDebug(250) << "Setting m_command to" << m_command;
881  }
882  if ( m_pService && terminal->isChecked() != m_pService->terminal() )
883  m_pService = 0; // It's not exactly this service we're running
884 
885 
886  const bool bRemember = remember && remember->isChecked();
887  kDebug(250) << "bRemember=" << bRemember << "service found=" << m_pService;
888  if (m_pService) {
889  if (bRemember) {
890  // Associate this app with qMimeType in mimeapps.list
891  Q_ASSERT(!qMimeType.isEmpty()); // we don't show the remember checkbox otherwise
892  addToMimeAppsList(m_pService->storageId());
893  }
894  } else {
895  const bool createDesktopFile = bRemember || saveNewApps;
896  if (!createDesktopFile) {
897  // Create temp service
898  if (configPath.isEmpty())
899  m_pService = new KService(initialServiceName, fullExec, QString());
900  else {
901  if (!typedExec.contains(QLatin1String("%u"), Qt::CaseInsensitive) &&
902  !typedExec.contains(QLatin1String("%f"), Qt::CaseInsensitive)) {
903  int index = serviceExec.indexOf(QLatin1String("%u"), 0, Qt::CaseInsensitive);
904  if (index == -1) {
905  index = serviceExec.indexOf(QLatin1String("%f"), 0, Qt::CaseInsensitive);
906  }
907  if (index > -1) {
908  fullExec += QLatin1Char(' ');
909  fullExec += serviceExec.mid(index, 2);
910  }
911  }
912  kDebug(250) << "Creating service with Exec=" << fullExec;
913  m_pService = new KService(configPath);
914  m_pService->setExec(fullExec);
915  }
916  if (terminal->isChecked()) {
917  m_pService->setTerminal(true);
918  // only add --noclose when we are sure it is konsole we're using
919  if (preferredTerminal == "konsole" && nocloseonexit->isChecked())
920  m_pService->setTerminalOptions("--noclose");
921  }
922  } else {
923  // If we got here, we can't seem to find a service for what they wanted. Create one.
924 
925  QString menuId;
926 #ifdef Q_WS_WIN32
927  // on windows, do not use the complete path, but only the default name.
928  serviceName = QFileInfo(serviceName).fileName();
929 #endif
930  QString newPath = KService::newServicePath(false /* ignored argument */, serviceName, &menuId);
931  kDebug(250) << "Creating new service" << serviceName << "(" << newPath << ")" << "menuId=" << menuId;
932 
933  KDesktopFile desktopFile(newPath);
934  KConfigGroup cg = desktopFile.desktopGroup();
935  cg.writeEntry("Type", "Application");
936  cg.writeEntry("Name", initialServiceName);
937  cg.writeEntry("Exec", fullExec);
938  cg.writeEntry("NoDisplay", true); // don't make it appear in the K menu
939  if (terminal->isChecked()) {
940  cg.writeEntry("Terminal", true);
941  // only add --noclose when we are sure it is konsole we're using
942  if (preferredTerminal == "konsole" && nocloseonexit->isChecked())
943  cg.writeEntry("TerminalOptions", "--noclose");
944  }
945  cg.writeXdgListEntry("MimeType", QStringList() << qMimeType);
946  cg.sync();
947 
948  addToMimeAppsList(menuId);
949  }
950  }
951 
952  saveComboboxHistory();
953  return true;
954 }
955 
956 void KOpenWithDialog::accept()
957 {
958  if (d->checkAccept())
959  KDialog::accept();
960 }
961 
962 QString KOpenWithDialog::text() const
963 {
964  if (!d->m_command.isEmpty())
965  return d->m_command;
966  else
967  return d->edit->text();
968 }
969 
970 void KOpenWithDialog::hideNoCloseOnExit()
971 {
972  // uncheck the checkbox because the value could be used when "Run in Terminal" is selected
973  d->nocloseonexit->setChecked(false);
974  d->nocloseonexit->hide();
975 }
976 
977 void KOpenWithDialog::hideRunInTerminal()
978 {
979  d->terminal->hide();
980  hideNoCloseOnExit();
981 }
982 
983 KService::Ptr KOpenWithDialog::service() const
984 {
985  return d->m_pService;
986 }
987 
988 void KOpenWithDialogPrivate::saveComboboxHistory()
989 {
990  KHistoryComboBox *combo = static_cast<KHistoryComboBox*>(edit->comboBox());
991  if (combo) {
992  combo->addToHistory(edit->text());
993 
994  KConfigGroup cg( KGlobal::config(), QString::fromLatin1("Open-with settings") );
995  cg.writeEntry( "History", combo->historyItems() );
996  writeEntry( cg, "CompletionMode", combo->completionMode() );
997  // don't store the completion-list, as it contains all of KUrlCompletion's
998  // executables
999  cg.sync();
1000  }
1001 }
1002 
1003 #include "kopenwithdialog.moc"
1004 #include "kopenwithdialog_p.moc"
QSpacerItem
i18n
QString i18n(const char *text)
kservicegroup.h
QModelIndex
KSharedPtr
Definition: kprotocolmanager.h:31
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
KBuildSycocaProgressDialog::rebuildKSycoca
static void rebuildKSycoca(QWidget *parent)
Rebuild KSycoca and show a progress dialog while doing so.
Definition: kbuildsycocaprogressdialog.cpp:41
KConfig::sync
void sync()
KApplicationModel::headerData
virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
Definition: kopenwithdialog.cpp:262
QWidget
kopenwithdialog.h
QDialog::setModal
void setModal(bool modal)
kdebug.h
KOpenWithDialog::hideRunInTerminal
void hideRunInTerminal()
Hide the "Run in &terminal" Checkbox.
Definition: kopenwithdialog.cpp:977
QAbstractItemModel::layoutChanged
void layoutChanged()
kmimetype.h
QItemSelectionModel::currentIndex
QModelIndex currentIndex() const
QAbstractItemView::selectionModel
QItemSelectionModel * selectionModel() const
KOpenWithDialog::~KOpenWithDialog
~KOpenWithDialog()
Destructor.
Definition: kopenwithdialog.cpp:690
KService::serviceByDesktopName
static Ptr serviceByDesktopName(const QString &_name)
kauthorized.h
QTreeView::currentChanged
virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous)
KApplicationModel::entryPathFor
QString entryPathFor(const QModelIndex &index) const
Definition: kopenwithdialog.cpp:318
KApplicationModel::hasChildren
virtual bool hasChildren(const QModelIndex &parent=QModelIndex()) const
Definition: kopenwithdialog.cpp:253
KUrlRequester
This class is a widget showing a lineedit and a button, which invokes a filedialog.
Definition: kurlrequester.h:60
KOpenWithDialog::KOpenWithDialog
KOpenWithDialog(const KUrl::List &urls, QWidget *parent=0)
Create a dialog that asks for a application to open a given URL(s) with.
Definition: kopenwithdialog.cpp:478
label
QString label(StandardShortcut id)
KService
QObject::children
const QObjectList & children() const
kshell.h
kbuildsycocaprogressdialog.h
QString::simplified
QString simplified() const
kurlrequester.h
name
const char * name(StandardAction id)
KOpenWithDialog::slotTextChanged
void slotTextChanged()
Definition: kopenwithdialog.cpp:723
QHBoxLayout
KConfig::group
KConfigGroup group(const QByteArray &group)
KOpenWithDialog::accept
virtual void accept()
Reimplemented from QDialog::accept()
Definition: kopenwithdialog.cpp:956
KConfigGroup::writeEntry
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
KApplicationView::isDirSel
bool isDirSel() const
Definition: kopenwithdialog.cpp:382
KDialog
QString::remove
QString & remove(int position, int n)
KApplicationModel::data
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
Definition: kopenwithdialog.cpp:216
kdesktopfile.h
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
klocale.h
QObject::disconnect
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
KUrl
KApplicationModel::columnCount
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const
Definition: kopenwithdialog.cpp:210
QStyleOptionButton
KGlobal::config
KSharedConfigPtr config()
KHistoryComboBox::addToHistory
void addToHistory(const QString &item)
KHistoryComboBox
QStyleOption::initFrom
void initFrom(const QWidget *widget)
kserviceoffer.h
KHistoryComboBox::historyItems
QStringList historyItems
KComboBox::setLineEdit
virtual void setLineEdit(QLineEdit *)
Ok
QAbstractItemModel::layoutAboutToBeChanged
void layoutAboutToBeChanged()
QModelIndex::isValid
bool isValid() const
QBoxLayout::addWidget
void addWidget(QWidget *widget, int stretch, QFlags< Qt::AlignmentFlag > alignment)
QString::number
QString number(int n, int base)
QList::count
int count(const T &value) const
simplifiedExecLineFromService
static QString simplifiedExecLineFromService(const QString &fullExec)
Definition: kopenwithdialog.cpp:761
KConfig::NoGlobals
KService::newServicePath
static QString newServicePath(bool showInMenu, const QString &suggestedName, QString *menuId=0, const QStringList *reservedMenuIds=0)
QFileInfo::fileName
QString fileName() const
QObject
KApplicationModel::rowCount
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
Definition: kopenwithdialog.cpp:309
QCheckBox
QBoxLayout::addItem
virtual void addItem(QLayoutItem *item)
QObject::setObjectName
void setObjectName(const QString &name)
QString::isEmpty
bool isEmpty() const
KComboBox::setCompletionMode
virtual void setCompletionMode(KGlobalSettings::Completion mode)
QList::removeAll
int removeAll(const T &value)
KApplicationView
Definition: kopenwithdialog_p.h:66
KApplicationModel::execFor
QString execFor(const QModelIndex &index) const
Definition: kopenwithdialog.cpp:327
KConfigGroup::writeXdgListEntry
void writeXdgListEntry(const QString &pKey, const QStringList &value, WriteConfigFlags pFlags=Normal)
KIcon
KOpenWithDialog
"Open With" dialog box.
Definition: kopenwithdialog.h:39
QVBoxLayout
QModelIndex::internalPointer
void * internalPointer() const
kopenwithdialog_p.h
khistorycombobox.h
KStringHandler::csqueeze
QString csqueeze(const QString &str, int maxlen=40)
QList::first
T & first()
QString
QList< AppNode * >
KOpenWithDialog::slotSelected
void slotSelected(const QString &_name, const QString &_exec)
Definition: kopenwithdialog.cpp:698
QModelIndex::parent
QModelIndex parent() const
QComboBox::setDuplicatesEnabled
void setDuplicatesEnabled(bool enable)
QLayout::setMargin
void setMargin(int margin)
KService::serviceByDesktopPath
static Ptr serviceByDesktopPath(const QString &_path)
QDialog::accept
virtual void accept()
QStringList
KApplicationModel::canFetchMore
virtual bool canFetchMore(const QModelIndex &parent) const
Definition: kopenwithdialog.cpp:201
QAbstractItemModel::createIndex
QModelIndex createIndex(int row, int column, void *ptr) const
writeEntry
void writeEntry(KConfigGroup &group, const char *key, const KGlobalSettings::Completion &aValue, KConfigBase::WriteConfigFlags flags=KConfigBase::Normal)
Definition: kopenwithdialog.cpp:57
QFileInfo
QList::end
iterator end()
KShell::quoteArg
QString quoteArg(const QString &arg)
QString::contains
bool contains(QChar ch, Qt::CaseSensitivity cs) const
QLatin1Char
KApplicationModel::isDirectory
bool isDirectory(const QModelIndex &index) const
Definition: kopenwithdialog.cpp:336
KApplicationModel::index
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
Definition: kopenwithdialog.cpp:276
kstringhandler.h
KDesktopFile
ok
KGuiItem ok()
KOpenWithDialog::text
QString text() const
Definition: kopenwithdialog.cpp:962
KConfigGroup
QItemSelection
KUrl::List
krun.h
KLineEdit
KOpenWithDialog::setSaveNewApplications
void setSaveNewApplications(bool b)
Set whether a new .desktop file should be created if the user selects an application for which no cor...
Definition: kopenwithdialog.cpp:756
KOpenWithDialog::slotTerminalToggled
void slotTerminalToggled(bool)
Definition: kopenwithdialog.cpp:732
QString::mid
QString mid(int position, int n) const
KUrlCompletion::ExeCompletion
Definition: kurlcompletion.h:53
KApplicationView::highlighted
void highlighted(const QString &_name, const QString &_exec)
QLatin1String
QTreeView
KApplicationView::KApplicationView
KApplicationView(QWidget *parent=0)
Definition: kopenwithdialog.cpp:356
KApplicationView::selected
void selected(const QString &_name, const QString &_exec)
kstandarddirs.h
KSharedPtr::staticCast
static KSharedPtr< T > staticCast(const KSharedPtr< U > &o)
KOpenWithDialog::hideNoCloseOnExit
void hideNoCloseOnExit()
Hide the "Do not &close when command exits" Checkbox.
Definition: kopenwithdialog.cpp:970
QTreeView::setModel
virtual void setModel(QAbstractItemModel *model)
QTreeView::selectionChanged
virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
KApplicationView::~KApplicationView
~KApplicationView()
Definition: kopenwithdialog.cpp:361
KStandardDirs::findExe
static QString findExe(const QString &appname, const QString &pathstr=QString(), SearchOptions options=NoSearchOptions)
QList< SPtr >::ConstIterator
typedef ConstIterator
QWidget::setCaption
void setCaption(const QString &c)
KGlobalSettings::Completion
Completion
kWarning
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
KApplicationView::currentChanged
virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous)
Definition: kopenwithdialog.cpp:391
QAbstractItemModel
KAuthorized::authorize
bool authorize(const QString &genericAction)
QString::fromLatin1
QString fromLatin1(const char *str, int size)
KLineEdit::setClearButtonShown
void setClearButtonShown(bool show)
QList::prepend
void prepend(const T &value)
KOpenWithDialog::slotHighlighted
void slotHighlighted(const QString &_name, const QString &_exec)
Definition: kopenwithdialog.cpp:708
klineedit.h
KConfigGroup::sync
void sync()
KApplicationModel::KApplicationModel
KApplicationModel(QObject *parent=0)
Definition: kopenwithdialog.cpp:190
QComboBox::setMaxCount
void setMaxCount(int max)
KGlobalSettings::completionMode
static Completion completionMode()
QAbstractItemView::model
QAbstractItemModel * model() const
KServiceGroup::group
static Ptr group(const QString &relPath)
KSharedConfig::openConfig
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, const char *resourceType="config")
KApplicationModel::~KApplicationModel
virtual ~KApplicationModel()
Definition: kopenwithdialog.cpp:196
QComboBox::setSizeAdjustPolicy
void setSizeAdjustPolicy(SizeAdjustPolicy policy)
kmessagebox.h
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QLabel
QObject::parent
QObject * parent() const
KApplicationModel
Definition: kopenwithdialog_p.h:32
KRun::binaryName
static QString binaryName(const QString &execLine, bool removePath)
Given a full command line (e.g.
Definition: krun.cpp:568
QBoxLayout
QList::begin
iterator begin()
KHistoryComboBox::setHistoryItems
void setHistoryItems(const QStringList &items)
QBoxLayout::setStretchFactor
bool setStretchFactor(QWidget *widget, int stretch)
KComboBox::completionMode
KGlobalSettings::Completion completionMode() const
KUrlCompletion
This class does completion of URLs including user directories (~user) and environment variables...
Definition: kurlcompletion.h:41
KMessageBox::error
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
kconfiggroup.h
KOpenWithDialog::service
KService::Ptr service() const
Definition: kopenwithdialog.cpp:983
QBoxLayout::addLayout
void addLayout(QLayout *layout, int stretch)
KService::serviceByStorageId
static Ptr serviceByStorageId(const QString &_storageId)
QWidget::caption
QString caption() const
KRecentDirs::list
QStringList list(const QString &fileClass)
Returns a list of directories associated with this file-class.
Definition: krecentdirs.cpp:60
KApplicationModel::fetchMore
virtual void fetchMore(const QModelIndex &parent)
Definition: kopenwithdialog.cpp:238
QVariant
kurlcompletion.h
KApplicationView::setModel
virtual void setModel(QAbstractItemModel *model)
Definition: kopenwithdialog.cpp:366
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:24:53 by doxygen 1.8.7 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 API Reference

Skip menu "kdelibs 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

Search



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