]> cloud.milkyroute.net Git - dolphin.git/blob - src/urlnavigator.cpp
Allow zooming in and zooming out in the icons view.
[dolphin.git] / src / urlnavigator.cpp
1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz (<peter.penz@gmx.at>) *
3 * Copyright (C) 2006 by Aaron J. Seigo (<aseigo@kde.org>) *
4 * Copyright (C) 2006 by Patrice Tremblay *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
20 ***************************************************************************/
21
22 #include "urlnavigator.h"
23
24 #include "bookmarkselector.h"
25 #include "dolphinsettings.h"
26 #include "dolphin_generalsettings.h"
27 #include "protocolcombo.h"
28 #include "urlnavigatorbutton.h"
29
30 #include <assert.h>
31
32 #include <kfileitem.h>
33 #include <kicon.h>
34 #include <klocale.h>
35 #include <kprotocolinfo.h>
36 #include <kurlcombobox.h>
37 #include <kurlcompletion.h>
38
39 #include <QApplication>
40 #include <QClipboard>
41 #include <QDir>
42 #include <QHBoxLayout>
43 #include <QLabel>
44 #include <QLineEdit>
45 #include <QMouseEvent>
46 #include <QPushButton>
47
48 UrlNavigator::HistoryElem::HistoryElem() :
49 m_url(),
50 m_currentFileName(),
51 m_contentsX(0),
52 m_contentsY(0)
53 {
54 }
55
56 UrlNavigator::HistoryElem::HistoryElem(const KUrl& url) :
57 m_url(url),
58 m_currentFileName(),
59 m_contentsX(0),
60 m_contentsY(0)
61 {
62 }
63
64 UrlNavigator::HistoryElem::~HistoryElem()
65 {
66 }
67
68 UrlNavigator::UrlNavigator(const KUrl& url,
69 QWidget* parent) :
70 QWidget(parent),
71 m_active(true),
72 m_historyIndex(0),
73 m_layout(0),
74 m_protocols(0),
75 m_protocolSeparator(0),
76 m_host(0),
77 m_filler(0)
78 {
79 m_layout = new QHBoxLayout();
80 m_layout->setSpacing(0);
81 m_layout->setMargin(0);
82
83 m_history.prepend(HistoryElem(url));
84
85 QFontMetrics fontMetrics(font());
86 setMinimumHeight(fontMetrics.height() + 10);
87
88 // intialize toggle button which switches between the breadcrumb view
89 // and the traditional view
90 m_toggleButton = new QPushButton();
91 m_toggleButton->setCheckable(true);
92 m_toggleButton->setFlat(true);
93 m_toggleButton->setIcon(KIcon("locationbar_erase")); // TODO: is just a placeholder icon
94 m_toggleButton->setFocusPolicy(Qt::NoFocus);
95 m_toggleButton->setMinimumHeight(minimumHeight());
96 connect(m_toggleButton, SIGNAL(clicked()),
97 this, SLOT(switchView()));
98 if (DolphinSettings::instance().generalSettings()->editableUrl()) {
99 m_toggleButton->toggle();
100 }
101
102 // initialize the bookmark selector
103 m_bookmarkSelector = new BookmarkSelector(this);
104 connect(m_bookmarkSelector, SIGNAL(bookmarkActivated(const KUrl&)),
105 this, SLOT(setUrl(const KUrl&)));
106
107 // initialize the path box of the traditional view
108 m_pathBox = new KUrlComboBox(KUrlComboBox::Directories, true, this);
109
110 KUrlCompletion* kurlCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion);
111 m_pathBox->setCompletionObject(kurlCompletion);
112 m_pathBox->setAutoDeleteCompletionObject(true);
113
114 connect(m_pathBox, SIGNAL(returnPressed(const QString&)),
115 this, SLOT(slotReturnPressed(const QString&)));
116 connect(m_pathBox, SIGNAL(urlActivated(const KUrl&)),
117 this, SLOT(slotUrlActivated(const KUrl&)));
118
119 //connect(dolphinView, SIGNAL(redirection(const KUrl&, const KUrl&)),
120 // this, SLOT(slotRedirection(const KUrl&, const KUrl&)));
121
122 // Append a filler widget at the end, which automatically resizes to the
123 // maximum available width. This assures that the URL navigator uses the
124 // whole width, so that the clipboard content can be dropped.
125 m_filler = new QWidget();
126 m_filler->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
127
128 m_layout->addWidget(m_toggleButton);
129 m_layout->addWidget(m_bookmarkSelector);
130 m_layout->addWidget(m_pathBox);
131 m_layout->addWidget(m_filler);
132 setLayout(m_layout);
133
134 updateContent();
135 }
136
137 UrlNavigator::~UrlNavigator()
138 {
139 }
140
141 const KUrl& UrlNavigator::url() const
142 {
143 assert(!m_history.empty());
144 QLinkedList<HistoryElem>::const_iterator it = m_history.begin();
145 it += m_historyIndex;
146 return (*it).url();
147 }
148
149 KUrl UrlNavigator::url(int index) const
150 {
151 assert(index >= 0);
152 QString path(url().pathOrUrl());
153 path = path.section('/', 0, index);
154
155 if ( path.length() >= 1 && path.at(path.length()-1) != '/')
156 {
157 path.append('/');
158 }
159
160 return path;
161 }
162
163 const QLinkedList<UrlNavigator::HistoryElem>& UrlNavigator::history(int& index) const
164 {
165 index = m_historyIndex;
166 return m_history;
167 }
168
169 void UrlNavigator::goBack()
170 {
171 updateHistoryElem();
172
173 const int count = m_history.count();
174 if (m_historyIndex < count - 1) {
175 ++m_historyIndex;
176 updateContent();
177 emit urlChanged(url());
178 emit historyChanged();
179 }
180 }
181
182 void UrlNavigator::goForward()
183 {
184 if (m_historyIndex > 0) {
185 --m_historyIndex;
186 updateContent();
187 emit urlChanged(url());
188 emit historyChanged();
189 }
190 }
191
192 void UrlNavigator::goUp()
193 {
194 setUrl(url().upUrl());
195 }
196
197 void UrlNavigator::goHome()
198 {
199 setUrl(DolphinSettings::instance().generalSettings()->homeUrl());
200 }
201
202 void UrlNavigator::setUrlEditable(bool editable)
203 {
204 if (isUrlEditable() != editable) {
205 m_toggleButton->toggle();
206 switchView();
207 }
208 }
209
210 bool UrlNavigator::isUrlEditable() const
211 {
212 return m_toggleButton->isChecked();
213 }
214
215 void UrlNavigator::editUrl(bool editOrBrowse)
216 {
217 setUrlEditable(editOrBrowse);
218 if (editOrBrowse) {
219 m_pathBox->setFocus();
220 }
221 }
222
223 void UrlNavigator::setActive(bool active)
224 {
225 if (active != m_active) {
226 m_active = active;
227 update();
228 if (active) {
229 emit activated();
230 }
231 }
232 }
233
234 void UrlNavigator::dropUrls(const KUrl::List& urls,
235 const KUrl& destination)
236 {
237 emit urlsDropped(urls, destination);
238 }
239
240 void UrlNavigator::setUrl(const KUrl& url)
241 {
242 QString urlStr(url.pathOrUrl());
243 //kDebug() << "setUrl(" << url << ")" << endl;
244 if ( urlStr.length() > 0 && urlStr.at(0) == '~') {
245 // replace '~' by the home directory
246 urlStr.remove(0, 1);
247 urlStr.insert(0, QDir::home().path());
248 }
249
250 const KUrl transformedUrl(urlStr);
251
252 if (m_historyIndex > 0) {
253 // Check whether the previous element of the history has the same Url.
254 // If yes, just go forward instead of inserting a duplicate history
255 // element.
256 QLinkedList<HistoryElem>::const_iterator it = m_history.begin();
257 it += m_historyIndex - 1;
258 const KUrl& nextUrl = (*it).url();
259 if (transformedUrl == nextUrl) {
260 goForward();
261 // kDebug() << "goin' forward in history" << endl;
262 return;
263 }
264 }
265
266 QLinkedList<HistoryElem>::iterator it = m_history.begin() + m_historyIndex;
267 const KUrl& currUrl = (*it).url();
268 if (currUrl == transformedUrl) {
269 // don't insert duplicate history elements
270 // kDebug() << "currUrl == transformedUrl" << endl;
271 return;
272 }
273
274 updateHistoryElem();
275 m_history.insert(it, HistoryElem(transformedUrl));
276
277 updateContent();
278
279 emit urlChanged(transformedUrl);
280 emit historyChanged();
281
282 // Prevent an endless growing of the history: remembering
283 // the last 100 Urls should be enough...
284 if (m_historyIndex > 100) {
285 m_history.erase(m_history.begin());
286 --m_historyIndex;
287 }
288
289 /* kDebug() << "history starting ====================" << endl;
290 int i = 0;
291 for (QValueListIterator<UrlNavigator::HistoryElem> it = m_history.begin();
292 it != m_history.end();
293 ++it, ++i)
294 {
295 kDebug() << i << ": " << (*it).url() << endl;
296 }
297 kDebug() << "history done ========================" << endl;*/
298
299 requestActivation();
300 }
301
302 void UrlNavigator::requestActivation()
303 {
304 setActive(true);
305 }
306
307 void UrlNavigator::storeContentsPosition(int x, int y)
308 {
309 QLinkedList<HistoryElem>::iterator it = m_history.begin() + m_historyIndex;
310 (*it).setContentsX(x);
311 (*it).setContentsY(y);
312 }
313
314 void UrlNavigator::keyReleaseEvent(QKeyEvent* event)
315 {
316 QWidget::keyReleaseEvent(event);
317 if (isUrlEditable() && (event->key() == Qt::Key_Escape)) {
318 setUrlEditable(false);
319 }
320 }
321
322 void UrlNavigator::mouseReleaseEvent(QMouseEvent* event)
323 {
324 if (event->button() == Qt::MidButton) {
325 QClipboard* clipboard = QApplication::clipboard();
326 const QMimeData* mimeData = clipboard->mimeData();
327 if (mimeData->hasText()) {
328 const QString text = mimeData->text();
329 setUrl(KUrl(text));
330 }
331 }
332 QWidget::mouseReleaseEvent(event);
333 }
334
335 void UrlNavigator::slotReturnPressed(const QString& text)
336 {
337 // Parts of the following code have been taken
338 // from the class KateFileSelector located in
339 // kate/app/katefileselector.hpp of Kate.
340 // Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
341 // Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
342 // Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
343
344 KUrl typedUrl(text);
345 if (typedUrl.hasPass()) {
346 typedUrl.setPass(QString());
347 }
348
349 QStringList urls = m_pathBox->urls();
350 urls.removeAll(typedUrl.url());
351 urls.prepend(typedUrl.url());
352 m_pathBox->setUrls(urls, KUrlComboBox::RemoveBottom);
353
354 setUrl(typedUrl);
355 // The URL might have been adjusted by UrlNavigator::setUrl(), hence
356 // synchronize the result in the path box.
357 m_pathBox->setUrl(url());
358 }
359
360 void UrlNavigator::slotUrlActivated(const KUrl& url)
361 {
362 setUrl(url);
363 }
364
365 void UrlNavigator::slotRemoteHostActivated()
366 {
367 KUrl u = url();
368
369 QString host = m_host->text();
370 QString user;
371
372 int marker = host.indexOf("@");
373 if (marker != -1)
374 {
375 user = host.left(marker);
376 u.setUser(user);
377 host = host.right(host.length() - marker - 1);
378 }
379
380 marker = host.indexOf("/");
381 if (marker != -1)
382 {
383 u.setPath(host.right(host.length() - marker));
384 host.truncate(marker);
385 }
386 else
387 {
388 u.setPath("");
389 }
390
391 if (m_protocols->currentProtocol() != u.protocol() ||
392 host != u.host() ||
393 user != u.user())
394 {
395 u.setProtocol(m_protocols->currentProtocol());
396 u.setHost(m_host->text());
397
398 //TODO: get rid of this HACK for file:///!
399 if (u.protocol() == "file")
400 {
401 u.setHost("");
402 if (u.path().isEmpty())
403 {
404 u.setPath("/");
405 }
406 }
407
408 setUrl(u);
409 }
410 }
411
412 void UrlNavigator::slotProtocolChanged(const QString& protocol)
413 {
414 KUrl url;
415 url.setProtocol(protocol);
416 //url.setPath(KProtocolInfo::protocolClass(protocol) == ":local" ? "/" : "");
417 url.setPath("/");
418 QLinkedList<UrlNavigatorButton*>::const_iterator it = m_navButtons.begin();
419 const QLinkedList<UrlNavigatorButton*>::const_iterator itEnd = m_navButtons.end();
420 while (it != itEnd) {
421 (*it)->close();
422 (*it)->deleteLater();
423 ++it;
424 }
425 m_navButtons.clear();
426
427 if (KProtocolInfo::protocolClass(protocol) == ":local") {
428 setUrl(url);
429 }
430 else {
431 if (!m_host) {
432 m_protocolSeparator = new QLabel("://", this);
433 appendWidget(m_protocolSeparator);
434 m_host = new QLineEdit(this);
435 appendWidget(m_host);
436
437 connect(m_host, SIGNAL(lostFocus()),
438 this, SLOT(slotRemoteHostActivated()));
439 connect(m_host, SIGNAL(returnPressed()),
440 this, SLOT(slotRemoteHostActivated()));
441 }
442 else {
443 m_host->setText("");
444 }
445 m_protocolSeparator->show();
446 m_host->show();
447 m_host->setFocus();
448 }
449 }
450
451 void UrlNavigator::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl)
452 {
453 // kDebug() << "received redirection to " << newUrl << endl;
454 kDebug() << "received redirection from " << oldUrl << " to " << newUrl << endl;
455 /* UrlStack::iterator it = m_urls.find(oldUrl);
456 if (it != m_urls.end())
457 {
458 m_urls.erase(++it, m_urls.end());
459 }
460
461 m_urls.append(newUrl);*/
462 }
463
464 void UrlNavigator::switchView()
465 {
466 updateContent();
467 if (isUrlEditable()) {
468 m_pathBox->setFocus();
469 }
470 else {
471 setUrl(m_pathBox->currentText());
472 }
473 emit requestActivation();
474 }
475
476 void UrlNavigator::updateHistoryElem()
477 {
478 assert(m_historyIndex >= 0);
479 const KFileItem* item = 0; // TODO: m_dolphinView->currentFileItem();
480 if (item != 0) {
481 QLinkedList<HistoryElem>::iterator it = m_history.begin() + m_historyIndex;
482 (*it).setCurrentFileName(item->name());
483 }
484 }
485
486 void UrlNavigator::updateContent()
487 {
488 m_bookmarkSelector->updateSelection(url());
489
490 m_toggleButton->setToolTip(QString());
491 QString path(url().pathOrUrl());
492
493 // TODO: prevent accessing the DolphinMainWindow out from this scope
494 //const QAction* action = dolphinView()->mainWindow()->actionCollection()->action("editable_location");
495 // TODO: registry of default shortcuts
496 //QString shortcut = action? action->shortcut().toString() : "Ctrl+L";
497 const QString shortcut = "Ctrl+L";
498
499 if (m_toggleButton->isChecked()) {
500 delete m_protocols; m_protocols = 0;
501 delete m_protocolSeparator; m_protocolSeparator = 0;
502 delete m_host; m_host = 0;
503 deleteButtons();
504 m_filler->hide();
505
506 m_toggleButton->setToolTip(i18n("Browse (%1, Escape)", shortcut));
507
508 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
509 m_pathBox->show();
510 m_pathBox->setUrl(url());
511 }
512 else {
513 m_toggleButton->setToolTip(i18n("Edit location (%1)", shortcut));
514
515 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
516 m_pathBox->hide();
517 m_filler->show();
518
519 // get the data from the currently selected bookmark
520 KBookmark bookmark = m_bookmarkSelector->selectedBookmark();
521 //int bookmarkIndex = m_bookmarkSelector->selectedIndex();
522
523 QString bookmarkPath;
524 if (bookmark.isNull()) {
525 // No bookmark is a part of the current Url.
526 // The following code tries to guess the bookmark
527 // path. E. g. "fish://root@192.168.0.2/var/lib" writes
528 // "fish://root@192.168.0.2" to 'bookmarkPath', which leads to the
529 // navigation indication 'Custom Path > var > lib".
530 int idx = path.indexOf(QString("//"));
531 idx = path.indexOf("/", (idx < 0) ? 0 : idx + 2);
532 bookmarkPath = (idx < 0) ? path : path.left(idx);
533 }
534 else {
535 bookmarkPath = bookmark.url().pathOrUrl();
536 }
537 const uint len = bookmarkPath.length();
538
539 // calculate the start point for the URL navigator buttons by counting
540 // the slashs inside the bookmark URL
541 int slashCount = 0;
542 for (uint i = 0; i < len; ++i) {
543 if (bookmarkPath.at(i) == QChar('/')) {
544 ++slashCount;
545 }
546 }
547 if ((len > 0) && bookmarkPath.at(len - 1) == QChar('/')) {
548 assert(slashCount > 0);
549 --slashCount;
550 }
551
552 if (!url().isLocalFile() && bookmark.isNull()) {
553 QString protocol = url().protocol();
554 if (!m_protocols) {
555 deleteButtons();
556 m_protocols = new ProtocolCombo(protocol, this);
557 appendWidget(m_protocols);
558 connect(m_protocols, SIGNAL(activated(const QString&)),
559 this, SLOT(slotProtocolChanged(const QString&)));
560 }
561 else {
562 m_protocols->setProtocol(protocol);
563 }
564 m_protocols->show();
565
566 if (KProtocolInfo::protocolClass(protocol) != ":local") {
567 QString hostText = url().host();
568
569 if (!url().user().isEmpty()) {
570 hostText = url().user() + '@' + hostText;
571 }
572
573 if (!m_host) {
574 m_protocolSeparator = new QLabel("://", this);
575 appendWidget(m_protocolSeparator);
576 m_host = new QLineEdit(hostText, this);
577 appendWidget(m_host);
578
579 connect(m_host, SIGNAL(lostFocus()),
580 this, SLOT(slotRemoteHostActivated()));
581 connect(m_host, SIGNAL(returnPressed()),
582 this, SLOT(slotRemoteHostActivated()));
583 }
584 else {
585 m_host->setText(hostText);
586 }
587 m_protocolSeparator->show();
588 m_host->show();
589 }
590 else {
591 delete m_protocolSeparator; m_protocolSeparator = 0;
592 delete m_host; m_host = 0;
593 }
594 }
595 else if (m_protocols) {
596 m_protocols->hide();
597
598 if (m_host) {
599 m_protocolSeparator->hide();
600 m_host->hide();
601 }
602 }
603
604 updateButtons(path, slashCount);
605 }
606 }
607
608 void UrlNavigator::updateButtons(const QString& path, int startIndex)
609 {
610 QLinkedList<UrlNavigatorButton*>::iterator it = m_navButtons.begin();
611 const QLinkedList<UrlNavigatorButton*>::const_iterator itEnd = m_navButtons.end();
612 bool createButton = false;
613
614 int idx = startIndex;
615 bool hasNext = true;
616 do {
617 createButton = (it == itEnd);
618
619 const QString dirName = path.section('/', idx, idx);
620 const bool isFirstButton = (idx == startIndex);
621 hasNext = isFirstButton || !dirName.isEmpty();
622 if (hasNext) {
623 QString text;
624 if (isFirstButton) {
625 // the first URL navigator button should get the name of the
626 // bookmark instead of the directory name
627 const KBookmark bookmark = m_bookmarkSelector->selectedBookmark();
628 text = bookmark.text();
629 if (text.isEmpty()) {
630 if (url().isLocalFile()) {
631 text = i18n("Custom Path");
632 }
633 else {
634 ++idx;
635 continue;
636 }
637 }
638 }
639
640 UrlNavigatorButton* button = 0;
641 if (createButton) {
642 button = new UrlNavigatorButton(idx, this);
643 appendWidget(button);
644 }
645 else {
646 button = *it;
647 button->setIndex(idx);
648 }
649
650 if (isFirstButton) {
651 button->setText(text);
652 }
653
654 if (createButton) {
655 button->show();
656 m_navButtons.append(button);
657 }
658 else {
659 ++it;
660 }
661 ++idx;
662 }
663 } while (hasNext);
664
665 // delete buttons which are not used anymore
666 QLinkedList<UrlNavigatorButton*>::iterator itBegin = it;
667 while (it != itEnd) {
668 (*it)->close();
669 (*it)->deleteLater();
670 ++it;
671 }
672 m_navButtons.erase(itBegin, m_navButtons.end());
673 }
674
675 void UrlNavigator::deleteButtons()
676 {
677 QLinkedList<UrlNavigatorButton*>::iterator itBegin = m_navButtons.begin();
678 QLinkedList<UrlNavigatorButton*>::iterator itEnd = m_navButtons.end();
679 QLinkedList<UrlNavigatorButton*>::iterator it = itBegin;
680 while (it != itEnd) {
681 (*it)->close();
682 (*it)->deleteLater();
683 ++it;
684 }
685 m_navButtons.erase(itBegin, itEnd);
686 }
687
688 void UrlNavigator::appendWidget(QWidget* widget)
689 {
690 m_layout->insertWidget(m_layout->count() - 1, widget);
691 }
692
693 #include "urlnavigator.moc"