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