]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinmainwindow.cpp
Cleanup of URL drop handling (simplified code, modifier keys work again). After furth...
[dolphin.git] / src / dolphinmainwindow.cpp
1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at> *
3 * Copyright (C) 2006 by Stefan Monov <logixoul@gmail.com> *
4 * Copyright (C) 2006 by Cvetoslav Ludmiloff <ludmiloff@gmail.com> *
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 "dolphinmainwindow.h"
23
24 #include <assert.h>
25
26 #include "dolphinapplication.h"
27 #include "dolphinsettings.h"
28 #include "dolphinsettingsdialog.h"
29 #include "dolphinstatusbar.h"
30 #include "dolphinapplication.h"
31 #include "urlnavigator.h"
32 #include "dolphinsettings.h"
33 #include "bookmarkssidebarpage.h"
34 #include "infosidebarpage.h"
35 #include "generalsettings.h"
36 #include "viewpropertiesdialog.h"
37 #include "viewproperties.h"
38
39 #include <kaction.h>
40 #include <kactioncollection.h>
41 #include <kbookmarkmanager.h>
42 #include <kconfig.h>
43 #include <kdesktopfile.h>
44 #include <kdeversion.h>
45 #include <kfiledialog.h>
46 #include <kglobal.h>
47 #include <kicon.h>
48 #include <kiconloader.h>
49 #include <kio/netaccess.h>
50 #include <kio/renamedialog.h>
51 #include <kinputdialog.h>
52 #include <klocale.h>
53 #include <kmenu.h>
54 #include <kmessagebox.h>
55 #include <knewmenu.h>
56 #include <konqmimedata.h>
57 #include <konq_undo.h>
58 #include <kpropertiesdialog.h>
59 #include <kprotocolinfo.h>
60 #include <ktoggleaction.h>
61 #include <krun.h>
62 #include <kshell.h>
63 #include <kstandarddirs.h>
64 #include <kstatusbar.h>
65 #include <kstandardaction.h>
66 #include <kurl.h>
67
68 #include <QCloseEvent>
69 #include <QClipboard>
70 #include <QSplitter>
71 #include <QDockWidget>
72
73 DolphinMainWindow::DolphinMainWindow() :
74 KMainWindow(0),
75 m_newMenu(0),
76 m_splitter(0),
77 m_activeView(0)
78 {
79 setObjectName("Dolphin");
80 m_view[PrimaryIdx] = 0;
81 m_view[SecondaryIdx] = 0;
82
83 KonqUndoManager::incRef();
84
85 connect(KonqUndoManager::self(), SIGNAL(undoAvailable(bool)),
86 this, SLOT(slotUndoAvailable(bool)));
87 connect(KonqUndoManager::self(), SIGNAL(undoTextChanged(const QString&)),
88 this, SLOT(slotUndoTextChanged(const QString&)));
89 }
90
91 DolphinMainWindow::~DolphinMainWindow()
92 {
93 KonqUndoManager::decRef();
94 DolphinApplication::app()->removeMainWindow(this);
95 }
96
97 void DolphinMainWindow::setActiveView(DolphinView* view)
98 {
99 assert((view == m_view[PrimaryIdx]) || (view == m_view[SecondaryIdx]));
100 if (m_activeView == view) {
101 return;
102 }
103
104 m_activeView = view;
105
106 updateHistory();
107 updateEditActions();
108 updateViewActions();
109 updateGoActions();
110
111 setCaption(m_activeView->url().fileName());
112
113 emit activeViewChanged();
114 }
115
116 void DolphinMainWindow::dropUrls(const KUrl::List& urls,
117 const KUrl& destination)
118 {
119 Qt::DropAction action = Qt::CopyAction;
120
121 Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers();
122 const bool shiftPressed = modifier & Qt::ShiftModifier;
123 const bool controlPressed = modifier & Qt::ControlModifier;
124 if (shiftPressed && controlPressed) {
125 // shortcut for 'Link Here' is used
126 action = Qt::LinkAction;
127 }
128 else if (shiftPressed) {
129 // shortcut for 'Move Here' is used
130 action = Qt::MoveAction;
131 }
132 else if (controlPressed) {
133 // shortcut for 'Copy Here' is used
134 action = Qt::CopyAction;
135 }
136 else {
137 // open a context menu which offers the following actions:
138 // - Move Here
139 // - Copy Here
140 // - Link Here
141 // - Cancel
142
143 KMenu popup(this);
144
145 QString seq = QKeySequence(Qt::ShiftModifier).toString();
146 seq.chop(1); // chop superfluous '+'
147 QAction* moveAction = popup.addAction(KIcon("goto"),
148 i18n("&Move Here") + "\t" + seq);
149
150 seq = QKeySequence(Qt::ControlModifier).toString();
151 seq.chop(1);
152 QAction* copyAction = popup.addAction(KIcon("editcopy"),
153 i18n("&Copy Here") + "\t" + seq);
154
155 seq = QKeySequence(Qt::ControlModifier + Qt::ShiftModifier).toString();
156 seq.chop(1);
157 QAction* linkAction = popup.addAction(KIcon("www"),
158 i18n("&Link Here") + "\t" + seq);
159
160 popup.addSeparator();
161 popup.addAction(KIcon("stop"), i18n("Cancel"));
162
163 QAction* activatedAction = popup.exec(QCursor::pos());
164 if (activatedAction == moveAction) {
165 action = Qt::MoveAction;
166 }
167 else if (activatedAction == copyAction) {
168 action = Qt::CopyAction;
169 }
170 else if (activatedAction == linkAction) {
171 action = Qt::LinkAction;
172 }
173 }
174
175 switch (action) {
176 case Qt::MoveAction:
177 moveUrls(urls, destination);
178 break;
179
180 case Qt::CopyAction:
181 copyUrls(urls, destination);
182 break;
183
184 case Qt::LinkAction:
185 KonqOperations::copy(this, KonqOperations::LINK, urls, destination);
186 m_undoOperations.append(KonqOperations::LINK);
187 break;
188
189 default:
190 break;
191 }
192 }
193
194 void DolphinMainWindow::refreshViews()
195 {
196 const bool split = DolphinSettings::instance().generalSettings()->splitView();
197 const bool isPrimaryViewActive = (m_activeView == m_view[PrimaryIdx]);
198 KUrl url;
199 for (int i = PrimaryIdx; i <= SecondaryIdx; ++i) {
200 if (m_view[i] != 0) {
201 url = m_view[i]->url();
202
203 // delete view instance...
204 m_view[i]->close();
205 m_view[i]->deleteLater();
206 m_view[i] = 0;
207 }
208
209 if (split || (i == PrimaryIdx)) {
210 // ... and recreate it
211 ViewProperties props(url);
212 m_view[i] = new DolphinView(this,
213 m_splitter,
214 url,
215 props.viewMode(),
216 props.showHiddenFiles());
217 connectViewSignals(i);
218 m_view[i]->show();
219 }
220 }
221
222 m_activeView = isPrimaryViewActive ? m_view[PrimaryIdx] : m_view[SecondaryIdx];
223 assert(m_activeView != 0);
224
225 updateViewActions();
226 emit activeViewChanged();
227 }
228
229 void DolphinMainWindow::slotViewModeChanged()
230 {
231 updateViewActions();
232 }
233
234 void DolphinMainWindow::slotShowHiddenFilesChanged()
235 {
236 KToggleAction* showHiddenFilesAction =
237 static_cast<KToggleAction*>(actionCollection()->action("show_hidden_files"));
238 showHiddenFilesAction->setChecked(m_activeView->showHiddenFiles());
239 }
240
241 void DolphinMainWindow::slotSortingChanged(DolphinView::Sorting sorting)
242 {
243 QAction* action = 0;
244 switch (sorting) {
245 case DolphinView::SortByName:
246 action = actionCollection()->action("by_name");
247 break;
248 case DolphinView::SortBySize:
249 action = actionCollection()->action("by_size");
250 break;
251 case DolphinView::SortByDate:
252 action = actionCollection()->action("by_date");
253 break;
254 default:
255 break;
256 }
257
258 if (action != 0) {
259 KToggleAction* toggleAction = static_cast<KToggleAction*>(action);
260 toggleAction->setChecked(true);
261 }
262 }
263
264 void DolphinMainWindow::slotSortOrderChanged(Qt::SortOrder order)
265 {
266 KToggleAction* descending = static_cast<KToggleAction*>(actionCollection()->action("descending"));
267 const bool sortDescending = (order == Qt::Descending);
268 descending->setChecked(sortDescending);
269 }
270
271 void DolphinMainWindow::slotSelectionChanged()
272 {
273 updateEditActions();
274
275 assert(m_view[PrimaryIdx] != 0);
276 int selectedUrlsCount = m_view[PrimaryIdx]->selectedUrls().count();
277 if (m_view[SecondaryIdx] != 0) {
278 selectedUrlsCount += m_view[SecondaryIdx]->selectedUrls().count();
279 }
280
281 QAction* compareFilesAction = actionCollection()->action("compare_files");
282 compareFilesAction->setEnabled(selectedUrlsCount == 2);
283
284 m_activeView->updateStatusBar();
285
286 emit selectionChanged();
287 }
288
289 void DolphinMainWindow::slotHistoryChanged()
290 {
291 updateHistory();
292 }
293
294 void DolphinMainWindow::slotUrlChanged(const KUrl& url)
295 {
296 updateEditActions();
297 updateGoActions();
298 setCaption(url.fileName());
299 }
300
301 void DolphinMainWindow::updateFilterBarAction(bool show)
302 {
303 KToggleAction* showFilterBarAction =
304 static_cast<KToggleAction*>(actionCollection()->action("show_filter_bar"));
305 showFilterBarAction->setChecked(show);
306 }
307
308 void DolphinMainWindow::openNewMainWindow()
309 {
310 DolphinApplication::app()->createMainWindow()->show();
311 }
312
313 void DolphinMainWindow::closeEvent(QCloseEvent* event)
314 {
315 DolphinSettings& settings = DolphinSettings::instance();
316 GeneralSettings* generalSettings = settings.generalSettings();
317 generalSettings->setFirstRun(false);
318
319 settings.save();
320
321 // TODO: I assume there will be a generic way in KDE 4 to store the docks
322 // of the main window. In the meantime they are stored manually:
323 QString filename = KStandardDirs::locateLocal("data", KGlobal::instance()->instanceName());
324 filename.append("/panels_layout");
325 QFile file(filename);
326 if (file.open(QIODevice::WriteOnly)) {
327 QByteArray data = saveState();
328 file.write(data);
329 file.close();
330 }
331
332 KMainWindow::closeEvent(event);
333 }
334
335 void DolphinMainWindow::saveProperties(KConfig* config)
336 {
337 config->setGroup("Primary view");
338 config->writeEntry("Url", m_view[PrimaryIdx]->url().url());
339 config->writeEntry("Editable Url", m_view[PrimaryIdx]->isUrlEditable());
340 if (m_view[SecondaryIdx] != 0) {
341 config->setGroup("Secondary view");
342 config->writeEntry("Url", m_view[SecondaryIdx]->url().url());
343 config->writeEntry("Editable Url", m_view[SecondaryIdx]->isUrlEditable());
344 }
345 }
346
347 void DolphinMainWindow::readProperties(KConfig* config)
348 {
349 config->setGroup("Primary view");
350 m_view[PrimaryIdx]->setUrl(config->readEntry("Url"));
351 m_view[PrimaryIdx]->setUrlEditable(config->readEntry("Editable Url", false));
352 if (config->hasGroup("Secondary view")) {
353 config->setGroup("Secondary view");
354 if (m_view[SecondaryIdx] == 0) {
355 toggleSplitView();
356 }
357 m_view[SecondaryIdx]->setUrl(config->readEntry("Url"));
358 m_view[SecondaryIdx]->setUrlEditable(config->readEntry("Editable Url", false));
359 }
360 else if (m_view[SecondaryIdx] != 0) {
361 toggleSplitView();
362 }
363 }
364
365 void DolphinMainWindow::updateNewMenu()
366 {
367 m_newMenu->slotCheckUpToDate();
368 m_newMenu->setPopupFiles(activeView()->url());
369 }
370
371 void DolphinMainWindow::rename()
372 {
373 clearStatusBar();
374 m_activeView->renameSelectedItems();
375 }
376
377 void DolphinMainWindow::moveToTrash()
378 {
379 clearStatusBar();
380 const KUrl::List selectedUrls = m_activeView->selectedUrls();
381 KonqOperations::del(this, KonqOperations::TRASH, selectedUrls);
382 m_undoOperations.append(KonqOperations::TRASH);
383 }
384
385 void DolphinMainWindow::deleteItems()
386 {
387 clearStatusBar();
388
389 KUrl::List list = m_activeView->selectedUrls();
390 const uint itemCount = list.count();
391 assert(itemCount >= 1);
392
393 QString text;
394 if (itemCount > 1) {
395 text = i18n("Do you really want to delete the %1 selected items?",itemCount);
396 }
397 else {
398 const KUrl& url = list.first();
399 text = i18n("Do you really want to delete '%1'?",url.fileName());
400 }
401
402 const bool del = KMessageBox::warningContinueCancel(this,
403 text,
404 QString::null,
405 KGuiItem(i18n("Delete"), KIcon("editdelete"))
406 ) == KMessageBox::Continue;
407 if (del) {
408 KIO::Job* job = KIO::del(list);
409 connect(job, SIGNAL(result(KJob*)),
410 this, SLOT(slotHandleJobError(KJob*)));
411 connect(job, SIGNAL(result(KJob*)),
412 this, SLOT(slotDeleteFileFinished(KJob*)));
413 }
414 }
415
416 void DolphinMainWindow::properties()
417 {
418 const KFileItemList list = m_activeView->selectedItems();
419 new KPropertiesDialog(list, this);
420 }
421
422 void DolphinMainWindow::quit()
423 {
424 close();
425 }
426
427 void DolphinMainWindow::slotHandleJobError(KJob* job)
428 {
429 if (job->error() != 0) {
430 DolphinStatusBar* statusBar = m_activeView->statusBar();
431 statusBar->setMessage(job->errorString(),
432 DolphinStatusBar::Error);
433 }
434 }
435
436 void DolphinMainWindow::slotDeleteFileFinished(KJob* job)
437 {
438 if (job->error() == 0) {
439 DolphinStatusBar* statusBar = m_activeView->statusBar();
440 statusBar->setMessage(i18n("Delete operation completed."),
441 DolphinStatusBar::OperationCompleted);
442 }
443 }
444
445 void DolphinMainWindow::slotUndoAvailable(bool available)
446 {
447 QAction* undoAction = actionCollection()->action(KStandardAction::stdName(KStandardAction::Undo));
448 if (undoAction != 0) {
449 undoAction->setEnabled(available);
450 }
451
452 if (available && (m_undoOperations.count() > 0)) {
453 const KonqOperations::Operation op = m_undoOperations.takeFirst();
454 DolphinStatusBar* statusBar = m_activeView->statusBar();
455 switch (op) {
456 case KonqOperations::COPY:
457 statusBar->setMessage(i18n("Copy operation completed."),
458 DolphinStatusBar::OperationCompleted);
459 break;
460 case KonqOperations::MOVE:
461 statusBar->setMessage(i18n("Move operation completed."),
462 DolphinStatusBar::OperationCompleted);
463 break;
464 case KonqOperations::LINK:
465 statusBar->setMessage(i18n("Link operation completed."),
466 DolphinStatusBar::OperationCompleted);
467 break;
468 case KonqOperations::TRASH:
469 statusBar->setMessage(i18n("Move to trash operation completed."),
470 DolphinStatusBar::OperationCompleted);
471 break;
472 default:
473 break;
474 }
475
476 }
477 }
478
479 void DolphinMainWindow::slotUndoTextChanged(const QString& text)
480 {
481 QAction* undoAction = actionCollection()->action(KStandardAction::stdName(KStandardAction::Undo));
482 if (undoAction != 0) {
483 undoAction->setText(text);
484 }
485 }
486
487 void DolphinMainWindow::cut()
488 {
489 QMimeData* mimeData = new QMimeData();
490 const KUrl::List kdeUrls = m_activeView->selectedUrls();
491 const KUrl::List mostLocalUrls;
492 KonqMimeData::populateMimeData(mimeData, kdeUrls, mostLocalUrls, true);
493 QApplication::clipboard()->setMimeData(mimeData);
494 }
495
496 void DolphinMainWindow::copy()
497 {
498 QMimeData* mimeData = new QMimeData();
499 const KUrl::List kdeUrls = m_activeView->selectedUrls();
500 const KUrl::List mostLocalUrls;
501 KonqMimeData::populateMimeData(mimeData, kdeUrls, mostLocalUrls, false);
502
503 QApplication::clipboard()->setMimeData(mimeData);
504 }
505
506 void DolphinMainWindow::paste()
507 {
508 QClipboard* clipboard = QApplication::clipboard();
509 const QMimeData* mimeData = clipboard->mimeData();
510
511 clearStatusBar();
512
513 const KUrl::List sourceUrls = KUrl::List::fromMimeData(mimeData);
514
515 // per default the pasting is done into the current Url of the view
516 KUrl destUrl(m_activeView->url());
517
518 // check whether the pasting should be done into a selected directory
519 KUrl::List selectedUrls = m_activeView->selectedUrls();
520 if (selectedUrls.count() == 1) {
521 const KFileItem fileItem(S_IFDIR,
522 KFileItem::Unknown,
523 selectedUrls.first(),
524 true);
525 if (fileItem.isDir()) {
526 // only one item is selected which is a directory, hence paste
527 // into this directory
528 destUrl = selectedUrls.first();
529 }
530 }
531
532 if (KonqMimeData::decodeIsCutSelection(mimeData)) {
533 moveUrls(sourceUrls, destUrl);
534 clipboard->clear();
535 }
536 else {
537 copyUrls(sourceUrls, destUrl);
538 }
539 }
540
541 void DolphinMainWindow::updatePasteAction()
542 {
543 QAction* pasteAction = actionCollection()->action(KStandardAction::stdName(KStandardAction::Paste));
544 if (pasteAction == 0) {
545 return;
546 }
547
548 QString text(i18n("Paste"));
549 QClipboard* clipboard = QApplication::clipboard();
550 const QMimeData* mimeData = clipboard->mimeData();
551
552 KUrl::List urls = KUrl::List::fromMimeData(mimeData);
553 if (!urls.isEmpty()) {
554 pasteAction->setEnabled(true);
555
556 const int count = urls.count();
557 if (count == 1) {
558 pasteAction->setText(i18n("Paste 1 File"));
559 }
560 else {
561 pasteAction->setText(i18n("Paste %1 Files").arg(count));
562 }
563 }
564 else {
565 pasteAction->setEnabled(false);
566 pasteAction->setText(i18n("Paste"));
567 }
568
569 if (pasteAction->isEnabled()) {
570 KUrl::List urls = m_activeView->selectedUrls();
571 const uint count = urls.count();
572 if (count > 1) {
573 // pasting should not be allowed when more than one file
574 // is selected
575 pasteAction->setEnabled(false);
576 }
577 else if (count == 1) {
578 // Only one file is selected. Pasting is only allowed if this
579 // file is a directory.
580 // TODO: this doesn't work with remote protocols; instead we need a
581 // m_activeView->selectedFileItems() to get the real KFileItems
582 const KFileItem fileItem(S_IFDIR,
583 KFileItem::Unknown,
584 urls.first(),
585 true);
586 pasteAction->setEnabled(fileItem.isDir());
587 }
588 }
589 }
590
591 void DolphinMainWindow::selectAll()
592 {
593 clearStatusBar();
594 m_activeView->selectAll();
595 }
596
597 void DolphinMainWindow::invertSelection()
598 {
599 clearStatusBar();
600 m_activeView->invertSelection();
601 }
602 void DolphinMainWindow::setIconsView()
603 {
604 m_activeView->setMode(DolphinView::IconsView);
605 }
606
607 void DolphinMainWindow::setDetailsView()
608 {
609 m_activeView->setMode(DolphinView::DetailsView);
610 }
611
612 void DolphinMainWindow::sortByName()
613 {
614 m_activeView->setSorting(DolphinView::SortByName);
615 }
616
617 void DolphinMainWindow::sortBySize()
618 {
619 m_activeView->setSorting(DolphinView::SortBySize);
620 }
621
622 void DolphinMainWindow::sortByDate()
623 {
624 m_activeView->setSorting(DolphinView::SortByDate);
625 }
626
627 void DolphinMainWindow::toggleSortOrder()
628 {
629 const Qt::SortOrder order = (m_activeView->sortOrder() == Qt::Ascending) ?
630 Qt::Descending :
631 Qt::Ascending;
632 m_activeView->setSortOrder(order);
633 }
634
635 void DolphinMainWindow::toggleSplitView()
636 {
637 if (m_view[SecondaryIdx] == 0) {
638 const int newWidth = (m_view[PrimaryIdx]->width() - m_splitter->handleWidth()) / 2;
639 // create a secondary view
640 m_view[SecondaryIdx] = new DolphinView(this,
641 0,
642 m_view[PrimaryIdx]->url(),
643 m_view[PrimaryIdx]->mode(),
644 m_view[PrimaryIdx]->showHiddenFiles());
645 connectViewSignals(SecondaryIdx);
646 m_splitter->addWidget(m_view[SecondaryIdx]);
647 m_splitter->setSizes(QList<int>() << newWidth << newWidth);
648 m_view[SecondaryIdx]->show();
649 }
650 else {
651 // remove secondary view
652 if (m_activeView == m_view[PrimaryIdx]) {
653 m_view[SecondaryIdx]->close();
654 m_view[SecondaryIdx]->deleteLater();
655 m_view[SecondaryIdx] = 0;
656 setActiveView(m_view[PrimaryIdx]);
657 }
658 else {
659 // The secondary view is active, hence from the users point of view
660 // the content of the secondary view should be moved to the primary view.
661 // From an implementation point of view it is more efficient to close
662 // the primary view and exchange the internal pointers afterwards.
663 m_view[PrimaryIdx]->close();
664 delete m_view[PrimaryIdx];
665 m_view[PrimaryIdx] = m_view[SecondaryIdx];
666 m_view[SecondaryIdx] = 0;
667 setActiveView(m_view[PrimaryIdx]);
668 }
669 }
670 }
671
672 void DolphinMainWindow::reloadView()
673 {
674 clearStatusBar();
675 m_activeView->reload();
676 }
677
678 void DolphinMainWindow::stopLoading()
679 {
680 }
681
682 void DolphinMainWindow::togglePreview()
683 {
684 clearStatusBar();
685
686 const KToggleAction* showPreviewAction =
687 static_cast<KToggleAction*>(actionCollection()->action("show_preview"));
688 const bool show = showPreviewAction->isChecked();
689 m_activeView->setShowPreview(show);
690 }
691
692 void DolphinMainWindow::toggleShowHiddenFiles()
693 {
694 clearStatusBar();
695
696 const KToggleAction* showHiddenFilesAction =
697 static_cast<KToggleAction*>(actionCollection()->action("show_hidden_files"));
698 const bool show = showHiddenFilesAction->isChecked();
699 m_activeView->setShowHiddenFiles(show);
700 }
701
702 void DolphinMainWindow::showFilterBar()
703 {
704 const KToggleAction* showFilterBarAction =
705 static_cast<KToggleAction*>(actionCollection()->action("show_filter_bar"));
706 const bool show = showFilterBarAction->isChecked();
707 m_activeView->showFilterBar(show);
708 }
709
710 void DolphinMainWindow::zoomIn()
711 {
712 m_activeView->zoomIn();
713 updateViewActions();
714 }
715
716 void DolphinMainWindow::zoomOut()
717 {
718 m_activeView->zoomOut();
719 updateViewActions();
720 }
721
722 void DolphinMainWindow::toggleEditLocation()
723 {
724 clearStatusBar();
725
726 KToggleAction* action = static_cast<KToggleAction*>(actionCollection()->action("editable_location"));
727
728 bool editOrBrowse = action->isChecked();
729 m_activeView->setUrlEditable(editOrBrowse);
730 }
731
732 void DolphinMainWindow::editLocation()
733 {
734 KToggleAction* action = static_cast<KToggleAction*>(actionCollection()->action("editable_location"));
735 action->setChecked(true);
736 m_activeView->setUrlEditable(true);
737 }
738
739 void DolphinMainWindow::adjustViewProperties()
740 {
741 clearStatusBar();
742 ViewPropertiesDialog dlg(m_activeView);
743 dlg.exec();
744 }
745
746 void DolphinMainWindow::goBack()
747 {
748 clearStatusBar();
749 m_activeView->goBack();
750 }
751
752 void DolphinMainWindow::goForward()
753 {
754 clearStatusBar();
755 m_activeView->goForward();
756 }
757
758 void DolphinMainWindow::goUp()
759 {
760 clearStatusBar();
761 m_activeView->goUp();
762 }
763
764 void DolphinMainWindow::goHome()
765 {
766 clearStatusBar();
767 m_activeView->goHome();
768 }
769
770 void DolphinMainWindow::openTerminal()
771 {
772 QString command("konsole --workdir \"");
773 command.append(m_activeView->url().path());
774 command.append('\"');
775
776 KRun::runCommand(command, "Konsole", "konsole");
777 }
778
779 void DolphinMainWindow::findFile()
780 {
781 KRun::run("kfind", m_activeView->url());
782 }
783
784 void DolphinMainWindow::compareFiles()
785 {
786 // The method is only invoked if exactly 2 files have
787 // been selected. The selected files may be:
788 // - both in the primary view
789 // - both in the secondary view
790 // - one in the primary view and the other in the secondary
791 // view
792 assert(m_view[PrimaryIdx] != 0);
793
794 KUrl urlA;
795 KUrl urlB;
796 KUrl::List urls = m_view[PrimaryIdx]->selectedUrls();
797
798 switch (urls.count()) {
799 case 0: {
800 assert(m_view[SecondaryIdx] != 0);
801 urls = m_view[SecondaryIdx]->selectedUrls();
802 assert(urls.count() == 2);
803 urlA = urls[0];
804 urlB = urls[1];
805 break;
806 }
807
808 case 1: {
809 urlA = urls[0];
810 assert(m_view[SecondaryIdx] != 0);
811 urls = m_view[SecondaryIdx]->selectedUrls();
812 assert(urls.count() == 1);
813 urlB = urls[0];
814 break;
815 }
816
817 case 2: {
818 urlA = urls[0];
819 urlB = urls[1];
820 break;
821 }
822
823 default: {
824 // may not happen: compareFiles may only get invoked if 2
825 // files are selected
826 assert(false);
827 }
828 }
829
830 QString command("kompare -c \"");
831 command.append(urlA.pathOrUrl());
832 command.append("\" \"");
833 command.append(urlB.pathOrUrl());
834 command.append('\"');
835 KRun::runCommand(command, "Kompare", "kompare");
836
837 }
838
839 void DolphinMainWindow::editSettings()
840 {
841 // TODO: make a static method for opening the settings dialog
842 DolphinSettingsDialog dlg(this);
843 dlg.exec();
844 }
845
846 void DolphinMainWindow::init()
847 {
848 // Check whether Dolphin runs the first time. If yes then
849 // a proper default window size is given at the end of DolphinMainWindow::init().
850 GeneralSettings* generalSettings = DolphinSettings::instance().generalSettings();
851 const bool firstRun = generalSettings->firstRun();
852
853 setAcceptDrops(true);
854
855 m_splitter = new QSplitter(this);
856
857 DolphinSettings& settings = DolphinSettings::instance();
858
859 KBookmarkManager* manager = settings.bookmarkManager();
860 assert(manager != 0);
861 KBookmarkGroup root = manager->root();
862 if (root.first().isNull()) {
863 root.addBookmark(manager, i18n("Home"), settings.generalSettings()->homeUrl(), "folder_home");
864 root.addBookmark(manager, i18n("Storage Media"), KUrl("media:/"), "blockdevice");
865 root.addBookmark(manager, i18n("Network"), KUrl("remote:/"), "network_local");
866 root.addBookmark(manager, i18n("Root"), KUrl("/"), "folder_red");
867 root.addBookmark(manager, i18n("Trash"), KUrl("trash:/"), "trashcan_full");
868 }
869
870 setupActions();
871
872 const KUrl& homeUrl = root.first().url();
873 setCaption(homeUrl.fileName());
874 ViewProperties props(homeUrl);
875 m_view[PrimaryIdx] = new DolphinView(this,
876 m_splitter,
877 homeUrl,
878 props.viewMode(),
879 props.showHiddenFiles());
880 connectViewSignals(PrimaryIdx);
881 m_view[PrimaryIdx]->show();
882
883 m_activeView = m_view[PrimaryIdx];
884
885 setCentralWidget(m_splitter);
886 setupDockWidgets();
887
888 setupGUI(Keys|Save|Create|ToolBar);
889 createGUI();
890
891 stateChanged("new_file");
892 setAutoSaveSettings();
893
894 QClipboard* clipboard = QApplication::clipboard();
895 connect(clipboard, SIGNAL(dataChanged()),
896 this, SLOT(updatePasteAction()));
897 updatePasteAction();
898 updateGoActions();
899
900 loadSettings();
901
902 if (firstRun) {
903 // assure a proper default size if Dolphin runs the first time
904 resize(640, 480);
905 }
906 }
907
908 void DolphinMainWindow::loadSettings()
909 {
910 GeneralSettings* settings = DolphinSettings::instance().generalSettings();
911
912 KToggleAction* splitAction = static_cast<KToggleAction*>(actionCollection()->action("split_view"));
913 if (settings->splitView()) {
914 splitAction->setChecked(true);
915 toggleSplitView();
916 }
917
918 updateViewActions();
919
920 // TODO: I assume there will be a generic way in KDE 4 to restore the docks
921 // of the main window. In the meantime they are restored manually (see also
922 // DolphinMainWindow::closeEvent() for more details):
923 QString filename = KStandardDirs::locateLocal("data", KGlobal::instance()->instanceName());
924 filename.append("/panels_layout");
925 QFile file(filename);
926 if (file.open(QIODevice::ReadOnly)) {
927 QByteArray data = file.readAll();
928 restoreState(data);
929 file.close();
930 }
931 }
932
933 void DolphinMainWindow::setupActions()
934 {
935 // setup 'File' menu
936 m_newMenu = new KNewMenu(actionCollection(), this, "create_new");
937 KMenu* menu = m_newMenu->menu();
938 menu->setTitle(i18n("Create New..."));
939 menu->setIcon(SmallIcon("filenew"));
940 connect(menu, SIGNAL(aboutToShow()),
941 this, SLOT(updateNewMenu()));
942
943 QAction* action = actionCollection()->addAction("new_window");
944 action->setIcon(KIcon("window_new"));
945 action->setText(i18n("New &Window"));
946 connect(action, SIGNAL(triggered()), this, SLOT(openNewMainWindow()));
947
948 QAction* rename = actionCollection()->addAction("rename");
949 rename->setText(i18n("Rename"));
950 rename->setShortcut(Qt::Key_F2);
951 connect(rename, SIGNAL(triggered()), this, SLOT(rename()));
952
953 QAction* moveToTrash = actionCollection()->addAction("move_to_trash");
954 moveToTrash->setText(i18n("Move to Trash"));
955 moveToTrash->setIcon(KIcon("edittrash"));
956 moveToTrash->setShortcut(QKeySequence::Delete);
957 connect(moveToTrash, SIGNAL(triggered()), this, SLOT(moveToTrash()));
958
959 QAction* deleteAction = actionCollection()->addAction("delete");
960 deleteAction->setText(i18n("Delete"));
961 deleteAction->setShortcut(Qt::ALT | Qt::Key_Delete);
962 deleteAction->setIcon(KIcon("editdelete"));
963 connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteItems()));
964
965 QAction* properties = actionCollection()->addAction("properties");
966 properties->setText(i18n("Propert&ies"));
967 properties->setShortcut(Qt::Key_Alt | Qt::Key_Return);
968 connect(properties, SIGNAL(triggered()), this, SLOT(properties()));
969
970 KStandardAction::quit(this, SLOT(quit()), actionCollection());
971
972 // setup 'Edit' menu
973 KStandardAction::undo(KonqUndoManager::self(),
974 SLOT(undo()),
975 actionCollection());
976
977 KStandardAction::cut(this, SLOT(cut()), actionCollection());
978 KStandardAction::copy(this, SLOT(copy()), actionCollection());
979 KStandardAction::paste(this, SLOT(paste()), actionCollection());
980
981 QAction* selectAll = actionCollection()->addAction("select_all");
982 selectAll->setText(i18n("Select All"));
983 selectAll->setShortcut(Qt::CTRL + Qt::Key_A);
984 connect(selectAll, SIGNAL(triggered()), this, SLOT(selectAll()));
985
986 QAction* invertSelection = actionCollection()->addAction("invert_selection");
987 invertSelection->setText(i18n("Invert Selection"));
988 invertSelection->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_A);
989 connect(invertSelection, SIGNAL(triggered()), this, SLOT(invertSelection()));
990
991 // setup 'View' menu
992 KStandardAction::zoomIn(this,
993 SLOT(zoomIn()),
994 actionCollection());
995
996 KStandardAction::zoomOut(this,
997 SLOT(zoomOut()),
998 actionCollection());
999
1000 KToggleAction* iconsView = actionCollection()->add<KToggleAction>("icons");
1001 iconsView->setText(i18n("Icons"));
1002 iconsView->setShortcut(Qt::CTRL | Qt::Key_1);
1003 iconsView->setIcon(KIcon("view_icon"));
1004 connect(iconsView, SIGNAL(triggered()), this, SLOT(setIconsView()));
1005
1006 KToggleAction* detailsView = actionCollection()->add<KToggleAction>("details");
1007 detailsView->setText(i18n("Details"));
1008 detailsView->setShortcut(Qt::CTRL | Qt::Key_2);
1009 detailsView->setIcon(KIcon("view_text"));
1010 connect(detailsView, SIGNAL(triggered()), this, SLOT(setDetailsView()));
1011
1012 QActionGroup* viewModeGroup = new QActionGroup(this);
1013 viewModeGroup->addAction(iconsView);
1014 viewModeGroup->addAction(detailsView);
1015
1016 KToggleAction* sortByName = actionCollection()->add<KToggleAction>("by_name");
1017 sortByName->setText(i18n("By Name"));
1018 connect(sortByName, SIGNAL(triggered()), this, SLOT(sortByName()));
1019
1020 KToggleAction* sortBySize = actionCollection()->add<KToggleAction>("by_size");
1021 sortBySize->setText(i18n("By Size"));
1022 connect(sortBySize, SIGNAL(triggered()), this, SLOT(sortBySize()));
1023
1024 KToggleAction* sortByDate = actionCollection()->add<KToggleAction>("by_date");
1025 sortByDate->setText(i18n("By Date"));
1026 connect(sortByDate, SIGNAL(triggered()), this, SLOT(sortByDate()));
1027
1028 QActionGroup* sortGroup = new QActionGroup(this);
1029 sortGroup->addAction(sortByName);
1030 sortGroup->addAction(sortBySize);
1031 sortGroup->addAction(sortByDate);
1032
1033 KToggleAction* sortDescending = actionCollection()->add<KToggleAction>("descending");
1034 sortDescending->setText(i18n("Descending"));
1035 connect(sortDescending, SIGNAL(triggered()), this, SLOT(toggleSortOrder()));
1036
1037 KToggleAction* showPreview = actionCollection()->add<KToggleAction>("show_preview");
1038 showPreview->setText(i18n("Show Preview"));
1039 connect(showPreview, SIGNAL(triggered()), this, SLOT(togglePreview()));
1040
1041 KToggleAction* showHiddenFiles = actionCollection()->add<KToggleAction>("show_hidden_files");
1042 showHiddenFiles->setText(i18n("Show Hidden Files"));
1043 //showHiddenFiles->setShortcut(Qt::ALT | Qt::Key_ KDE4-TODO: what Qt-Key represents '.'?
1044 connect(showHiddenFiles, SIGNAL(triggered()), this, SLOT(toggleShowHiddenFiles()));
1045
1046 KToggleAction* split = actionCollection()->add<KToggleAction>("split_view");
1047 split->setText(i18n("Split View"));
1048 split->setShortcut(Qt::Key_F10);
1049 split->setIcon(KIcon("view_left_right"));
1050 connect(split, SIGNAL(triggered()), this, SLOT(toggleSplitView()));
1051
1052 QAction* reload = actionCollection()->addAction("reload");
1053 reload->setText(i18n("Reload"));
1054 reload->setShortcut(Qt::Key_F5);
1055 reload->setIcon(KIcon("reload"));
1056 connect(reload, SIGNAL(triggered()), this, SLOT(reloadView()));
1057
1058 QAction* stop = actionCollection()->addAction("stop");
1059 stop->setText(i18n("Stop"));
1060 stop->setIcon(KIcon("stop"));
1061 connect(stop, SIGNAL(triggered()), this, SLOT(stopLoading()));
1062
1063 KToggleAction* showFullLocation = actionCollection()->add<KToggleAction>("editable_location");
1064 showFullLocation->setText(i18n("Show Full Location"));
1065 showFullLocation->setShortcut(Qt::CTRL | Qt::Key_L);
1066 connect(showFullLocation, SIGNAL(triggered()), this, SLOT(toggleEditLocation()));
1067
1068 KToggleAction* editLocation = actionCollection()->add<KToggleAction>("edit_location");
1069 editLocation->setText(i18n("Edit Location"));
1070 editLocation->setShortcut(Qt::Key_F6);
1071 connect(editLocation, SIGNAL(triggered()), this, SLOT(editLocation()));
1072
1073 QAction* adjustViewProps = actionCollection()->addAction("view_properties");
1074 adjustViewProps->setText(i18n("Adjust View Properties..."));
1075 connect(adjustViewProps, SIGNAL(triggered()), this, SLOT(adjustViewProperties()));
1076
1077 // setup 'Go' menu
1078 KStandardAction::back(this, SLOT(goBack()), actionCollection());
1079 KStandardAction::forward(this, SLOT(goForward()), actionCollection());
1080 KStandardAction::up(this, SLOT(goUp()), actionCollection());
1081 KStandardAction::home(this, SLOT(goHome()), actionCollection());
1082
1083 // setup 'Tools' menu
1084 QAction* openTerminal = actionCollection()->addAction("open_terminal");
1085 openTerminal->setText(i18n("Open Terminal"));
1086 openTerminal->setShortcut(Qt::Key_F4);
1087 openTerminal->setIcon(KIcon("konsole"));
1088 connect(openTerminal, SIGNAL(triggered()), this, SLOT(openTerminal()));
1089
1090 QAction* findFile = actionCollection()->addAction("find_file");
1091 findFile->setText(i18n("Find File..."));
1092 findFile->setShortcut(Qt::Key_F);
1093 findFile->setIcon(KIcon("filefind"));
1094 connect(findFile, SIGNAL(triggered()), this, SLOT(findFile()));
1095
1096 KToggleAction* showFilterBar = actionCollection()->add<KToggleAction>("show_filter_bar");
1097 showFilterBar->setText(i18n("Show Filter Bar"));
1098 showFilterBar->setShortcut(Qt::Key_Slash);
1099 connect(showFilterBar, SIGNAL(triggered()), this, SLOT(showFilterBar()));
1100
1101 QAction* compareFiles = actionCollection()->addAction("compare_files");
1102 compareFiles->setText(i18n("Compare Files"));
1103 compareFiles->setIcon(KIcon("kompare"));
1104 compareFiles->setEnabled(false);
1105 connect(compareFiles, SIGNAL(triggered()), this, SLOT(compareFiles()));
1106
1107 // setup 'Settings' menu
1108 KStandardAction::preferences(this, SLOT(editSettings()), actionCollection());
1109 }
1110
1111 void DolphinMainWindow::setupDockWidgets()
1112 {
1113 QDockWidget* shortcutsDock = new QDockWidget(i18n("Bookmarks"));
1114 shortcutsDock->setObjectName("bookmarksDock");
1115 shortcutsDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1116 shortcutsDock->setWidget(new BookmarksSidebarPage(this));
1117
1118 shortcutsDock->toggleViewAction()->setText(i18n("Show Bookmarks Panel"));
1119 actionCollection()->addAction("show_bookmarks_panel", shortcutsDock->toggleViewAction());
1120
1121 addDockWidget(Qt::LeftDockWidgetArea, shortcutsDock);
1122
1123 QDockWidget* infoDock = new QDockWidget(i18n("Information"));
1124 infoDock->setObjectName("infoDock");
1125 infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1126 infoDock->setWidget(new InfoSidebarPage(this));
1127
1128 infoDock->toggleViewAction()->setText(i18n("Show Information Panel"));
1129 actionCollection()->addAction("show_info_panel", infoDock->toggleViewAction());
1130
1131 addDockWidget(Qt::RightDockWidgetArea, infoDock);
1132 }
1133
1134 void DolphinMainWindow::updateHistory()
1135 {
1136 int index = 0;
1137 const QLinkedList<UrlNavigator::HistoryElem> list = m_activeView->urlHistory(index);
1138
1139 QAction* backAction = actionCollection()->action("go_back");
1140 if (backAction != 0) {
1141 backAction->setEnabled(index < static_cast<int>(list.count()) - 1);
1142 }
1143
1144 QAction* forwardAction = actionCollection()->action("go_forward");
1145 if (forwardAction != 0) {
1146 forwardAction->setEnabled(index > 0);
1147 }
1148 }
1149
1150 void DolphinMainWindow::updateEditActions()
1151 {
1152 const KFileItemList list = m_activeView->selectedItems();
1153 if (list.isEmpty()) {
1154 stateChanged("has_no_selection");
1155 }
1156 else {
1157 stateChanged("has_selection");
1158
1159 QAction* renameAction = actionCollection()->action("rename");
1160 if (renameAction != 0) {
1161 renameAction->setEnabled(list.count() >= 1);
1162 }
1163
1164 bool enableMoveToTrash = true;
1165
1166 KFileItemList::const_iterator it = list.begin();
1167 const KFileItemList::const_iterator end = list.end();
1168 while (it != end) {
1169 KFileItem* item = *it;
1170 const KUrl& url = item->url();
1171 // only enable the 'Move to Trash' action for local files
1172 if (!url.isLocalFile()) {
1173 enableMoveToTrash = false;
1174 }
1175 ++it;
1176 }
1177
1178 QAction* moveToTrashAction = actionCollection()->action("move_to_trash");
1179 moveToTrashAction->setEnabled(enableMoveToTrash);
1180 }
1181 updatePasteAction();
1182 }
1183
1184 void DolphinMainWindow::updateViewActions()
1185 {
1186 QAction* zoomInAction = actionCollection()->action(KStandardAction::stdName(KStandardAction::ZoomIn));
1187 if (zoomInAction != 0) {
1188 zoomInAction->setEnabled(m_activeView->isZoomInPossible());
1189 }
1190
1191 QAction* zoomOutAction = actionCollection()->action(KStandardAction::stdName(KStandardAction::ZoomOut));
1192 if (zoomOutAction != 0) {
1193 zoomOutAction->setEnabled(m_activeView->isZoomOutPossible());
1194 }
1195
1196 QAction* action = 0;
1197 switch (m_activeView->mode()) {
1198 case DolphinView::IconsView:
1199 action = actionCollection()->action("icons");
1200 break;
1201 case DolphinView::DetailsView:
1202 action = actionCollection()->action("details");
1203 break;
1204 default:
1205 break;
1206 }
1207
1208 if (action != 0) {
1209 KToggleAction* toggleAction = static_cast<KToggleAction*>(action);
1210 toggleAction->setChecked(true);
1211 }
1212
1213 slotSortingChanged(m_activeView->sorting());
1214 slotSortOrderChanged(m_activeView->sortOrder());
1215
1216 KToggleAction* showFilterBarAction =
1217 static_cast<KToggleAction*>(actionCollection()->action("show_filter_bar"));
1218 showFilterBarAction->setChecked(m_activeView->isFilterBarVisible());
1219
1220 KToggleAction* showHiddenFilesAction =
1221 static_cast<KToggleAction*>(actionCollection()->action("show_hidden_files"));
1222 showHiddenFilesAction->setChecked(m_activeView->showHiddenFiles());
1223
1224 KToggleAction* splitAction = static_cast<KToggleAction*>(actionCollection()->action("split_view"));
1225 splitAction->setChecked(m_view[SecondaryIdx] != 0);
1226 }
1227
1228 void DolphinMainWindow::updateGoActions()
1229 {
1230 QAction* goUpAction = actionCollection()->action(KStandardAction::stdName(KStandardAction::Up));
1231 const KUrl& currentUrl = m_activeView->url();
1232 goUpAction->setEnabled(currentUrl.upUrl() != currentUrl);
1233 }
1234
1235 void DolphinMainWindow::copyUrls(const KUrl::List& source, const KUrl& dest)
1236 {
1237 KonqOperations::copy(this, KonqOperations::COPY, source, dest);
1238 m_undoOperations.append(KonqOperations::COPY);
1239 }
1240
1241 void DolphinMainWindow::moveUrls(const KUrl::List& source, const KUrl& dest)
1242 {
1243 KonqOperations::copy(this, KonqOperations::MOVE, source, dest);
1244 m_undoOperations.append(KonqOperations::MOVE);
1245 }
1246
1247 void DolphinMainWindow::clearStatusBar()
1248 {
1249 m_activeView->statusBar()->clear();
1250 }
1251
1252 void DolphinMainWindow::connectViewSignals(int viewIndex)
1253 {
1254 DolphinView* view = m_view[viewIndex];
1255 connect(view, SIGNAL(modeChanged()),
1256 this, SLOT(slotViewModeChanged()));
1257 connect(view, SIGNAL(showHiddenFilesChanged()),
1258 this, SLOT(slotShowHiddenFilesChanged()));
1259 connect(view, SIGNAL(sortingChanged(DolphinView::Sorting)),
1260 this, SLOT(slotSortingChanged(DolphinView::Sorting)));
1261 connect(view, SIGNAL(sortOrderChanged(Qt::SortOrder)),
1262 this, SLOT(slotSortOrderChanged(Qt::SortOrder)));
1263 connect(view, SIGNAL(selectionChanged()),
1264 this, SLOT(slotSelectionChanged()));
1265 connect(view, SIGNAL(showFilterBarChanged(bool)),
1266 this, SLOT(updateFilterBarAction(bool)));
1267
1268 const UrlNavigator* navigator = view->urlNavigator();
1269 connect(navigator, SIGNAL(urlChanged(const KUrl&)),
1270 this, SLOT(slotUrlChanged(const KUrl&)));
1271 connect(navigator, SIGNAL(historyChanged()),
1272 this, SLOT(slotHistoryChanged()));
1273
1274 }
1275
1276 #include "dolphinmainwindow.moc"