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