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>
59 KConfigBase::WriteConfigFlags flags = KConfigBase::Normal )
64 namespace KDEPrivate {
70 : isDir(false), parent(0), fetched(false)
90 bool AppNodeLessThan(KDEPrivate::AppNode *n1, KDEPrivate::AppNode *n2)
94 return n1->text.compare(n2->text, Qt::CaseInsensitive) < 0;
102 return n1->text.compare(n2->text, Qt::CaseInsensitive) < 0;
111 class KApplicationModelPrivate
115 : q(qq), root(new KDEPrivate::AppNode())
118 ~KApplicationModelPrivate()
123 void fillNode(
const QString &entryPath, KDEPrivate::AppNode *node);
127 KDEPrivate::AppNode *root;
130 void KApplicationModelPrivate::fillNode(
const QString &_entryPath, KDEPrivate::AppNode *node)
133 if (!root || !root->isValid())
return;
137 for( KServiceGroup::List::ConstIterator it = list.begin();
138 it != list.end(); ++it)
145 const KSycocaEntry::Ptr p = (*it);
146 if (p->isType(KST_KService))
153 icon = service->
icon();
154 text = service->name();
155 exec = service->
exec();
156 entryPath = service->entryPath();
158 else if (p->isType(KST_KServiceGroup))
162 if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0)
165 icon = serviceGroup->icon();
166 text = serviceGroup->caption();
167 entryPath = serviceGroup->entryPath();
172 kWarning(250) <<
"KServiceGroup: Unexpected object in list!";
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);
185 qStableSort(node->children.begin(), node->children.end(), KDEPrivate::AppNodeLessThan);
193 d->fillNode(
QString(), d->root);
203 if (!parent.isValid())
206 KDEPrivate::AppNode *node =
static_cast<KDEPrivate::AppNode*
>(parent.internalPointer());
207 return node->isDir && !node->fetched;
218 if (!index.isValid())
221 KDEPrivate::AppNode *node =
static_cast<KDEPrivate::AppNode*
>(index.internalPointer());
224 case Qt::DisplayRole:
227 case Qt::DecorationRole:
228 if (!node->icon.isEmpty()) {
229 return KIcon(node->icon);
240 if (!parent.isValid())
243 KDEPrivate::AppNode *node =
static_cast<KDEPrivate::AppNode*
>(parent.internalPointer());
247 emit layoutAboutToBeChanged();
248 d->fillNode(node->entryPath, node);
249 node->fetched =
true;
250 emit layoutChanged();
255 if (!parent.isValid())
258 KDEPrivate::AppNode *node =
static_cast<KDEPrivate::AppNode*
>(parent.internalPointer());
264 if (orientation != Qt::Horizontal || section != 0)
268 case Qt::DisplayRole:
269 return i18n(
"Known Applications");
278 if (row < 0 || column != 0)
279 return QModelIndex();
281 KDEPrivate::AppNode *node = d->root;
282 if (parent.isValid())
283 node = static_cast<KDEPrivate::AppNode*>(parent.internalPointer());
285 if (row >= node->children.count())
286 return QModelIndex();
288 return createIndex(row, 0, node->children.at(row));
293 if (!index.isValid())
294 return QModelIndex();
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);
300 if (
id >= 0 && id < node->
parent->parent->children.count())
301 return createIndex(
id, 0, node->parent);
303 return QModelIndex();
306 return QModelIndex();
311 if (!parent.isValid())
312 return d->root->children.count();
314 KDEPrivate::AppNode *node =
static_cast<KDEPrivate::AppNode*
>(parent.internalPointer());
315 return node->children.count();
320 if (!index.isValid())
323 KDEPrivate::AppNode *node =
static_cast<KDEPrivate::AppNode*
>(index.internalPointer());
324 return node->entryPath;
329 if (!index.isValid())
332 KDEPrivate::AppNode *node =
static_cast<KDEPrivate::AppNode*
>(index.internalPointer());
338 if (!index.isValid())
341 KDEPrivate::AppNode *node =
static_cast<KDEPrivate::AppNode*
>(index.internalPointer());
345 class KApplicationViewPrivate
348 KApplicationViewPrivate()
357 :
QTreeView(parent), d(new KApplicationViewPrivate)
369 disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
370 this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection)));
373 QTreeView::setModel(model);
377 connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
378 this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection)));
385 QModelIndex index = selectionModel()->currentIndex();
386 return d->appModel->isDirectory(index);
393 QTreeView::currentChanged(current, previous);
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);
403 void KApplicationView::slotSelectionChanged(
const QItemSelection &selected,
const QItemSelection &deselected)
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);
423 class KOpenWithDialogPrivate
427 : q(qq), saveNewApps(false)
438 void addToMimeAppsList(
const QString& serviceId);
452 void saveComboboxHistory();
461 void _k_slotDbClick();
462 void _k_slotFileSelected();
465 bool m_terminaldirty;
474 QCheckBox *nocloseonexit;
479 :
KDialog(parent), d(new KOpenWithDialogPrivate(this))
481 setObjectName( QLatin1String(
"openwith" ) );
483 setCaption(
i18n(
"Open With" ) );
486 if( _urls.count() == 1 )
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() );
494 text =
i18n(
"Choose the name of the program with which to open the selected files." );
495 d->setMimeType(_urls);
501 :
KDialog(parent), d(new KOpenWithDialogPrivate(this))
503 setObjectName( QLatin1String(
"openwith" ) );
506 if (_urls.count()>0 && !_urls.first().isEmpty())
508 if (_urls.count() > 1)
509 caption += QString::fromLatin1(
"...");
511 d->setMimeType(_urls);
512 d->init(_text, _value);
517 :
KDialog(parent), d(new KOpenWithDialogPrivate(this))
519 setObjectName( QLatin1String(
"openwith" ) );
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);
533 :
KDialog(parent), d(new KOpenWithDialogPrivate(this))
535 setObjectName( QLatin1String(
"openwith" ) );
537 setCaption(
i18n(
"Choose Application"));
539 "If the program is not listed, enter the name or click "
540 "the browse button.</qt>");
541 d->qMimeType.clear();
545 void KOpenWithDialogPrivate::setMimeType(
const KUrl::List &_urls)
547 if ( _urls.count() == 1 )
549 qMimeType = KMimeType::findByUrl( _urls.first())->
name();
550 if (qMimeType == QLatin1String(
"application/octet-stream"))
557 void KOpenWithDialogPrivate::init(
const QString &_text,
const QString &_value)
560 m_terminaldirty =
false;
565 q->setButtons(KDialog::Ok | KDialog::Cancel);
567 QWidget *mainWidget = q->mainWidget();
569 QBoxLayout *topLayout =
new QVBoxLayout( mainWidget );
570 topLayout->setMargin(0);
572 label->setWordWrap(
true);
573 topLayout->addWidget(label);
582 combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
583 combo->setDuplicatesEnabled(
false );
585 int max = cg.readEntry(
"Maximum history", 15 );
586 combo->setMaxCount( max );
596 edit->lineEdit()->setReadOnly(
true);
597 edit->button()->hide();
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"
611 "%m - the mini-icon\n"
612 "%c - the comment"));
614 topLayout->addWidget(edit);
616 if ( edit->comboBox() ) {
618 edit->comboBox()->setCompletionObject( comp );
619 edit->comboBox()->setAutoDeleteCompletionObject(
true );
622 QObject::connect(edit, SIGNAL(textChanged(
QString)), q, SLOT(slotTextChanged()));
623 QObject::connect(edit, SIGNAL(urlSelected(
KUrl)), q, SLOT(_k_slotFileSelected()));
627 topLayout->addWidget(view);
628 topLayout->setStretchFactor(view, 1);
634 QObject::connect(view, SIGNAL(doubleClicked(QModelIndex)),
635 q, SLOT(_k_slotDbClick()));
637 terminal =
new QCheckBox(
i18n(
"Run in &terminal"), mainWidget );
640 QObject::connect(terminal, SIGNAL(toggled(
bool)), q, SLOT(slotTerminalToggled(
bool)));
642 topLayout->addWidget(terminal);
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 );
649 QBoxLayout* nocloseonexitLayout =
new QHBoxLayout();
650 nocloseonexitLayout->setMargin( 0 );
651 QSpacerItem* spacer =
new QSpacerItem( checkBoxIndentation, 0, QSizePolicy::Fixed, QSizePolicy::Minimum );
652 nocloseonexitLayout->addItem( spacer );
654 nocloseonexit =
new QCheckBox(
i18n(
"&Do not close when command exits"), mainWidget );
655 nocloseonexit->setChecked(
false );
656 nocloseonexit->setDisabled(
true );
661 QString preferredTerminal = confGroup.readPathEntry(
"TerminalApplication", QString::fromLatin1(
"konsole"));
663 if (bReadOnly || preferredTerminal !=
"konsole")
664 nocloseonexit->hide();
666 nocloseonexitLayout->addWidget( nocloseonexit );
667 topLayout->addLayout( nocloseonexitLayout );
669 if (!qMimeType.isNull())
671 remember =
new QCheckBox(
i18n(
"&Remember application association for this type of file"), mainWidget);
673 topLayout->addWidget(remember);
678 q->setMinimumSize(q->minimumSizeHint());
684 q->slotTextChanged();
701 d->edit->setText(_exec);
702 d->curService = pService;
711 if (d->curService && !d->m_terminaldirty)
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;
727 enableButton(
Ok, !d->edit->text().isEmpty());
735 d->m_terminaldirty =
true;
736 d->nocloseonexit->setDisabled(!d->terminal->isChecked());
741 void KOpenWithDialogPrivate::_k_slotDbClick()
744 if (view->isDirSel()) {
750 void KOpenWithDialogPrivate::_k_slotFileSelected()
764 exec.remove(
"%u", Qt::CaseInsensitive);
765 exec.remove(
"%f", Qt::CaseInsensitive);
766 exec.remove(
"-caption %c");
767 exec.remove(
"-caption \"%c\"");
770 return exec.simplified();
773 void KOpenWithDialogPrivate::addToMimeAppsList(
const QString& serviceId )
777 QStringList apps = addedApps.readXdgListEntry(qMimeType);
778 apps.removeAll(serviceId);
779 apps.prepend(serviceId);
780 addedApps.writeXdgListEntry(qMimeType, apps);
786 fileTypesConfig->
sync();
788 kDebug(250) <<
"rebuilding ksycoca...";
794 Q_ASSERT( m_pService );
797 bool KOpenWithDialogPrivate::checkAccept()
799 const QString typedExec(edit->text());
800 if (typedExec.isEmpty())
809 m_pService = curService;
815 if (serviceName.isEmpty()) {
816 KMessageBox::error(q,
i18n(
"Could not extract executable name from '%1', please type a valid program name.", serviceName));
819 initialServiceName = serviceName;
822 kDebug(250) <<
"initialServiceName=" << initialServiceName;
827 kDebug(250) <<
"looking for service" << serviceName;
837 if (typedExec == serviceExec){
840 kDebug(250) <<
"OK, found identical service: " << serv->entryPath();
842 kDebug(250) <<
"Exec line differs, service says:" << serviceExec;
843 configPath = serv->entryPath();
844 serviceExec = serv->
exec();
847 kDebug(250) <<
"Found, but not an application:" << serv->entryPath();
858 serviceName = m_pService->name();
859 initialServiceName = serviceName;
860 fullExec = m_pService->exec();
863 kDebug(250) <<
"binaryName=" << binaryName;
871 if (terminal->isChecked()) {
873 preferredTerminal = confGroup.readPathEntry(
"TerminalApplication", QString::fromLatin1(
"konsole"));
874 m_command = preferredTerminal;
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;
882 if ( m_pService && terminal->isChecked() != m_pService->terminal() )
886 const bool bRemember = remember && remember->isChecked();
887 kDebug(250) <<
"bRemember=" << bRemember <<
"service found=" << m_pService;
891 Q_ASSERT(!qMimeType.isEmpty());
892 addToMimeAppsList(m_pService->storageId());
895 const bool createDesktopFile = bRemember || saveNewApps;
896 if (!createDesktopFile) {
898 if (configPath.isEmpty())
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);
905 index = serviceExec.indexOf(QLatin1String(
"%f"), 0, Qt::CaseInsensitive);
908 fullExec += QLatin1Char(
' ');
909 fullExec += serviceExec.mid(index, 2);
912 kDebug(250) <<
"Creating service with Exec=" << fullExec;
913 m_pService =
new KService(configPath);
914 m_pService->setExec(fullExec);
916 if (terminal->isChecked()) {
917 m_pService->setTerminal(
true);
919 if (preferredTerminal ==
"konsole" && nocloseonexit->isChecked())
920 m_pService->setTerminalOptions(
"--noclose");
928 serviceName = QFileInfo(serviceName).fileName();
931 kDebug(250) <<
"Creating new service" << serviceName <<
"(" << newPath <<
")" <<
"menuId=" << menuId;
939 if (terminal->isChecked()) {
942 if (preferredTerminal ==
"konsole" && nocloseonexit->isChecked())
943 cg.
writeEntry(
"TerminalOptions",
"--noclose");
948 addToMimeAppsList(menuId);
952 saveComboboxHistory();
958 if (d->checkAccept())
964 if (!d->m_command.isEmpty())
967 return d->edit->text();
973 d->nocloseonexit->setChecked(
false);
974 d->nocloseonexit->hide();
985 return d->m_pService;
988 void KOpenWithDialogPrivate::saveComboboxHistory()
1003 #include "kopenwithdialog.moc"
1004 #include "kopenwithdialog_p.moc"
QString i18n(const char *text)
static void rebuildKSycoca(QWidget *parent)
Rebuild KSycoca and show a progress dialog while doing so.
virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
void hideRunInTerminal()
Hide the "Run in &terminal" Checkbox.
~KOpenWithDialog()
Destructor.
static Ptr serviceByDesktopName(const QString &_name)
QString entryPathFor(const QModelIndex &index) const
virtual bool hasChildren(const QModelIndex &parent=QModelIndex()) const
This class is a widget showing a lineedit and a button, which invokes a filedialog.
KOpenWithDialog(const KUrl::List &urls, QWidget *parent=0)
Create a dialog that asks for a application to open a given URL(s) with.
QString label(StandardShortcut id)
const char * name(StandardAction id)
KConfigGroup group(const QByteArray &group)
virtual void accept()
Reimplemented from QDialog::accept()
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
bool isApplication() const
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const
KSharedConfigPtr config()
void addToHistory(const QString &item)
virtual void setLineEdit(QLineEdit *)
static QString simplifiedExecLineFromService(const QString &fullExec)
static QString newServicePath(bool showInMenu, const QString &suggestedName, QString *menuId=0, const QStringList *reservedMenuIds=0)
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
virtual QModelIndex parent(const QModelIndex &index) const
virtual void setCompletionMode(KGlobalSettings::Completion mode)
QString execFor(const QModelIndex &index) const
void writeXdgListEntry(const QString &pKey, const QStringList &value, WriteConfigFlags pFlags=Normal)
QString csqueeze(const QString &str, int maxlen=40)
void slotSelected(const QString &_name, const QString &_exec)
static Ptr serviceByDesktopPath(const QString &_path)
virtual bool canFetchMore(const QModelIndex &parent) const
void writeEntry(KConfigGroup &group, const char *key, const KGlobalSettings::Completion &aValue, KConfigBase::WriteConfigFlags flags=KConfigBase::Normal)
QString quoteArg(const QString &arg)
bool isDirectory(const QModelIndex &index) const
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
void setSaveNewApplications(bool b)
Set whether a new .desktop file should be created if the user selects an application for which no cor...
void slotTerminalToggled(bool)
void highlighted(const QString &_name, const QString &_exec)
KApplicationView(QWidget *parent=0)
void selected(const QString &_name, const QString &_exec)
static KSharedPtr< KService > staticCast(const KSharedPtr< U > &o)
void hideNoCloseOnExit()
Hide the "Do not &close when command exits" Checkbox.
static QString findExe(const QString &appname, const QString &pathstr=QString(), SearchOptions options=NoSearchOptions)
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
bool authorize(const QString &genericAction)
void setClearButtonShown(bool show)
void slotHighlighted(const QString &_name, const QString &_exec)
KApplicationModel(QObject *parent=0)
static Completion completionMode()
static Ptr group(const QString &relPath)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, const char *resourceType="config")
virtual ~KApplicationModel()
static QString binaryName(const QString &execLine, bool removePath)
Given a full command line (e.g.
void setHistoryItems(const QStringList &items)
KGlobalSettings::Completion completionMode() const
This class does completion of URLs including user directories (~user) and environment variables...
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
KService::Ptr service() const
QString number(KIO::filesize_t size)
Converts a size to a string representation Not unlike QString::number(...)
static Ptr serviceByStorageId(const QString &_storageId)
QStringList list(const QString &fileClass)
Returns a list of directories associated with this file-class.
virtual void fetchMore(const QModelIndex &parent)
virtual void setModel(QAbstractItemModel *model)