]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinviewcontainer.cpp
fix issues when the split view is active and the inactive column view gets selected...
[dolphin.git] / src / dolphinviewcontainer.cpp
1 /***************************************************************************
2 * Copyright (C) 2007 by Peter Penz <peter.penz@gmx.at> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20 #include "dolphinviewcontainer.h"
21
22 #include <QtGui/QApplication>
23 #include <QtGui/QClipboard>
24 #include <QtGui/QKeyEvent>
25 #include <QtGui/QItemSelection>
26 #include <QtGui/QBoxLayout>
27 #include <QtCore/QTimer>
28 #include <QtGui/QScrollBar>
29
30 #include <kfileitemdelegate.h>
31 #include <kfileplacesmodel.h>
32 #include <kglobalsettings.h>
33 #include <klocale.h>
34 #include <kiconeffect.h>
35 #include <kio/netaccess.h>
36 #include <kio/renamedialog.h>
37 #include <kio/previewjob.h>
38 #include <kmimetyperesolver.h>
39 #include <konqmimedata.h>
40 #include <konq_operations.h>
41 #include <kurl.h>
42
43 #include "dolphinmodel.h"
44 #include "dolphincolumnview.h"
45 #include "dolphincontroller.h"
46 #include "dolphinstatusbar.h"
47 #include "dolphinmainwindow.h"
48 #include "dolphindirlister.h"
49 #include "dolphinsortfilterproxymodel.h"
50 #include "dolphindetailsview.h"
51 #include "dolphiniconsview.h"
52 #include "dolphincontextmenu.h"
53 #include "filterbar.h"
54 #include "renamedialog.h"
55 #include "kurlnavigator.h"
56 #include "viewproperties.h"
57 #include "dolphinsettings.h"
58 #include "dolphin_generalsettings.h"
59
60 DolphinViewContainer::DolphinViewContainer(DolphinMainWindow* mainWindow,
61 QWidget* parent,
62 const KUrl& url) :
63 QWidget(parent),
64 m_showProgress(false),
65 m_folderCount(0),
66 m_fileCount(0),
67 m_mainWindow(mainWindow),
68 m_topLayout(0),
69 m_urlNavigator(0),
70 m_view(0),
71 m_filterBar(0),
72 m_statusBar(0),
73 m_dirLister(0),
74 m_proxyModel(0)
75 {
76 hide();
77 setFocusPolicy(Qt::StrongFocus);
78 m_topLayout = new QVBoxLayout(this);
79 m_topLayout->setSpacing(0);
80 m_topLayout->setMargin(0);
81
82 m_urlNavigator = new KUrlNavigator(DolphinSettings::instance().placesModel(), url, this);
83 connect(m_urlNavigator, SIGNAL(urlsDropped(const KUrl::List&, const KUrl&)),
84 m_mainWindow, SLOT(dropUrls(const KUrl::List&, const KUrl&)));
85 connect(m_urlNavigator, SIGNAL(activated()),
86 this, SLOT(activate()));
87
88 const GeneralSettings* settings = DolphinSettings::instance().generalSettings();
89 m_urlNavigator->setUrlEditable(settings->editableUrl());
90 m_urlNavigator->setHomeUrl(settings->homeUrl());
91
92 m_dirLister = new DolphinDirLister();
93 m_dirLister->setAutoUpdate(true);
94 m_dirLister->setMainWindow(this);
95 m_dirLister->setDelayedMimeTypes(true);
96
97 m_dolphinModel = new DolphinModel(this);
98 m_dolphinModel->setDirLister(m_dirLister);
99 m_dolphinModel->setDropsAllowed(DolphinModel::DropOnDirectory);
100
101 m_proxyModel = new DolphinSortFilterProxyModel(this);
102 m_proxyModel->setSourceModel(m_dolphinModel);
103
104 connect(m_dirLister, SIGNAL(clear()),
105 this, SLOT(updateStatusBar()));
106 connect(m_dirLister, SIGNAL(percent(int)),
107 this, SLOT(updateProgress(int)));
108 connect(m_dirLister, SIGNAL(deleteItem(const KFileItem&)),
109 this, SLOT(updateStatusBar()));
110 connect(m_dirLister, SIGNAL(completed()),
111 this, SLOT(updateItemCount()));
112 connect(m_dirLister, SIGNAL(infoMessage(const QString&)),
113 this, SLOT(showInfoMessage(const QString&)));
114 connect(m_dirLister, SIGNAL(errorMessage(const QString&)),
115 this, SLOT(showErrorMessage(const QString&)));
116
117 m_view = new DolphinView(this,
118 url,
119 m_dirLister,
120 m_dolphinModel,
121 m_proxyModel);
122 connect(m_view, SIGNAL(urlChanged(const KUrl&)),
123 m_urlNavigator, SLOT(setUrl(const KUrl&)));
124 connect(m_view, SIGNAL(requestContextMenu(KFileItem, const KUrl&)),
125 this, SLOT(openContextMenu(KFileItem, const KUrl&)));
126 connect(m_view, SIGNAL(urlsDropped(const KUrl::List&, const KUrl&)),
127 m_mainWindow, SLOT(dropUrls(const KUrl::List&, const KUrl&)));
128 connect(m_view, SIGNAL(contentsMoved(int, int)),
129 this, SLOT(saveContentsPos(int, int)));
130 connect(m_view, SIGNAL(requestItemInfo(KFileItem)),
131 this, SLOT(showItemInfo(KFileItem)));
132 connect(m_view, SIGNAL(errorMessage(const QString&)),
133 this, SLOT(showErrorMessage(const QString&)));
134 connect(m_view, SIGNAL(infoMessage(const QString&)),
135 this, SLOT(showInfoMessage(const QString&)));
136 connect(m_view, SIGNAL(itemTriggered(KFileItem)),
137 this, SLOT(slotItemTriggered(KFileItem)));
138 connect(m_view, SIGNAL(startedPathLoading(const KUrl&)),
139 this, SLOT(saveRootUrl(const KUrl&)));
140
141 connect(m_urlNavigator, SIGNAL(urlChanged(const KUrl&)),
142 this, SLOT(restoreView(const KUrl&)));
143
144 m_statusBar = new DolphinStatusBar(this, url);
145 connect(m_view, SIGNAL(urlChanged(const KUrl&)),
146 m_statusBar, SLOT(updateSpaceInfoContent(const KUrl&)));
147
148 m_filterBar = new FilterBar(this);
149 m_filterBar->setVisible(settings->filterBar());
150 connect(m_filterBar, SIGNAL(filterChanged(const QString&)),
151 this, SLOT(changeNameFilter(const QString&)));
152 connect(m_filterBar, SIGNAL(closeRequest()),
153 this, SLOT(closeFilterBar()));
154
155 m_topLayout->addWidget(m_urlNavigator);
156 m_topLayout->addWidget(m_view);
157 m_topLayout->addWidget(m_filterBar);
158 m_topLayout->addWidget(m_statusBar);
159 }
160
161 DolphinViewContainer::~DolphinViewContainer()
162 {
163 delete m_dirLister;
164 m_dirLister = 0;
165 }
166
167 void DolphinViewContainer::setUrl(const KUrl& url)
168 {
169 m_urlNavigator->setUrl(url);
170 }
171
172 const KUrl& DolphinViewContainer::url() const
173 {
174 return m_urlNavigator->url();
175 }
176
177 void DolphinViewContainer::setActive(bool active)
178 {
179 m_urlNavigator->setActive(active);
180 m_view->setActive(active);
181 }
182
183 bool DolphinViewContainer::isActive() const
184 {
185 Q_ASSERT(m_view->isActive() == m_urlNavigator->isActive());
186 return m_view->isActive();
187 }
188
189 void DolphinViewContainer::renameSelectedItems()
190 {
191 DolphinViewContainer* view = m_mainWindow->activeViewContainer();
192 const KFileItemList items = m_view->selectedItems();
193 if (items.count() > 1) {
194 // More than one item has been selected for renaming. Open
195 // a rename dialog and rename all items afterwards.
196 RenameDialog dialog(items);
197 if (dialog.exec() == QDialog::Rejected) {
198 return;
199 }
200
201 const QString& newName = dialog.newName();
202 if (newName.isEmpty()) {
203 view->statusBar()->setMessage(dialog.errorString(),
204 DolphinStatusBar::Error);
205 } else {
206 // TODO: check how this can be integrated into KonqUndoManager/KonqOperations
207 // as one operation instead of n rename operations like it is done now...
208 Q_ASSERT(newName.contains('#'));
209
210 // iterate through all selected items and rename them...
211 const int replaceIndex = newName.indexOf('#');
212 Q_ASSERT(replaceIndex >= 0);
213 int index = 1;
214
215 KFileItemList::const_iterator it = items.begin();
216 KFileItemList::const_iterator end = items.end();
217 while (it != end) {
218 const KUrl& oldUrl = (*it).url();
219 QString number;
220 number.setNum(index++);
221
222 QString name(newName);
223 name.replace(replaceIndex, 1, number);
224
225 if (oldUrl.fileName() != name) {
226 KUrl newUrl = oldUrl;
227 newUrl.setFileName(name);
228 m_mainWindow->rename(oldUrl, newUrl);
229 }
230 ++it;
231 }
232 }
233 } else {
234 // Only one item has been selected for renaming. Use the custom
235 // renaming mechanism from the views.
236 Q_ASSERT(items.count() == 1);
237
238 // TODO: Think about using KFileItemDelegate as soon as it supports editing.
239 // Currently the RenameDialog is used, but I'm not sure whether inline renaming
240 // is a benefit for the user at all -> let's wait for some input first...
241 RenameDialog dialog(items);
242 if (dialog.exec() == QDialog::Rejected) {
243 return;
244 }
245
246 const QString& newName = dialog.newName();
247 if (newName.isEmpty()) {
248 view->statusBar()->setMessage(dialog.errorString(),
249 DolphinStatusBar::Error);
250 } else {
251 const KUrl& oldUrl = items.first().url();
252 KUrl newUrl = oldUrl;
253 newUrl.setFileName(newName);
254 m_mainWindow->rename(oldUrl, newUrl);
255 }
256 }
257 }
258
259 bool DolphinViewContainer::isFilterBarVisible() const
260 {
261 return m_filterBar->isVisible();
262 }
263
264 bool DolphinViewContainer::isUrlEditable() const
265 {
266 return m_urlNavigator->isUrlEditable();
267 }
268
269 KFileItem DolphinViewContainer::fileItem(const QModelIndex& index) const
270 {
271 const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
272 return m_dolphinModel->itemForIndex(dolphinModelIndex);
273 }
274
275 void DolphinViewContainer::updateProgress(int percent)
276 {
277 if (!m_showProgress) {
278 // Only show the directory loading progress if the status bar does
279 // not contain another progress information. This means that
280 // the directory loading progress information has the lowest priority.
281 const QString progressText(m_statusBar->progressText());
282 const QString loadingText(i18nc("@info:progress", "Loading folder..."));
283 m_showProgress = progressText.isEmpty() || (progressText == loadingText);
284 if (m_showProgress) {
285 m_statusBar->setProgressText(loadingText);
286 m_statusBar->setProgress(0);
287 }
288 }
289
290 if (m_showProgress) {
291 m_statusBar->setProgress(percent);
292 }
293 }
294
295 void DolphinViewContainer::updateItemCount()
296 {
297 if (m_showProgress) {
298 m_statusBar->setProgressText(QString());
299 m_statusBar->setProgress(100);
300 m_showProgress = false;
301 }
302
303 KFileItemList items(m_dirLister->items());
304 KFileItemList::const_iterator it = items.begin();
305 const KFileItemList::const_iterator end = items.end();
306
307 m_fileCount = 0;
308 m_folderCount = 0;
309
310 while (it != end) {
311 const KFileItem item = *it;
312 if (item.isDir()) {
313 ++m_folderCount;
314 } else {
315 ++m_fileCount;
316 }
317 ++it;
318 }
319
320 updateStatusBar();
321
322 QTimer::singleShot(100, this, SLOT(restoreContentsPos()));
323 }
324
325 void DolphinViewContainer::showItemInfo(const KFileItem& item)
326 {
327 if (item.isNull()) {
328 m_statusBar->clear();
329 } else {
330 m_statusBar->setMessage(item.getStatusBarInfo(), DolphinStatusBar::Default);
331 }
332 }
333
334 void DolphinViewContainer::showInfoMessage(const QString& msg)
335 {
336 m_statusBar->setMessage(msg, DolphinStatusBar::Information);
337 }
338
339 void DolphinViewContainer::showErrorMessage(const QString& msg)
340 {
341 m_statusBar->setMessage(msg, DolphinStatusBar::Error);
342 }
343
344 void DolphinViewContainer::closeFilterBar()
345 {
346 m_filterBar->hide();
347 emit showFilterBarChanged(false);
348 }
349
350 QString DolphinViewContainer::defaultStatusBarText() const
351 {
352 int m_fileCount = 0;
353 int m_folderCount = 0;
354
355 for (int i = 0; i < m_proxyModel->rowCount(); i++)
356 {
357 if (m_dolphinModel->itemForIndex(m_proxyModel->mapToSource(m_proxyModel->index(i, m_proxyModel->sortColumn()))).isDir())
358 {
359 m_folderCount++;
360 }
361 else
362 {
363 m_fileCount++;
364 }
365 }
366
367 return KIO::itemsSummaryString(m_fileCount + m_folderCount,
368 m_fileCount,
369 m_folderCount,
370 0, false);
371 }
372
373 QString DolphinViewContainer::selectionStatusBarText() const
374 {
375 QString text;
376 const KFileItemList list = m_view->selectedItems();
377 if (list.isEmpty()) {
378 // when an item is triggered, it is temporary selected but selectedItems()
379 // will return an empty list
380 return QString();
381 }
382
383 int fileCount = 0;
384 int folderCount = 0;
385 KIO::filesize_t byteSize = 0;
386 KFileItemList::const_iterator it = list.begin();
387 const KFileItemList::const_iterator end = list.end();
388 while (it != end) {
389 const KFileItem& item = *it;
390 if (item.isDir()) {
391 ++folderCount;
392 } else {
393 ++fileCount;
394 byteSize += item.size();
395 }
396 ++it;
397 }
398
399 if (folderCount > 0) {
400 text = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount);
401 if (fileCount > 0) {
402 text += ", ";
403 }
404 }
405
406 if (fileCount > 0) {
407 const QString sizeText(KIO::convertSize(byteSize));
408 text += i18ncp("@info:status", "1 File selected (%2)", "%1 Files selected (%2)", fileCount, sizeText);
409 }
410
411 return text;
412 }
413
414 void DolphinViewContainer::showFilterBar(bool show)
415 {
416 Q_ASSERT(m_filterBar != 0);
417 m_filterBar->setVisible(show);
418 }
419
420 void DolphinViewContainer::updateStatusBar()
421 {
422 // As the item count information is less important
423 // in comparison with other messages, it should only
424 // be shown if:
425 // - the status bar is empty or
426 // - shows already the item count information or
427 // - shows only a not very important information
428 // - if any progress is given don't show the item count info at all
429 const QString msg(m_statusBar->message());
430 const bool updateStatusBarMsg = (msg.isEmpty() ||
431 (msg == m_statusBar->defaultText()) ||
432 (m_statusBar->type() == DolphinStatusBar::Information)) &&
433 (m_statusBar->progress() == 100);
434
435 const QString text(m_view->hasSelection() ? selectionStatusBarText() : defaultStatusBarText());
436 m_statusBar->setDefaultText(text);
437
438 if (updateStatusBarMsg) {
439 m_statusBar->setMessage(text, DolphinStatusBar::Default);
440 }
441 }
442
443 void DolphinViewContainer::changeNameFilter(const QString& nameFilter)
444 {
445 // The name filter of KDirLister does a 'hard' filtering, which
446 // means that only the items are shown where the names match
447 // exactly the filter. This is non-transparent for the user, which
448 // just wants to have a 'soft' filtering: does the name contain
449 // the filter string?
450 QString adjustedFilter(nameFilter);
451 adjustedFilter.insert(0, '*');
452 adjustedFilter.append('*');
453
454 m_dirLister->setNameFilter(adjustedFilter);
455 m_dirLister->emitChanges();
456
457 updateStatusBar();
458 }
459
460 void DolphinViewContainer::openContextMenu(const KFileItem& item,
461 const KUrl& url)
462 {
463 DolphinContextMenu contextMenu(m_mainWindow, item, url);
464 contextMenu.open();
465 }
466
467 void DolphinViewContainer::saveContentsPos(int x, int y)
468 {
469 m_urlNavigator->savePosition(x, y);
470 }
471
472 void DolphinViewContainer::restoreContentsPos()
473 {
474 if (!url().isEmpty()) {
475 const QPoint pos = m_urlNavigator->savedPosition();
476 m_view->setContentsPosition(pos.x(), pos.y());
477 }
478 }
479
480 void DolphinViewContainer::activate()
481 {
482 setActive(true);
483 }
484
485 void DolphinViewContainer::restoreView(const KUrl& url)
486 {
487 m_view->updateView(url, m_urlNavigator->savedRootUrl());
488 }
489
490 void DolphinViewContainer::saveRootUrl(const KUrl& url)
491 {
492 Q_UNUSED(url);
493 m_urlNavigator->saveRootUrl(m_view->rootUrl());
494 }
495
496 void DolphinViewContainer::slotItemTriggered(const KFileItem& item)
497 {
498 // Prefer the local path over the URL.
499 bool isLocal;
500 KUrl url = item.mostLocalUrl(isLocal);
501
502 if (item.isDir()) {
503 m_view->setUrl(url);
504 } else if (item.isFile()) {
505 // allow to browse through ZIP and tar files
506 KMimeType::Ptr mime = item.mimeTypePtr();
507 if (mime->is("application/zip")) {
508 url.setProtocol("zip");
509 m_view->setUrl(url);
510 } else if (mime->is("application/x-tar") ||
511 mime->is("application/x-tarz") ||
512 mime->is("application/x-bzip-compressed-tar") ||
513 mime->is("application/x-compressed-tar") ||
514 mime->is("application/x-tzo")) {
515 url.setProtocol("tar");
516 m_view->setUrl(url);
517 } else {
518 item.run();
519 }
520 } else {
521 item.run();
522 }
523 }
524
525 #include "dolphinviewcontainer.moc"