KWidgetsAddons

kmimetypechooser.cpp
1 /* This file is part of the KDE libraries
2  Copyright (C) 2001 - 2004 Anders Lund <[email protected]>
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License version 2 as published by the Free Software Foundation.
7 
8  This library is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  Library General Public License for more details.
12 
13  You should have received a copy of the GNU Library General Public License
14  along with this library; see the file COPYING.LIB. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17 */
18 
19 #include "kmimetypechooser.h"
20 
21 #include "kmimetypeeditor.h"
22 #include <qmimedatabase.h>
23 
24 #include <QDialogButtonBox>
25 #include <QLabel>
26 #include <QLineEdit>
27 #include <QPushButton>
28 #include <QSortFilterProxyModel>
29 #include <QStandardPaths>
30 #include <QStandardItemModel>
31 #include <QTreeView>
32 #include <QVBoxLayout>
33 
34 //BEGIN KMimeTypeChooserPrivate
35 class KMimeTypeChooserPrivate
36 {
37 public:
38  KMimeTypeChooserPrivate(KMimeTypeChooser *parent)
39  : q(parent)
40  {
41  }
42 
43  void loadMimeTypes(const QStringList &selected = QStringList());
44  QVector<const QStandardItem *> getCheckedItems();
45 
46  void _k_editMimeType();
47  void _k_slotCurrentChanged(const QModelIndex &index);
48  void _k_slotSycocaDatabaseChanged(const QStringList &);
49 
51  QTreeView *mimeTypeTree = nullptr;
52  QStandardItemModel *m_model = nullptr;
53  QSortFilterProxyModel *m_proxyModel = nullptr;
54  QLineEdit *m_filterLineEdit = nullptr;
55  QPushButton *btnEditMimeType = nullptr;
56 
57  QString defaultgroup;
58  QStringList groups;
59  int visuals;
60 };
61 //END
62 
63 static const char s_keditfiletypeExecutable[] = "keditfiletype5";
64 
65 //BEGIN KMimeTypeChooser
67  const QStringList &selMimeTypes,
68  const QString &defaultGroup,
69  const QStringList &groupsToShow,
70  int visuals,
71  QWidget *parent)
72  : QWidget(parent),
73  d(new KMimeTypeChooserPrivate(this))
74 {
75  d->defaultgroup = defaultGroup;
76  d->groups = groupsToShow;
77  if (visuals & EditButton) {
78  if (QStandardPaths::findExecutable(QString::fromLatin1(s_keditfiletypeExecutable)).isEmpty()) {
79  visuals &= ~EditButton;
80  }
81  }
82  d->visuals = visuals;
83 
84  QVBoxLayout *vboxLayout = new QVBoxLayout(this);
85  vboxLayout->setContentsMargins(0, 0, 0, 0);
86  if (!text.isEmpty()) {
87  vboxLayout->addWidget(new QLabel(text, this));
88  }
89 
90  d->mimeTypeTree = new QTreeView(this);
91  d->m_model = new QStandardItemModel(d->mimeTypeTree);
92  d->m_proxyModel = new QSortFilterProxyModel(d->mimeTypeTree);
93  d->m_proxyModel->setRecursiveFilteringEnabled(true);
94  d->m_proxyModel->setFilterKeyColumn(-1);
95  d->m_proxyModel->setSourceModel(d->m_model);
96  d->mimeTypeTree->setModel(d->m_proxyModel);
97 
98  d->m_filterLineEdit = new QLineEdit(this);
99  d->m_filterLineEdit->setPlaceholderText(tr("Search for file type or filename pattern...", "@info:placeholder"));
100  QLabel *filterLabel = new QLabel(tr("&Filter:", "@label:textbox"));
101  filterLabel->setBuddy(d->m_filterLineEdit);
102  connect(d->m_filterLineEdit, &QLineEdit::textChanged, this, [this](const QString &text) {
103  d->m_proxyModel->setFilterRegularExpression(
104  QRegularExpression(text, QRegularExpression::CaseInsensitiveOption));
105  });
106 
107  QHBoxLayout *filterLayout = new QHBoxLayout();
108  filterLayout->addWidget(filterLabel);
109  filterLayout->addWidget(d->m_filterLineEdit);
110  vboxLayout->addLayout(filterLayout);
111  d->m_filterLineEdit->setFocus();
112 
113  vboxLayout->addWidget(d->mimeTypeTree);
114  QStringList headerLabels({tr("MIME Type", "@title:column")});
115 
116  if (visuals & Comments) {
117  headerLabels.append(tr("Comment", "@title:column"));
118  }
119 
120  if (visuals & Patterns) {
121  headerLabels.append(tr("Patterns", "@title:column"));
122  }
123 
124  d->m_model->setColumnCount(headerLabels.count());
125  d->m_model->setHorizontalHeaderLabels(headerLabels);
126  QFontMetrics fm(d->mimeTypeTree->fontMetrics());
127  // big enough for most names/comments, but not for the insanely long ones
128  const int optWidth = 20 * fm.averageCharWidth();
129  d->mimeTypeTree->setColumnWidth(0, optWidth);
130  d->mimeTypeTree->setColumnWidth(1, optWidth);
131 
132  d->loadMimeTypes(selMimeTypes);
133 
134  if (visuals & EditButton) {
135  QHBoxLayout *buttonLayout = new QHBoxLayout();
136  buttonLayout->addStretch(1);
137  d->btnEditMimeType = new QPushButton(tr("&Edit...", "@action:button"), this);
138  buttonLayout->addWidget(d->btnEditMimeType);
139  d->btnEditMimeType->setEnabled(false);
140 
141  connect(d->btnEditMimeType, &QPushButton::clicked, this, [this]() { d->_k_editMimeType(); });
142  connect(d->mimeTypeTree, &QAbstractItemView::doubleClicked, this, [this]() { d->_k_editMimeType(); });
143 
144  connect(d->mimeTypeTree, &QTreeView::activated,
145  this, [this](const QModelIndex &index) { d->_k_slotCurrentChanged(index); });
146 
147  d->btnEditMimeType->setToolTip(tr("Launch the MIME type editor", "@info:tooltip"));
148 
149  vboxLayout->addLayout(buttonLayout);
150  }
151  setLayout(vboxLayout);
152 }
153 
154 KMimeTypeChooser::~KMimeTypeChooser()
155 {
156  delete d;
157 }
158 
159 void KMimeTypeChooserPrivate::loadMimeTypes(const QStringList &_selectedMimeTypes)
160 {
161  QStringList selMimeTypes;
162 
163  if (!_selectedMimeTypes.isEmpty()) {
164  selMimeTypes = _selectedMimeTypes;
165  } else {
166  selMimeTypes = q->mimeTypes();
167  }
168 
170  QMimeDatabase db;
171  const QList<QMimeType> mimetypes = db.allMimeTypes();
172 
173  bool agroupisopen = false;
174  QStandardItem *idefault = nullptr; //open this, if all other fails
175  QStandardItem *firstChecked = nullptr; // make this one visible after the loop
176 
177  for (const QMimeType &mt : mimetypes) {
178  const QString mimetype = mt.name();
179  const int index = mimetype.indexOf(QLatin1Char('/'));
180  // e.g. "text", "audio", "inode"
181  const QString maj = mimetype.left(index);
182 
183  if (!groups.isEmpty() && !groups.contains(maj)) {
184  continue;
185  }
186 
187  QStandardItem *groupItem;
188  QMap<QString, QStandardItem *>::Iterator mit = groupItems.find(maj);
189  if (mit == groupItems.end()) {
190  groupItem = new QStandardItem(maj);
191  groupItem->setFlags(Qt::ItemIsEnabled);
192  // a dud item to fill the patterns column next to "groupItem" and setFlags() on it
193  QStandardItem *secondColumn = new QStandardItem();
194  secondColumn->setFlags(Qt::NoItemFlags);
195  QStandardItem *thirdColumn = new QStandardItem();
196  thirdColumn->setFlags(Qt::NoItemFlags);
197  m_model->appendRow({groupItem, secondColumn, thirdColumn});
198  groupItems.insert(maj, groupItem);
199  if (maj == defaultgroup) {
200  idefault = groupItem;
201  }
202  } else {
203  groupItem = mit.value();
204  }
205 
206  // e.g. "html", "plain", "mp4"
207  const QString min = mimetype.mid(index + 1);
208  QStandardItem *mime = new QStandardItem(QIcon::fromTheme(mt.iconName()), min);
209  mime->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
210 
211  QStandardItem *comments = nullptr;
212  if (visuals & KMimeTypeChooser::Comments) {
213  comments = new QStandardItem(mt.comment());
214  comments->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
215  }
216 
217  QStandardItem *patterns = nullptr;
218 
219  if (visuals & KMimeTypeChooser::Patterns) {
220  patterns = new QStandardItem(mt.globPatterns().join(QLatin1String("; ")));
221  patterns->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
222  }
223 
224  groupItem->appendRow(QList<QStandardItem *>({mime, comments, patterns}));
225 
226  if (selMimeTypes.contains(mimetype)) {
227  mime->setCheckState(Qt::Checked);
228  const QModelIndex index = m_proxyModel->mapFromSource(m_model->indexFromItem(groupItem));
229  mimeTypeTree->expand(index);
230  agroupisopen = true;
231  if (!firstChecked) {
232  firstChecked = mime;
233  }
234  } else {
235  mime->setCheckState(Qt::Unchecked);
236  }
237  }
238 
239  m_model->sort(0);
240 
241  if (firstChecked) {
242  const QModelIndex index = m_proxyModel->mapFromSource(m_model->indexFromItem(firstChecked));
243  mimeTypeTree->scrollTo(index);
244  }
245 
246  if (!agroupisopen && idefault) {
247  const QModelIndex index = m_proxyModel->mapFromSource(m_model->indexFromItem(idefault));
248  mimeTypeTree->expand(index);
249  mimeTypeTree->scrollTo(index);
250  }
251 }
252 
253 void KMimeTypeChooserPrivate::_k_editMimeType()
254 {
255  QModelIndex mimeIndex = m_proxyModel->mapToSource(mimeTypeTree->currentIndex());
256 
257  // skip parent (non-leaf) nodes
258  if (m_model->hasChildren(mimeIndex)) {
259  return;
260  }
261 
262  if (mimeIndex.column() > 0) { // we need the item from column 0 to concatenate "mt" below
263  mimeIndex = m_model->sibling(mimeIndex.row(), 0, mimeIndex);
264  }
265 
266  const QStandardItem *item = m_model->itemFromIndex(mimeIndex);
267  const QString mt = (item->parent())->text() + QLatin1Char('/') + item->text();
269 
270  // KF5 TODO: use a QFileSystemWatcher on one of the shared-mime-info generated files, instead.
271  //q->connect( KSycoca::self(), SIGNAL(databaseChanged(QStringList)),
272  // q, SLOT(_k_slotSycocaDatabaseChanged(QStringList)) );
273 #pragma message("KF5 TODO: use QFileSystemWatcher to be told when keditfiletype changed a MIME type")
274  // or a better idea: a QMimeDatabaseWatcher class in Qt itself
275 
276 }
277 
278 void KMimeTypeChooserPrivate::_k_slotCurrentChanged(const QModelIndex &index)
279 {
280  if (btnEditMimeType) {
281  const QModelIndex srcIndex = m_proxyModel->mapToSource(index);
282  const QStandardItem *currentItem = m_model->itemFromIndex(srcIndex);
283  btnEditMimeType->setEnabled(currentItem && currentItem->parent());
284  }
285 }
286 
287 // TODO: see _k_editMimeType
288 void KMimeTypeChooserPrivate::_k_slotSycocaDatabaseChanged(const QStringList &changedResources)
289 {
290  if (changedResources.contains(QLatin1String("xdgdata-mime"))) {
291  loadMimeTypes();
292  }
293 }
294 
295 QVector<const QStandardItem *> KMimeTypeChooserPrivate::getCheckedItems()
296 {
298  const int rowCount = m_model->rowCount();
299  for (int i = 0; i < rowCount; ++i) {
300  const QStandardItem *groupItem = m_model->item(i);
301  const int childCount = groupItem->rowCount();
302  for (int j = 0; j < childCount; ++j) {
303  const QStandardItem *child = groupItem->child(j);
304  if (child->checkState() == Qt::Checked) {
305  lst.append(child);
306  }
307  }
308  }
309  return lst;
310 }
311 
313 {
314  QStringList mimeList;
315  const QVector<const QStandardItem *> checkedItems = d->getCheckedItems();
316  mimeList.reserve(checkedItems.size());
317  for (const QStandardItem *item : checkedItems) {
318  mimeList.append(item->parent()->text() + QLatin1Char('/') + item->text());
319  }
320  return mimeList;
321 }
322 
324 {
325  QStringList patternList;
326  const QVector<const QStandardItem *> checkedItems = d->getCheckedItems();
327  QMimeDatabase db;
328  for (const QStandardItem *item : checkedItems) {
329  QMimeType mime = db.mimeTypeForName(item->parent()->text() + QLatin1Char('/') + item->text());
330  Q_ASSERT(mime.isValid());
331  patternList += mime.globPatterns();
332  }
333  return patternList;
334 }
335 //END
336 
337 //BEGIN KMimeTypeChooserDialog::Private
338 
339 class Q_DECL_HIDDEN KMimeTypeChooserDialog::Private
340 {
341 public:
342  Private(KMimeTypeChooserDialog *parent)
343  : q(parent)
344  {
345  }
346 
347  void init();
348 
349  KMimeTypeChooserDialog *q;
350  KMimeTypeChooser *m_chooser;
351 };
352 
353 //END
354 
355 //BEGIN KMimeTypeChooserDialog
357  const QString &title,
358  const QString &text,
359  const QStringList &selMimeTypes,
360  const QString &defaultGroup,
361  const QStringList &groupsToShow,
362  int visuals,
363  QWidget *parent)
364  : QDialog(parent), d(new Private(this))
365 {
366  setWindowTitle(title);
367 
368  d->m_chooser = new KMimeTypeChooser(text, selMimeTypes,
369  defaultGroup, groupsToShow, visuals,
370  this);
371  d->init();
372 }
373 
375  const QString &title,
376  const QString &text,
377  const QStringList &selMimeTypes,
378  const QString &defaultGroup,
379  QWidget *parent)
380  : QDialog(parent), d(new Private(this))
381 {
382  setWindowTitle(title);
383 
384  d->m_chooser = new KMimeTypeChooser(text, selMimeTypes,
385  defaultGroup, QStringList(),
387  this);
388  d->init();
389 }
390 
392 {
393  return d->m_chooser;
394 }
395 
396 void KMimeTypeChooserDialog::Private::init()
397 {
399  q->setLayout(layout);
400 
401  layout->addWidget(m_chooser);
402 
403  QDialogButtonBox *buttonBox = new QDialogButtonBox(q);
404  buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
407  layout->addWidget(buttonBox);
408 }
409 
410 KMimeTypeChooserDialog::~KMimeTypeChooserDialog()
411 {
412  delete d;
413 }
414 
415 QSize KMimeTypeChooserDialog::sizeHint() const
416 {
418  const int viewableSize = fm.averageCharWidth() * 60;
419  return QSize(viewableSize, viewableSize);
420 }
421 
422 //END KMimeTypeChooserDialog
423 
424 #include "moc_kmimetypechooser.cpp"
QLayout * layout() const const
void doubleClicked(const QModelIndex &index)
KWIDGETSADDONS_EXPORT void editMimeType(const QString &mimeType, QWidget *widget)
Starts the file types editor for a given MIME type.
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
void setContentsMargins(int left, int top, int right, int bottom)
virtual void reject()
void append(const T &value)
void reserve(int alloc)
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QString findExecutable(const QString &executableName, const QStringList &paths)
void textChanged(const QString &text)
Show the MIME type comment (e.g. "HTML Document") in a column.
Show the MIME types glob patterns (e.g. "*.html;*.htm") in a column.
QString tr(const char *sourceText, const char *disambiguation, int n)
QString text() const const
Qt::CheckState checkState() const const
void setBuddy(QWidget *buddy)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void append(const T &value)
void setLayout(QLayout *layout)
void appendRow(const QList< QStandardItem * > &items)
void setFlags(Qt::ItemFlags flags)
bool isEmpty() const const
bool isEmpty() const const
QStandardItem * parent() const const
void clicked(bool checked)
int row() const const
QList< QMimeType > allMimeTypes() const const
QMap::iterator end()
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
virtual void accept()
QStandardItem * child(int row, int column) const const
void setEnabled(bool enabled)
bool isValid() const const
QStringList mimeTypes() const
KMimeTypeChooser(const QString &text=QString(), const QStringList &selectedMimeTypes=QStringList(), const QString &defaultGroup=QString(), const QStringList &groupsToShow=QStringList(), int visuals=Comments|Patterns|EditButton, QWidget *parent=nullptr)
Create a new KMimeTypeChooser.
void activated(const QModelIndex &index)
Show the "Edit" button, allowing to edit the selected type.
QString mid(int position, int n) const const
QFontMetrics fontMetrics() const const
void setStandardButtons(QDialogButtonBox::StandardButtons buttons)
void addStretch(int stretch)
QModelIndex sibling(int row, int column) const const
void setWindowTitle(const QString &)
int column() const const
QString left(int n) const const
KMimeTypeChooser * chooser()
QString fromLatin1(const char *str, int size)
int rowCount() const const
QIcon fromTheme(const QString &name)
QMap::iterator insert(const Key &key, const T &value)
KMimeTypeChooserDialog(const QString &title=QString(), const QString &text=QString(), const QStringList &selectedMimeTypes=QStringList(), const QString &defaultGroup=QString(), const QStringList &groupsToShow=QStringList(), int visuals=KMimeTypeChooser::Comments|KMimeTypeChooser::Patterns|KMimeTypeChooser::EditButton, QWidget *parent=nullptr)
Create a KMimeTypeChooser dialog.
QStringList patterns() const
void setCheckState(Qt::CheckState state)
int averageCharWidth() const const
This widget provides a checkable list of all available MIME types, presented as a treeview...
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
int size() const const
QMap::iterator find(const Key &key)
void addLayout(QLayout *layout, int stretch)
const T value(const Key &key, const T &defaultValue) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sun May 31 2020 22:51:56 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.