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