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