1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "mimetypemodel.h" |
52 | |
53 | #include <QDebug> |
54 | #include <QIcon> |
55 | #include <QMimeDatabase> |
56 | #include <QTextStream> |
57 | #include <QVariant> |
58 | |
59 | #include <algorithm> |
60 | |
61 | Q_DECLARE_METATYPE(QMimeType) |
62 | |
63 | typedef QList<QStandardItem *> StandardItemList; |
64 | |
65 | enum { mimeTypeRole = Qt::UserRole + 1, iconQueriedRole = Qt::UserRole + 2 }; |
66 | |
67 | QT_BEGIN_NAMESPACE |
68 | bool operator<(const QMimeType &t1, const QMimeType &t2) |
69 | { |
70 | return t1.name() < t2.name(); |
71 | } |
72 | QT_END_NAMESPACE |
73 | |
74 | static StandardItemList createRow(const QMimeType &t) |
75 | { |
76 | const QVariant v = QVariant::fromValue(value: t); |
77 | QStandardItem *nameItem = new QStandardItem(t.name()); |
78 | const Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; |
79 | nameItem->setData(value: v, role: mimeTypeRole); |
80 | nameItem->setData(value: QVariant(false), role: iconQueriedRole); |
81 | nameItem->setFlags(flags); |
82 | nameItem->setToolTip(t.comment()); |
83 | return StandardItemList{nameItem}; |
84 | } |
85 | |
86 | MimetypeModel::MimetypeModel(QObject *parent) |
87 | : QStandardItemModel(0, ColumnCount, parent) |
88 | { |
89 | setHorizontalHeaderLabels(QStringList{tr(s: "Name" )}); |
90 | populate(); |
91 | } |
92 | |
93 | QVariant MimetypeModel::data(const QModelIndex &index, int role) const |
94 | { |
95 | if (role != Qt::DecorationRole || !index.isValid() || index.data(arole: iconQueriedRole).toBool()) |
96 | return QStandardItemModel::data(index, role); |
97 | QStandardItem *item = itemFromIndex(index); |
98 | const QString iconName = qvariant_cast<QMimeType>(v: item->data(role: mimeTypeRole)).iconName(); |
99 | if (!iconName.isEmpty()) |
100 | item->setIcon(QIcon::fromTheme(name: iconName)); |
101 | item->setData(value: QVariant(true), role: iconQueriedRole); |
102 | return item->icon(); |
103 | } |
104 | |
105 | QMimeType MimetypeModel::mimeType(const QModelIndex &index) const |
106 | { |
107 | return qvariant_cast<QMimeType>(v: index.data(arole: mimeTypeRole)); |
108 | } |
109 | |
110 | void MimetypeModel::populate() |
111 | { |
112 | typedef QList<QMimeType>::Iterator Iterator; |
113 | |
114 | QMimeDatabase mimeDatabase; |
115 | QList<QMimeType> allTypes = mimeDatabase.allMimeTypes(); |
116 | |
117 | // Move top level types to rear end of list, sort this partition, |
118 | // create top level items and truncate the list. |
119 | Iterator end = allTypes.end(); |
120 | const Iterator topLevelStart = |
121 | std::stable_partition(first: allTypes.begin(), last: end, |
122 | pred: [](const QMimeType &t) { return !t.parentMimeTypes().isEmpty(); }); |
123 | std::stable_sort(first: topLevelStart, last: end); |
124 | for (Iterator it = topLevelStart; it != end; ++it) { |
125 | const StandardItemList row = createRow(t: *it); |
126 | appendRow(items: row); |
127 | m_nameIndexHash.insert(akey: it->name(), avalue: indexFromItem(item: row.constFirst())); |
128 | } |
129 | allTypes.erase(afirst: topLevelStart, alast: end); |
130 | |
131 | while (!allTypes.isEmpty()) { |
132 | // Find a type inheriting one that is already in the model. |
133 | end = allTypes.end(); |
134 | auto nameIndexIt = m_nameIndexHash.constEnd(); |
135 | for (Iterator it = allTypes.begin(); it != end; ++it) { |
136 | nameIndexIt = m_nameIndexHash.constFind(akey: it->parentMimeTypes().constFirst()); |
137 | if (nameIndexIt != m_nameIndexHash.constEnd()) |
138 | break; |
139 | } |
140 | if (nameIndexIt == m_nameIndexHash.constEnd()) { |
141 | qWarning() << "Orphaned mime types:" << allTypes; |
142 | break; |
143 | } |
144 | |
145 | // Move types inheriting the parent type to rear end of list, sort this partition, |
146 | // append the items to parent and truncate the list. |
147 | const QString &parentName = nameIndexIt.key(); |
148 | const Iterator start = |
149 | std::stable_partition(first: allTypes.begin(), last: end, pred: [parentName](const QMimeType &t) |
150 | { return !t.parentMimeTypes().contains(str: parentName); }); |
151 | std::stable_sort(first: start, last: end); |
152 | QStandardItem *parentItem = itemFromIndex(index: nameIndexIt.value()); |
153 | for (Iterator it = start; it != end; ++it) { |
154 | const StandardItemList row = createRow(t: *it); |
155 | parentItem->appendRow(aitems: row); |
156 | m_nameIndexHash.insert(akey: it->name(), avalue: indexFromItem(item: row.constFirst())); |
157 | } |
158 | allTypes.erase(afirst: start, alast: end); |
159 | } |
160 | } |
161 | |
162 | QTextStream &operator<<(QTextStream &stream, const QStringList &list) |
163 | { |
164 | for (int i = 0, size = list.size(); i < size; ++i) { |
165 | if (i) |
166 | stream << ", " ; |
167 | stream << list.at(i); |
168 | } |
169 | return stream; |
170 | } |
171 | |
172 | QString MimetypeModel::formatMimeTypeInfo(const QMimeType &t) |
173 | { |
174 | QString result; |
175 | QTextStream str(&result); |
176 | str << "<html><head/><body><h3><center>" << t.name() << "</center></h3><br><table>" ; |
177 | |
178 | const QStringList &aliases = t.aliases(); |
179 | if (!aliases.isEmpty()) |
180 | str << "<tr><td>Aliases:</td><td>" << " (" << aliases << ')'; |
181 | |
182 | str << "</td></tr>" |
183 | << "<tr><td>Comment:</td><td>" << t.comment() << "</td></tr>" |
184 | << "<tr><td>Icon name:</td><td>" << t.iconName() << "</td></tr>" |
185 | << "<tr><td>Generic icon name</td><td>" << t.genericIconName() << "</td></tr>" ; |
186 | |
187 | const QString &filter = t.filterString(); |
188 | if (!filter.isEmpty()) |
189 | str << "<tr><td>Filter:</td><td>" << t.filterString() << "</td></tr>" ; |
190 | |
191 | const QStringList &patterns = t.globPatterns(); |
192 | if (!patterns.isEmpty()) |
193 | str << "<tr><td>Glob patterns:</td><td>" << patterns << "</td></tr>" ; |
194 | |
195 | const QStringList &parentMimeTypes = t.parentMimeTypes(); |
196 | if (!parentMimeTypes.isEmpty()) |
197 | str << "<tr><td>Parent types:</td><td>" << t.parentMimeTypes() << "</td></tr>" ; |
198 | |
199 | QStringList suffixes = t.suffixes(); |
200 | if (!suffixes.isEmpty()) { |
201 | str << "<tr><td>Suffixes:</td><td>" ; |
202 | const QString &preferredSuffix = t.preferredSuffix(); |
203 | if (!preferredSuffix.isEmpty()) { |
204 | suffixes.removeOne(t: preferredSuffix); |
205 | str << "<b>" << preferredSuffix << "</b> " ; |
206 | } |
207 | str << suffixes << "</td></tr>" ; |
208 | } |
209 | str << "</table></body></html>" ; |
210 | return result; |
211 | } |
212 | |