]> cloud.milkyroute.net Git - dolphin.git/blob - src/views/viewproperties.cpp
Allow to remember view-properties for search-results
[dolphin.git] / src / views / viewproperties.cpp
1 /***************************************************************************
2 * Copyright (C) 2006-2010 by Peter Penz <peter.penz19@gmail.com> *
3 * Copyright (C) 2006 by Aaron J. Seigo <aseigo@kde.org> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
19 ***************************************************************************/
20
21 #include "viewproperties.h"
22
23 #include "dolphin_directoryviewpropertysettings.h"
24 #include "dolphin_generalsettings.h"
25
26 #include <KComponentData>
27 #include <KLocale>
28 #include <KStandardDirs>
29 #include <KUrl>
30
31 #include <QCryptographicHash>
32 #include <QDate>
33 #include <QFile>
34 #include <QFileInfo>
35
36 namespace {
37 const int AdditionalInfoViewPropertiesVersion = 1;
38 const int NameRolePropertiesVersion = 2;
39 const int CurrentViewPropertiesVersion = 3;
40
41 // String representation to mark the additional properties of
42 // the details view as customized by the user. See
43 // ViewProperties::visibleRoles() for more information.
44 const char* CustomizedDetailsString = "CustomizedDetails";
45 }
46
47 ViewProperties::ViewProperties(const KUrl& url) :
48 m_changedProps(false),
49 m_autoSave(true),
50 m_node(0)
51 {
52 GeneralSettings* settings = GeneralSettings::self();
53 const bool useGlobalViewProps = settings->globalViewProps();
54 bool useDetailsViewWithPath = false;
55
56 // We try and save it to the file .directory in the directory being viewed.
57 // If the directory is not writable by the user or the directory is not local,
58 // we store the properties information in a local file.
59 if (useGlobalViewProps) {
60 m_filePath = destinationDir("global");
61 } else if (url.protocol().contains("search")) {
62 m_filePath = destinationDir("search/") + directoryHashForUrl(url);
63 useDetailsViewWithPath = true;
64 } else if (url.protocol() == QLatin1String("trash")) {
65 m_filePath = destinationDir("trash");
66 useDetailsViewWithPath = true;
67 } else if (url.isLocalFile()) {
68 m_filePath = url.toLocalFile();
69 const QFileInfo info(m_filePath);
70 if (!info.isWritable() || !isPartOfHome(m_filePath)) {
71 #ifdef Q_OS_WIN
72 // m_filePath probably begins with C:/ - the colon is not a valid character for paths though
73 m_filePath = QDir::separator() + m_filePath.remove(QLatin1Char(':'));
74 #endif
75 m_filePath = destinationDir("local") + m_filePath;
76 }
77 } else {
78 m_filePath = destinationDir("remote") + m_filePath;
79 }
80
81 const QString file = m_filePath + QDir::separator() + QLatin1String(".directory");
82 m_node = new ViewPropertySettings(KSharedConfig::openConfig(file));
83
84 // If the .directory file does not exist or the timestamp is too old,
85 // use default values instead.
86 const bool useDefaultProps = (!useGlobalViewProps || useDetailsViewWithPath) &&
87 (!QFileInfo(file).exists() ||
88 (m_node->timestamp() < settings->viewPropsTimestamp()));
89 if (useDefaultProps) {
90 if (useDetailsViewWithPath) {
91 setViewMode(DolphinView::DetailsView);
92 setVisibleRoles(QList<QByteArray>() << "path");
93 } else {
94 // The global view-properties act as default for directories without
95 // any view-property configuration
96 settings->setGlobalViewProps(true);
97
98 ViewProperties defaultProps(url);
99 setDirProperties(defaultProps);
100
101 settings->setGlobalViewProps(false);
102 m_changedProps = false;
103 }
104 }
105 }
106
107 ViewProperties::~ViewProperties()
108 {
109 if (m_changedProps && m_autoSave) {
110 save();
111 }
112
113 delete m_node;
114 m_node = 0;
115 }
116
117 void ViewProperties::setViewMode(DolphinView::Mode mode)
118 {
119 if (m_node->viewMode() != mode) {
120 m_node->setViewMode(mode);
121 update();
122 }
123 }
124
125 DolphinView::Mode ViewProperties::viewMode() const
126 {
127 const int mode = qBound(0, m_node->viewMode(), 2);
128 return static_cast<DolphinView::Mode>(mode);
129 }
130
131 void ViewProperties::setPreviewsShown(bool show)
132 {
133 if (m_node->previewsShown() != show) {
134 m_node->setPreviewsShown(show);
135 update();
136 }
137 }
138
139 bool ViewProperties::previewsShown() const
140 {
141 return m_node->previewsShown();
142 }
143
144 void ViewProperties::setHiddenFilesShown(bool show)
145 {
146 if (m_node->hiddenFilesShown() != show) {
147 m_node->setHiddenFilesShown(show);
148 update();
149 }
150 }
151
152 void ViewProperties::setGroupedSorting(bool grouped)
153 {
154 if (m_node->groupedSorting() != grouped) {
155 m_node->setGroupedSorting(grouped);
156 update();
157 }
158 }
159
160 bool ViewProperties::groupedSorting() const
161 {
162 return m_node->groupedSorting();
163 }
164
165 bool ViewProperties::hiddenFilesShown() const
166 {
167 return m_node->hiddenFilesShown();
168 }
169
170 void ViewProperties::setSortRole(const QByteArray& role)
171 {
172 if (m_node->sortRole() != role) {
173 m_node->setSortRole(role);
174 update();
175 }
176 }
177
178 QByteArray ViewProperties::sortRole() const
179 {
180 return m_node->sortRole().toLatin1();
181 }
182
183 void ViewProperties::setSortOrder(Qt::SortOrder sortOrder)
184 {
185 if (m_node->sortOrder() != sortOrder) {
186 m_node->setSortOrder(sortOrder);
187 update();
188 }
189 }
190
191 Qt::SortOrder ViewProperties::sortOrder() const
192 {
193 return static_cast<Qt::SortOrder>(m_node->sortOrder());
194 }
195
196 void ViewProperties::setSortFoldersFirst(bool foldersFirst)
197 {
198 if (m_node->sortFoldersFirst() != foldersFirst) {
199 m_node->setSortFoldersFirst(foldersFirst);
200 update();
201 }
202 }
203
204 bool ViewProperties::sortFoldersFirst() const
205 {
206 return m_node->sortFoldersFirst();
207 }
208
209 void ViewProperties::setVisibleRoles(const QList<QByteArray>& roles)
210 {
211 // See ViewProperties::visibleRoles() for the storage format
212 // of the additional information.
213
214 // Remove the old values stored for the current view-mode
215 const QStringList oldVisibleRoles = m_node->visibleRoles();
216 const QString prefix = viewModePrefix();
217 QStringList newVisibleRoles = oldVisibleRoles;
218 for (int i = newVisibleRoles.count() - 1; i >= 0; --i) {
219 if (newVisibleRoles[i].startsWith(prefix)) {
220 newVisibleRoles.removeAt(i);
221 }
222 }
223
224 // Add the updated values for the current view-mode
225 foreach (const QByteArray& role, roles) {
226 newVisibleRoles.append(prefix + role);
227 }
228
229 if (oldVisibleRoles != newVisibleRoles) {
230 const bool markCustomizedDetails = (m_node->viewMode() == DolphinView::DetailsView)
231 && !newVisibleRoles.contains(CustomizedDetailsString);
232 if (markCustomizedDetails) {
233 // The additional information of the details-view has been modified. Set a marker,
234 // so that it is allowed to also show no additional information without doing the
235 // fallback to show the size and date per default.
236 newVisibleRoles.append(CustomizedDetailsString);
237 }
238
239 m_node->setVisibleRoles(newVisibleRoles);
240 update();
241 }
242 }
243
244 QList<QByteArray> ViewProperties::visibleRoles() const
245 {
246 // The shown additional information is stored for each view-mode separately as
247 // string with the view-mode as prefix. Example:
248 //
249 // AdditionalInfo=Details_size,Details_date,Details_owner,Icons_size
250 //
251 // To get the representation as QList<QByteArray>, the current
252 // view-mode must be checked and the values of this mode added to the list.
253 //
254 // For the details-view a special case must be respected: Per default the size
255 // and date should be shown without creating a .directory file. Only if
256 // the user explictly has modified the properties of the details view (marked
257 // by "CustomizedDetails"), also a details-view with no additional information
258 // is accepted.
259
260 QList<QByteArray> roles;
261 roles.append("text");
262
263 // Iterate through all stored keys and append all roles that match to
264 // the curren view mode.
265 const QString prefix = viewModePrefix();
266 const int prefixLength = prefix.length();
267
268 QStringList visibleRoles = m_node->visibleRoles();
269 const int version = m_node->version();
270 if (visibleRoles.isEmpty() && version <= AdditionalInfoViewPropertiesVersion) {
271 // Convert the obsolete additionalInfo-property from older versions into the
272 // visibleRoles-property
273 visibleRoles = const_cast<ViewProperties*>(this)->convertAdditionalInfo();
274 } else if (version <= NameRolePropertiesVersion) {
275 visibleRoles = const_cast<ViewProperties*>(this)->convertNameRole();
276 }
277
278 foreach (const QString& visibleRole, visibleRoles) {
279 if (visibleRole.startsWith(prefix)) {
280 const QByteArray role = visibleRole.right(visibleRole.length() - prefixLength).toLatin1();
281 if (role != "text") {
282 roles.append(role);
283 }
284 }
285 }
286
287 // For the details view the size and date should be shown per default
288 // until the additional information has been explicitly changed by the user
289 const bool useDefaultValues = roles.count() == 1 // "text"
290 && (m_node->viewMode() == DolphinView::DetailsView)
291 && !visibleRoles.contains(CustomizedDetailsString);
292 if (useDefaultValues) {
293 roles.append("size");
294 roles.append("date");
295 }
296
297 return roles;
298 }
299
300 void ViewProperties::setHeaderColumnWidths(const QList<int>& widths)
301 {
302 if (m_node->headerColumnWidths() != widths) {
303 m_node->setHeaderColumnWidths(widths);
304 update();
305 }
306 }
307
308 QList<int> ViewProperties::headerColumnWidths() const
309 {
310 return m_node->headerColumnWidths();
311 }
312
313 void ViewProperties::setDirProperties(const ViewProperties& props)
314 {
315 setViewMode(props.viewMode());
316 setPreviewsShown(props.previewsShown());
317 setHiddenFilesShown(props.hiddenFilesShown());
318 setGroupedSorting(props.groupedSorting());
319 setSortRole(props.sortRole());
320 setSortOrder(props.sortOrder());
321 setSortFoldersFirst(props.sortFoldersFirst());
322 setVisibleRoles(props.visibleRoles());
323 setHeaderColumnWidths(props.headerColumnWidths());
324 }
325
326 void ViewProperties::setAutoSaveEnabled(bool autoSave)
327 {
328 m_autoSave = autoSave;
329 }
330
331 bool ViewProperties::isAutoSaveEnabled() const
332 {
333 return m_autoSave;
334 }
335
336 void ViewProperties::update()
337 {
338 m_changedProps = true;
339 m_node->setTimestamp(QDateTime::currentDateTime());
340 }
341
342 void ViewProperties::save()
343 {
344 KStandardDirs::makeDir(m_filePath);
345 m_node->setVersion(CurrentViewPropertiesVersion);
346 m_node->writeConfig();
347 m_changedProps = false;
348 }
349
350 KUrl ViewProperties::mirroredDirectory()
351 {
352 QString basePath = KGlobal::mainComponent().componentName();
353 basePath.append("/view_properties/");
354 return KUrl(KStandardDirs::locateLocal("data", basePath));
355 }
356
357 QString ViewProperties::destinationDir(const QString& subDir) const
358 {
359 QString basePath = KGlobal::mainComponent().componentName();
360 basePath.append("/view_properties/").append(subDir);
361 return KStandardDirs::locateLocal("data", basePath);
362 }
363
364 QString ViewProperties::viewModePrefix() const
365 {
366 QString prefix;
367
368 switch (m_node->viewMode()) {
369 case DolphinView::IconsView: prefix = "Icons_"; break;
370 case DolphinView::CompactView: prefix = "Compact_"; break;
371 case DolphinView::DetailsView: prefix = "Details_"; break;
372 default: kWarning() << "Unknown view-mode of the view properties";
373 }
374
375 return prefix;
376 }
377
378 QStringList ViewProperties::convertAdditionalInfo()
379 {
380 QStringList visibleRoles;
381
382 const QStringList additionalInfo = m_node->additionalInfo();
383 if (!additionalInfo.isEmpty()) {
384 // Convert the obsolete values like Icons_Size, Details_Date, ...
385 // to Icons_size, Details_date, ... where the suffix just represents
386 // the internal role. One special-case must be handled: "LinkDestination"
387 // has been used for "destination".
388 visibleRoles.reserve(additionalInfo.count());
389 foreach (const QString& info, additionalInfo) {
390 QString visibleRole = info;
391 int index = visibleRole.indexOf('_');
392 if (index >= 0 && index + 1 < visibleRole.length()) {
393 ++index;
394 if (visibleRole[index] == QLatin1Char('L')) {
395 visibleRole.replace("LinkDestination", "destination");
396 } else {
397 visibleRole[index] = visibleRole[index].toLower();
398 }
399 }
400 visibleRoles.append(visibleRole);
401 }
402 }
403
404 m_node->setAdditionalInfo(QStringList());
405 m_node->setVisibleRoles(visibleRoles);
406 update();
407
408 return visibleRoles;
409 }
410
411 QStringList ViewProperties::convertNameRole()
412 {
413 QStringList visibleRoles = m_node->visibleRoles();
414 for (int i = 0; i < visibleRoles.count(); ++i) {
415 if (visibleRoles[i].endsWith("_name")) {
416 const int leftLength = visibleRoles[i].length() - 5;
417 visibleRoles[i] = visibleRoles[i].left(leftLength) + "_text";
418 }
419 }
420
421 m_node->setVisibleRoles(visibleRoles);
422 update();
423
424 return visibleRoles;
425 }
426
427
428 bool ViewProperties::isPartOfHome(const QString& filePath)
429 {
430 // For performance reasons cache the path in a static QString
431 // (see QDir::homePath() for more details)
432 static QString homePath;
433 if (homePath.isEmpty()) {
434 homePath = QDir::homePath();
435 Q_ASSERT(!homePath.isEmpty());
436 }
437
438 return filePath.startsWith(homePath);
439 }
440
441 QString ViewProperties::directoryHashForUrl(const KUrl& url)
442 {
443 const QByteArray hashValue = QCryptographicHash::hash(url.prettyUrl().toLatin1(),
444 QCryptographicHash::Sha1);
445 QString hashString = hashValue.toBase64();
446 hashString.replace('/', '-');
447 return hashString;
448 }