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