]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinviewcontainer.cpp
Fix issue that the enabled state of the "Create New..." menu is not updated correctly...
[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 #include <kprotocolmanager.h>
22
23 #include <QtGui/QApplication>
24 #include <QtGui/QKeyEvent>
25 #include <QtGui/QItemSelection>
26 #include <QtGui/QBoxLayout>
27 #include <QtCore/QTimer>
28 #include <QtGui/QScrollBar>
29
30 #include <kdesktopfile.h>
31 #include <kfileitemdelegate.h>
32 #include <kfileplacesmodel.h>
33 #include <kglobalsettings.h>
34 #include <klocale.h>
35 #include <kiconeffect.h>
36 #include <kio/netaccess.h>
37 #include <kio/previewjob.h>
38 #include <kmenu.h>
39 #include <knewmenu.h>
40 #include <konqmimedata.h>
41 #include <konq_operations.h>
42 #include <kshell.h>
43 #include <kurl.h>
44 #include <kurlcombobox.h>
45 #include <kurlnavigator.h>
46 #include <krun.h>
47
48 #include "dolphin_generalsettings.h"
49 #include "dolphinmainwindow.h"
50 #include "filterbar/filterbar.h"
51 #include "search/dolphinsearchbox.h"
52 #include "settings/dolphinsettings.h"
53 #include "statusbar/dolphinstatusbar.h"
54 #include "views/dolphincolumnview.h"
55 #include "views/dolphindetailsview.h"
56 #include "views/dolphindirlister.h"
57 #include "views/dolphinsortfilterproxymodel.h"
58 #include "views/draganddrophelper.h"
59 #include "views/dolphiniconsview.h"
60 #include "views/dolphinmodel.h"
61 #include "views/dolphinviewcontroller.h"
62 #include "views/viewmodecontroller.h"
63 #include "views/viewproperties.h"
64
65 DolphinViewContainer::DolphinViewContainer(const KUrl& url, QWidget* parent) :
66 QWidget(parent),
67 m_topLayout(0),
68 m_urlNavigator(0),
69 m_searchBox(0),
70 m_view(0),
71 m_filterBar(0),
72 m_statusBar(0),
73 m_statusBarTimer(0),
74 m_statusBarTimestamp(),
75 m_dirLister(0),
76 m_proxyModel(0)
77 {
78 hide();
79
80 m_topLayout = new QVBoxLayout(this);
81 m_topLayout->setSpacing(0);
82 m_topLayout->setMargin(0);
83
84 m_urlNavigator = new KUrlNavigator(DolphinSettings::instance().placesModel(), url, this);
85 connect(m_urlNavigator, SIGNAL(urlsDropped(const KUrl&, QDropEvent*)),
86 this, SLOT(dropUrls(const KUrl&, QDropEvent*)));
87 connect(m_urlNavigator, SIGNAL(activated()),
88 this, SLOT(activate()));
89 connect(m_urlNavigator->editor(), SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
90 this, SLOT(saveUrlCompletionMode(KGlobalSettings::Completion)));
91
92 const GeneralSettings* settings = DolphinSettings::instance().generalSettings();
93 m_urlNavigator->setUrlEditable(settings->editableUrl());
94 m_urlNavigator->setShowFullPath(settings->showFullPath());
95 m_urlNavigator->setHomeUrl(KUrl(settings->homeUrl()));
96 KUrlComboBox* editor = m_urlNavigator->editor();
97 editor->setCompletionMode(KGlobalSettings::Completion(settings->urlCompletionMode()));
98
99 m_searchBox = new DolphinSearchBox(this);
100 m_searchBox->hide();
101 connect(m_searchBox, SIGNAL(closeRequest()), this, SLOT(closeSearchBox()));
102 connect(m_searchBox, SIGNAL(search(QString)), this, SLOT(startSearching(QString)));
103 connect(m_searchBox, SIGNAL(returnPressed(QString)), this, SLOT(requestFocus()));
104
105 m_dirLister = new DolphinDirLister();
106 m_dirLister->setAutoUpdate(true);
107 m_dirLister->setMainWindow(window());
108 m_dirLister->setDelayedMimeTypes(true);
109
110 m_dolphinModel = new DolphinModel(this);
111 m_dolphinModel->setDirLister(m_dirLister); // m_dolphinModel takes ownership of m_dirLister
112 m_dolphinModel->setDropsAllowed(DolphinModel::DropOnDirectory);
113
114 m_proxyModel = new DolphinSortFilterProxyModel(this);
115 m_proxyModel->setSourceModel(m_dolphinModel);
116 m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
117
118 // TODO: In the case of the column view the directory lister changes. Let the DolphinView
119 // inform the container about this information for KDE SC 4.7
120 connect(m_dirLister, SIGNAL(clear()),
121 this, SLOT(delayedStatusBarUpdate()));
122 connect(m_dirLister, SIGNAL(percent(int)),
123 this, SLOT(updateProgress(int)));
124 connect(m_dirLister, SIGNAL(itemsDeleted(const KFileItemList&)),
125 this, SLOT(delayedStatusBarUpdate()));
126 connect(m_dirLister, SIGNAL(newItems(KFileItemList)),
127 this, SLOT(delayedStatusBarUpdate()));
128 connect(m_dirLister, SIGNAL(infoMessage(const QString&)),
129 this, SLOT(showInfoMessage(const QString&)));
130 connect(m_dirLister, SIGNAL(errorMessage(const QString&)),
131 this, SLOT(showErrorMessage(const QString&)));
132 connect(m_dirLister, SIGNAL(urlIsFileError(const KUrl&)),
133 this, SLOT(openFile(const KUrl&)));
134
135 m_view = new DolphinView(this, url, m_proxyModel);
136 connect(m_view, SIGNAL(urlChanged(const KUrl&)),
137 m_urlNavigator, SLOT(setUrl(const KUrl&)));
138 connect(m_view, SIGNAL(requestItemInfo(KFileItem)),
139 this, SLOT(showItemInfo(KFileItem)));
140 connect(m_view, SIGNAL(errorMessage(const QString&)),
141 this, SLOT(showErrorMessage(const QString&)));
142 connect(m_view, SIGNAL(infoMessage(const QString&)),
143 this, SLOT(showInfoMessage(const QString&)));
144 connect(m_view, SIGNAL(operationCompletedMessage(const QString&)),
145 this, SLOT(showOperationCompletedMessage(const QString&)));
146 connect(m_view, SIGNAL(itemTriggered(KFileItem)),
147 this, SLOT(slotItemTriggered(KFileItem)));
148 connect(m_view, SIGNAL(redirection(KUrl, KUrl)),
149 this, SLOT(redirect(KUrl, KUrl)));
150 connect(m_view, SIGNAL(selectionChanged(const KFileItemList&)),
151 this, SLOT(delayedStatusBarUpdate()));
152 connect(m_view, SIGNAL(startedPathLoading(KUrl)),
153 this, SLOT(slotStartedPathLoading()));
154 connect(m_view, SIGNAL(finishedPathLoading(KUrl)),
155 this, SLOT(slotFinishedPathLoading()));
156 connect(m_view, SIGNAL(writeStateChanged(bool)),
157 this, SIGNAL(writeStateChanged(bool)));
158
159 connect(m_urlNavigator, SIGNAL(urlChanged(const KUrl&)),
160 this, SLOT(slotUrlNavigatorLocationChanged(const KUrl&)));
161 connect(m_urlNavigator, SIGNAL(urlAboutToBeChanged(const KUrl&)),
162 this, SLOT(saveViewState()));
163 connect(m_urlNavigator, SIGNAL(historyChanged()),
164 this, SLOT(slotHistoryChanged()));
165
166 // initialize status bar
167 m_statusBar = new DolphinStatusBar(this, m_view);
168 connect(m_statusBar, SIGNAL(stopPressed()), this, SLOT(stopLoading()));
169
170 m_statusBarTimer = new QTimer(this);
171 m_statusBarTimer->setSingleShot(true);
172 m_statusBarTimer->setInterval(300);
173 connect(m_statusBarTimer, SIGNAL(timeout()),
174 this, SLOT(updateStatusBar()));
175
176 KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self();
177 connect(undoManager, SIGNAL(jobRecordingFinished(CommandType)),
178 this, SLOT(delayedStatusBarUpdate()));
179
180 // initialize filter bar
181 m_filterBar = new FilterBar(this);
182 m_filterBar->setVisible(settings->filterBar());
183 connect(m_filterBar, SIGNAL(filterChanged(const QString&)),
184 this, SLOT(setNameFilter(const QString&)));
185 connect(m_filterBar, SIGNAL(closeRequest()),
186 this, SLOT(closeFilterBar()));
187 connect(m_view, SIGNAL(urlChanged(const KUrl&)),
188 m_filterBar, SLOT(clear()));
189
190 m_topLayout->addWidget(m_urlNavigator);
191 m_topLayout->addWidget(m_searchBox);
192 m_topLayout->addWidget(m_view);
193 m_topLayout->addWidget(m_filterBar);
194 m_topLayout->addWidget(m_statusBar);
195
196 setSearchModeEnabled(isSearchUrl(url));
197 }
198
199 DolphinViewContainer::~DolphinViewContainer()
200 {
201 }
202
203 KUrl DolphinViewContainer::url() const
204 {
205 return m_urlNavigator->locationUrl();
206 }
207
208 void DolphinViewContainer::setActive(bool active)
209 {
210 m_urlNavigator->setActive(active);
211 m_view->setActive(active);
212 }
213
214 bool DolphinViewContainer::isActive() const
215 {
216 Q_ASSERT(m_view->isActive() == m_urlNavigator->isActive());
217 return m_view->isActive();
218 }
219
220 void DolphinViewContainer::refresh()
221 {
222 GeneralSettings* settings = DolphinSettings::instance().generalSettings();
223 if (settings->modifiedStartupSettings()) {
224 // The startup settings should only get applied if they have been
225 // modified by the user. Otherwise keep the (possibly) different current
226 // settings of the URL navigator and the filterbar.
227 m_urlNavigator->setUrlEditable(settings->editableUrl());
228 m_urlNavigator->setShowFullPath(settings->showFullPath());
229 setFilterBarVisible(settings->filterBar());
230 }
231
232 m_view->refresh();
233 m_statusBar->refresh();
234 }
235
236 bool DolphinViewContainer::isFilterBarVisible() const
237 {
238 return m_filterBar->isVisible();
239 }
240
241 void DolphinViewContainer::setSearchModeEnabled(bool enabled)
242 {
243 if (enabled == isSearchModeEnabled()) {
244 return;
245 }
246
247 m_searchBox->setVisible(enabled);
248 m_urlNavigator->setVisible(!enabled);
249
250 if (enabled) {
251 // Remember the most recent non-search URL as search path
252 // of the search-box, so that it can be restored
253 // when switching back to the URL navigator.
254 KUrl url = m_urlNavigator->locationUrl();
255
256 int index = m_urlNavigator->historyIndex();
257 const int historySize = m_urlNavigator->historySize();
258 while (isSearchUrl(url) && (index < historySize)) {
259 ++index;
260 url = m_urlNavigator->locationUrl(index);
261 }
262
263 if (!isSearchUrl(url)) {
264 m_searchBox->setSearchPath(url);
265 }
266 } else {
267 // Restore the URL for the URL navigator. If Dolphin has been
268 // started with a search-URL, the home URL is used as fallback.
269 const KUrl url = m_searchBox->searchPath();
270 if (url.isValid() && !url.isEmpty()) {
271 if (isSearchUrl(url)) {
272 m_urlNavigator->goHome();
273 } else {
274 m_urlNavigator->setLocationUrl(url);
275 }
276 }
277 }
278
279 emit searchModeChanged(enabled);
280 }
281
282 bool DolphinViewContainer::isSearchModeEnabled() const
283 {
284 return m_searchBox->isVisible();
285 }
286
287 void DolphinViewContainer::setUrl(const KUrl& newUrl)
288 {
289 if (newUrl != m_urlNavigator->locationUrl()) {
290 m_urlNavigator->setLocationUrl(newUrl);
291 }
292 }
293
294 void DolphinViewContainer::setFilterBarVisible(bool visible)
295 {
296 Q_ASSERT(m_filterBar != 0);
297 if (visible) {
298 m_filterBar->show();
299 m_filterBar->setFocus();
300 } else {
301 closeFilterBar();
302 }
303 }
304
305 void DolphinViewContainer::delayedStatusBarUpdate()
306 {
307 if (m_statusBarTimer->isActive() && (m_statusBarTimestamp.elapsed() > 2000)) {
308 // No update of the statusbar has been done during the last 2 seconds,
309 // although an update has been requested. Trigger an immediate update.
310 m_statusBarTimer->stop();
311 updateStatusBar();
312 } else {
313 // Invoke updateStatusBar() with a small delay. This assures that
314 // when a lot of delayedStatusBarUpdates() are done in a short time,
315 // no bottleneck is given.
316 m_statusBarTimer->start();
317 }
318 }
319
320 void DolphinViewContainer::updateStatusBar()
321 {
322 m_statusBarTimestamp.start();
323
324 // As the item count information is less important
325 // in comparison with other messages, it should only
326 // be shown if:
327 // - the status bar is empty or
328 // - shows already the item count information or
329 // - shows only a not very important information
330 const QString newMessage = m_view->statusBarText();
331 const QString currentMessage = m_statusBar->message();
332 const bool updateStatusBarMsg = currentMessage.isEmpty()
333 || (currentMessage == m_statusBar->defaultText())
334 || (m_statusBar->type() == DolphinStatusBar::Information);
335
336 m_statusBar->setDefaultText(newMessage);
337
338 if (updateStatusBarMsg) {
339 m_statusBar->setMessage(newMessage, DolphinStatusBar::Default);
340 }
341 }
342
343 void DolphinViewContainer::updateProgress(int percent)
344 {
345 if (m_statusBar->progressText().isEmpty()) {
346 m_statusBar->setProgressText(i18nc("@info:progress", "Loading folder..."));
347 }
348 m_statusBar->setProgress(percent);
349 }
350
351 void DolphinViewContainer::slotStartedPathLoading()
352 {
353 if (isSearchUrl(url())) {
354 // Search KIO-slaves usually don't provide any progress information. Give
355 // a hint to the user that a searching is done:
356 updateStatusBar();
357 m_statusBar->setProgressText(i18nc("@info", "Searching..."));
358 m_statusBar->setProgress(-1);
359 } else {
360 // Trigger an undetermined progress indication. The progress
361 // information in percent will be triggered by the percent() signal
362 // of the directory lister later.
363 updateProgress(-1);
364 }
365 }
366
367 void DolphinViewContainer::slotFinishedPathLoading()
368 {
369 if (!m_statusBar->progressText().isEmpty()) {
370 m_statusBar->setProgressText(QString());
371 m_statusBar->setProgress(100);
372 }
373
374 if (isSearchUrl(url()) && (m_dirLister->items().count() == 0)) {
375 // The dir lister has been completed on a Nepomuk-URI and no items have been found. Instead
376 // of showing the default status bar information ("0 items") a more helpful information is given:
377 m_statusBar->setMessage(i18nc("@info:status", "No items found."), DolphinStatusBar::Information);
378 } else {
379 updateStatusBar();
380 }
381 }
382
383 void DolphinViewContainer::showItemInfo(const KFileItem& item)
384 {
385 if (item.isNull()) {
386 // Only clear the status bar if unimportant messages are shown.
387 // This prevents that information- or error-messages get hidden
388 // by moving the mouse above the viewport or when closing the
389 // context menu.
390 if (m_statusBar->type() == DolphinStatusBar::Default) {
391 m_statusBar->clear();
392 }
393 } else {
394 m_statusBar->setMessage(item.getStatusBarInfo(), DolphinStatusBar::Default);
395 }
396 }
397
398 void DolphinViewContainer::showInfoMessage(const QString& msg)
399 {
400 m_statusBar->setMessage(msg, DolphinStatusBar::Information);
401 }
402
403 void DolphinViewContainer::showErrorMessage(const QString& msg)
404 {
405 m_statusBar->setMessage(msg, DolphinStatusBar::Error);
406 }
407
408 void DolphinViewContainer::showOperationCompletedMessage(const QString& msg)
409 {
410 m_statusBar->setMessage(msg, DolphinStatusBar::OperationCompleted);
411 }
412
413 void DolphinViewContainer::closeFilterBar()
414 {
415 m_filterBar->hide();
416 m_filterBar->clear();
417 m_view->setFocus();
418 emit showFilterBarChanged(false);
419 }
420
421 void DolphinViewContainer::setNameFilter(const QString& nameFilter)
422 {
423 m_view->setNameFilter(nameFilter);
424 delayedStatusBarUpdate();
425 }
426
427 void DolphinViewContainer::activate()
428 {
429 setActive(true);
430 }
431
432 void DolphinViewContainer::saveViewState()
433 {
434 QByteArray locationState;
435 QDataStream stream(&locationState, QIODevice::WriteOnly);
436 m_view->saveState(stream);
437 m_urlNavigator->saveLocationState(locationState);
438 }
439
440 void DolphinViewContainer::slotUrlNavigatorLocationChanged(const KUrl& url)
441 {
442 if (KProtocolManager::supportsListing(url)) {
443 setSearchModeEnabled(isSearchUrl(url));
444
445 m_view->setUrl(url);
446 if (isActive() && !isSearchUrl(url)) {
447 // When an URL has been entered, the view should get the focus.
448 // The focus must be requested asynchronously, as changing the URL might create
449 // a new view widget.
450 QTimer::singleShot(0, this, SLOT(requestFocus()));
451 }
452 } else if (KProtocolManager::isSourceProtocol(url)) {
453 QString app = "konqueror";
454 if (url.protocol().startsWith(QLatin1String("http"))) {
455 showErrorMessage(i18nc("@info:status",
456 "Dolphin does not support web pages, the web browser has been launched"));
457 const KConfigGroup config(KSharedConfig::openConfig("kdeglobals"), "General");
458 const QString browser = config.readEntry("BrowserApplication");
459 if (!browser.isEmpty()) {
460 app = browser;
461 if (app.startsWith('!')) {
462 // a literal command has been configured, remove the '!' prefix
463 app = app.mid(1);
464 }
465 }
466 } else {
467 showErrorMessage(i18nc("@info:status",
468 "Protocol not supported by Dolphin, Konqueror has been launched"));
469 }
470
471 const QString secureUrl = KShell::quoteArg(url.pathOrUrl());
472 const QString command = app + ' ' + secureUrl;
473 KRun::runCommand(command, app, app, this);
474 } else {
475 showErrorMessage(i18nc("@info:status", "Invalid protocol"));
476 }
477 }
478
479 void DolphinViewContainer::dropUrls(const KUrl& destination, QDropEvent* event)
480 {
481 DragAndDropHelper::instance().dropUrls(KFileItem(), destination, event, this);
482 }
483
484 void DolphinViewContainer::redirect(const KUrl& oldUrl, const KUrl& newUrl)
485 {
486 Q_UNUSED(oldUrl);
487 const bool block = m_urlNavigator->signalsBlocked();
488 m_urlNavigator->blockSignals(true);
489
490 // Assure that the location state is reset for redirection URLs. This
491 // allows to skip redirection URLs when going back or forward in the
492 // URL history.
493 m_urlNavigator->saveLocationState(QByteArray());
494 m_urlNavigator->setLocationUrl(newUrl);
495 setSearchModeEnabled(isSearchUrl(newUrl));
496
497 m_urlNavigator->blockSignals(block);
498 }
499
500 void DolphinViewContainer::requestFocus()
501 {
502 m_view->setFocus();
503 }
504
505 void DolphinViewContainer::saveUrlCompletionMode(KGlobalSettings::Completion completion)
506 {
507 DolphinSettings& settings = DolphinSettings::instance();
508 settings.generalSettings()->setUrlCompletionMode(completion);
509 settings.save();
510 }
511
512 void DolphinViewContainer::slotHistoryChanged()
513 {
514 QByteArray locationState = m_urlNavigator->locationState();
515
516 if (!locationState.isEmpty()) {
517 QDataStream stream(&locationState, QIODevice::ReadOnly);
518 m_view->restoreState(stream);
519 }
520 }
521
522 void DolphinViewContainer::startSearching(const QString &text)
523 {
524 Q_UNUSED(text);
525 const KUrl url = m_searchBox->urlForSearching();
526 if (url.isValid() && !url.isEmpty()) {
527 m_urlNavigator->setLocationUrl(url);
528 }
529 }
530
531 void DolphinViewContainer::closeSearchBox()
532 {
533 setSearchModeEnabled(false);
534 }
535
536 void DolphinViewContainer::stopLoading()
537 {
538 m_view->stopLoading();
539 }
540
541 bool DolphinViewContainer::isSearchUrl(const KUrl& url) const
542 {
543 const QString protocol = url.protocol();
544 return protocol.contains("search") || (protocol == QLatin1String("nepomuk"));
545 }
546
547 void DolphinViewContainer::slotItemTriggered(const KFileItem& item)
548 {
549 KUrl url = item.targetUrl();
550
551 if (item.isDir()) {
552 m_view->setUrl(url);
553 return;
554 }
555
556 const GeneralSettings* settings = DolphinSettings::instance().generalSettings();
557 const bool browseThroughArchives = settings->browseThroughArchives();
558 if (browseThroughArchives && item.isFile() && url.isLocalFile()) {
559 // Generic mechanism for redirecting to tar:/<path>/ when clicking on a tar file,
560 // zip:/<path>/ when clicking on a zip file, etc.
561 // The .protocol file specifies the mimetype that the kioslave handles.
562 // Note that we don't use mimetype inheritance since we don't want to
563 // open OpenDocument files as zip folders...
564 const QString protocol = KProtocolManager::protocolForArchiveMimetype(item.mimetype());
565 if (!protocol.isEmpty()) {
566 url.setProtocol(protocol);
567 m_view->setUrl(url);
568 return;
569 }
570 }
571
572 if (item.mimetype() == "application/x-desktop") {
573 // redirect to the url in Type=Link desktop files
574 KDesktopFile desktopFile(url.toLocalFile());
575 if (desktopFile.hasLinkType()) {
576 url = desktopFile.readUrl();
577 m_view->setUrl(url);
578 return;
579 }
580 }
581
582 item.run();
583 }
584
585 void DolphinViewContainer::openFile(const KUrl& url)
586 {
587 // Using m_dolphinModel for getting the file item instance is not possible
588 // here: openFile() is triggered by an error of the directory lister
589 // job, so the file item must be received "manually".
590 const KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
591 slotItemTriggered(item);
592 }
593
594 #include "dolphinviewcontainer.moc"