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