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