1/* This file is part of the KDE project
2 Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
3 Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
4 Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
5 Copyright (C) 2007 Flavio Castelli <flavio.castelli@gmail.com>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License version 2 as published by the Free Software Foundation.
10
11 This library 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 GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22//BEGIN Includes
23#include "katemainwindow.h"
24#include "katemainwindow.moc"
25
26#include "kateconfigdialog.h"
27#include "katedocmanager.h"
28#include "katepluginmanager.h"
29#include "kateconfigplugindialogpage.h"
30#include "kateviewmanager.h"
31#include "kateapp.h"
32#include "katesavemodifieddialog.h"
33#include "katemwmodonhddialog.h"
34#include "katesession.h"
35#include "kateviewspace.h"
36#include "katequickopen.h"
37
38#include <kate/mainwindow.h>
39
40#include <KAboutApplicationDialog>
41#include <KAction>
42#include <kdebug.h>
43#include <KEditToolBar>
44#include <KGlobal>
45#include <KShortcutsDialog>
46#include <KLocale>
47#include <KMessageBox>
48#include <KMimeType>
49#include <KOpenWithDialog>
50#include <KMenu>
51#include <KConfig>
52#include <kstandardaction.h>
53#include <KMimeTypeTrader>
54#include <KMultiTabBar>
55#include <KTipDialog>
56#include <KStringHandler>
57#include <KToolInvocation>
58#include <KAuthorized>
59#include <KRun>
60#include <KRecentFilesAction>
61#include <KToggleFullScreenAction>
62#include <KActionCollection>
63#include <KAboutData>
64#include <kwindowsystem.h>
65#include <KToolBar>
66
67#include <QDragEnterEvent>
68#include <QEvent>
69#include <QDropEvent>
70#include <QList>
71#include <QDesktopWidget>
72
73#include <kio/job.h>
74#include <KIO/ListJob>
75#include <KFileItem>
76
77#include <assert.h>
78#include <unistd.h>
79#include <kmenubar.h>
80#include <kstandardaction.h>
81//END
82
83uint KateMainWindow::uniqueID = 1;
84KateMwModOnHdDialog *KateMainWindow::s_modOnHdDialog=0;
85
86KateContainerStackedLayout::KateContainerStackedLayout(QWidget* parent)
87 : QStackedLayout(parent)
88{}
89
90QSize KateContainerStackedLayout::sizeHint() const
91{
92 if (currentWidget())
93 return currentWidget()->sizeHint();
94 return QStackedLayout::sizeHint();
95}
96
97QSize KateContainerStackedLayout::minimumSize() const
98{
99 if (currentWidget())
100 return currentWidget()->minimumSize();
101 return QStackedLayout::minimumSize();
102}
103
104
105KateMainWindow::KateMainWindow (KConfig *sconfig, const QString &sgroup)
106 : KateMDI::MainWindow (0)
107{
108 setObjectName((QString("__KateMainWindow#%1").arg(uniqueID)).toLatin1());
109 // first the very important id
110 myID = uniqueID;
111 uniqueID++;
112
113 m_modignore = false;
114
115 // here we go, set some usable default sizes
116 if (!initialGeometrySet())
117 {
118 int scnum = QApplication::desktop()->screenNumber(parentWidget());
119 QRect desk = QApplication::desktop()->screenGeometry(scnum);
120
121 QSize size;
122
123 // try to load size
124 if (sconfig)
125 {
126 KConfigGroup cg( sconfig, sgroup );
127 size.setWidth (cg.readEntry( QString::fromLatin1("Width %1").arg(desk.width()), 0 ));
128 size.setHeight (cg.readEntry( QString::fromLatin1("Height %1").arg(desk.height()), 0 ));
129 }
130
131 // if thats fails, try to reuse size
132 if (size.isEmpty())
133 {
134 // first try to reuse size known from current or last created main window ;=)
135 if (KateApp::self()->mainWindows () > 0)
136 {
137 KateMainWindow *win = KateApp::self()->activeMainWindow ();
138
139 if (!win)
140 win = KateApp::self()->mainWindow (KateApp::self()->mainWindows () - 1);
141
142 size = win->size();
143 }
144 else // now fallback to hard defaults ;)
145 {
146 // first try global app config
147 KConfigGroup cg( KGlobal::config(), "MainWindow" );
148 size.setWidth (cg.readEntry( QString::fromLatin1("Width %1").arg(desk.width()), 0 ));
149 size.setHeight (cg.readEntry( QString::fromLatin1("Height %1").arg(desk.height()), 0 ));
150
151 if (size.isEmpty())
152 size = QSize (qMin (700, desk.width()), qMin(480, desk.height()));
153 }
154
155 resize (size);
156 }
157 }
158
159 // start session restore if needed
160 startRestore (sconfig, sgroup);
161
162 m_mainWindow = new Kate::MainWindow (this);
163
164 // setup most important actions first, needed by setupMainWindow
165 setupImportantActions ();
166
167 // setup the most important widgets
168 setupMainWindow();
169
170 // setup the actions
171 setupActions();
172
173 setStandardToolBarMenuEnabled( true );
174 setXMLFile( "kateui.rc" );
175 createShellGUI ( true );
176
177 //kDebug() << "****************************************************************************" << sconfig;
178
179 // register mainwindow in app
180 KateApp::self()->addMainWindow (this);
181
182 // enable plugin guis
183 KatePluginManager::self()->enableAllPluginsGUI (this, sconfig);
184
185 // caption update
186 for (uint i = 0; i < KateDocManager::self()->documents(); i++)
187 slotDocumentCreated (KateDocManager::self()->document(i));
188
189 connect(KateDocManager::self(), SIGNAL(documentCreated(KTextEditor::Document*)), this, SLOT(slotDocumentCreated(KTextEditor::Document*)));
190
191 readOptions();
192
193 if (sconfig)
194 m_viewManager->restoreViewConfiguration (KConfigGroup(sconfig, sgroup) );
195
196 finishRestore ();
197
198 fileOpenRecent->loadEntries( KConfigGroup(sconfig, "Recent Files" ) );
199
200 setAcceptDrops(true);
201
202 connect(KateSessionManager::self(), SIGNAL(sessionChanged()), this, SLOT(updateCaption()));
203
204 connect(this,SIGNAL(sigShowPluginConfigPage(Kate::PluginConfigPageInterface *,uint)),this,SLOT(showPluginConfigPage(Kate::PluginConfigPageInterface *,uint)));
205
206 // prior to this there was (possibly) no view, therefore not context menu.
207 // Hence, we have to take care of the menu bar here
208 toggleShowMenuBar(false);
209}
210
211KateMainWindow::~KateMainWindow()
212{
213 // first, save our fallback window size ;)
214 saveWindowSize (KConfigGroup(KGlobal::config(), "MainWindow"));
215
216 // save other options ;=)
217 saveOptions();
218
219 // unregister mainwindow in app
220 KateApp::self()->removeMainWindow (this);
221
222 // disable all plugin guis, delete all pluginViews
223 KatePluginManager::self()->disableAllPluginsGUI (this);
224}
225
226void KateMainWindow::setupImportantActions ()
227{
228 // settings
229 m_paShowStatusBar = KStandardAction::showStatusbar(this, SLOT(toggleShowStatusBar()), actionCollection());
230 m_paShowStatusBar->setWhatsThis(i18n("Use this command to show or hide the view's statusbar"));
231 m_paShowMenuBar = KStandardAction::showMenubar(this, SLOT(toggleShowMenuBar()), actionCollection());
232
233 m_paShowPath = new KToggleAction( i18n("Sho&w Path in Titlebar"), this );
234 actionCollection()->addAction( "settings_show_full_path", m_paShowPath );
235 connect( m_paShowPath, SIGNAL(toggled(bool)), this, SLOT(updateCaption()) );
236 m_paShowPath->setWhatsThis(i18n("Show the complete document path in the window caption"));
237}
238
239void KateMainWindow::setupMainWindow ()
240{
241 setToolViewStyle( KMultiTabBar::KDEV3ICON );
242
243 m_topViewBarContainer=new QWidget(centralWidget());
244 m_topContainerStack = new KateContainerStackedLayout(m_topViewBarContainer);
245
246 /**
247 * create central stacked widget with its children
248 */
249 m_mainStackedWidget = new QStackedWidget (centralWidget());
250 ((QBoxLayout*)(centralWidget()->layout()))->setStretchFactor(m_mainStackedWidget,100);
251
252 m_quickOpen = new KateQuickOpen (m_mainStackedWidget, this);
253 m_mainStackedWidget->addWidget (m_quickOpen);
254
255 m_viewManager = new KateViewManager (m_mainStackedWidget, this);
256 m_mainStackedWidget->addWidget (m_viewManager);
257
258 // make view manager default visible!
259 m_mainStackedWidget->setCurrentWidget (m_viewManager);
260
261 m_bottomViewBarContainer=new QWidget(centralWidget());
262 m_bottomContainerStack = new KateContainerStackedLayout(m_bottomViewBarContainer);
263}
264
265void KateMainWindow::setupActions()
266{
267 KAction *a;
268
269 actionCollection()->addAction( KStandardAction::New, "file_new", m_viewManager, SLOT(slotDocumentNew()) )
270 ->setWhatsThis(i18n("Create a new document"));
271 actionCollection()->addAction( KStandardAction::Open, "file_open", m_viewManager, SLOT(slotDocumentOpen()) )
272 ->setWhatsThis(i18n("Open an existing document for editing"));
273
274 fileOpenRecent = KStandardAction::openRecent (m_viewManager, SLOT(openUrl(KUrl)), this);
275 actionCollection()->addAction(fileOpenRecent->objectName(), fileOpenRecent);
276 fileOpenRecent->setWhatsThis(i18n("This lists files which you have opened recently, and allows you to easily open them again."));
277
278 a = actionCollection()->addAction( "file_save_all" );
279 a->setIcon( KIcon("document-save-all") );
280 a->setText( i18n("Save A&ll") );
281 a->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L) );
282 connect( a, SIGNAL(triggered()), KateDocManager::self(), SLOT(saveAll()) );
283 a->setWhatsThis(i18n("Save all open, modified documents to disk."));
284
285 a = actionCollection()->addAction( "file_reload_all" );
286 a->setText( i18n("&Reload All") );
287 connect( a, SIGNAL(triggered()), KateDocManager::self(), SLOT(reloadAll()) );
288 a->setWhatsThis(i18n("Reload all open documents."));
289
290 a = actionCollection()->addAction( "file_close_orphaned" );
291 a->setText( i18n("Close Orphaned") );
292 connect( a, SIGNAL(triggered()), KateDocManager::self(), SLOT(closeOrphaned()) );
293 a->setWhatsThis(i18n("Close all documents in the file list that could not be reopened, because they are not accessible anymore."));
294
295 actionCollection()->addAction( KStandardAction::Close, "file_close", m_viewManager, SLOT(slotDocumentClose()) )
296 ->setWhatsThis(i18n("Close the current document."));
297
298 a = actionCollection()->addAction( "file_close_other" );
299 a->setText( i18n( "Close Other" ) );
300 connect( a, SIGNAL(triggered()), this, SLOT(slotDocumentCloseOther()) );
301 a->setWhatsThis(i18n("Close other open documents."));
302
303 a = actionCollection()->addAction( "file_close_all" );
304 a->setText( i18n( "Clos&e All" ) );
305 connect( a, SIGNAL(triggered()), this, SLOT(slotDocumentCloseAll()) );
306 a->setWhatsThis(i18n("Close all open documents."));
307
308 a = actionCollection()->addAction( KStandardAction::Quit, "file_quit" );
309 // Qt::QueuedConnection: delay real shutdown, as we are inside menu action handling (bug #185708)
310 connect( a, SIGNAL(triggered()), this, SLOT(slotFileQuit()), Qt::QueuedConnection );
311 a->setWhatsThis(i18n("Close this window"));
312
313 a = actionCollection()->addAction( "view_new_view" );
314 a->setIcon( KIcon("window-new") );
315 a->setText( i18n("&New Window") );
316 connect( a, SIGNAL(triggered()), this, SLOT(newWindow()) );
317 a->setWhatsThis(i18n("Create a new Kate view (a new window with the same document list)."));
318
319 a = actionCollection()->addAction( "view_quick_open" );
320 a->setIcon( KIcon("fork") );
321 a->setText( i18n("&Quick Open") );
322 QList<QKeySequence> scuts;
323 scuts << QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_O)
324 << QKeySequence(Qt::CTRL + Qt::Key_Tab);
325 a->setShortcuts(scuts);
326 connect( a, SIGNAL(triggered()), this, SLOT(slotQuickOpen()) );
327 a->setWhatsThis(i18n("Open a form to quick open documents."));
328
329 KToggleAction* showFullScreenAction = KStandardAction::fullScreen( 0, 0, this, this);
330 actionCollection()->addAction( showFullScreenAction->objectName(), showFullScreenAction );
331 connect( showFullScreenAction, SIGNAL(toggled(bool)), this, SLOT(slotFullScreen(bool)));
332
333 documentOpenWith = new KActionMenu(i18n("Open W&ith"), this);
334 actionCollection()->addAction("file_open_with", documentOpenWith);
335 documentOpenWith->setWhatsThis(i18n("Open the current document using another application registered for its file type, or an application of your choice."));
336 connect(documentOpenWith->menu(), SIGNAL(aboutToShow()), this, SLOT(mSlotFixOpenWithMenu()));
337 connect(documentOpenWith->menu(), SIGNAL(triggered(QAction*)), this, SLOT(slotOpenWithMenuAction(QAction*)));
338
339 a = KStandardAction::keyBindings(this, SLOT(editKeys()), actionCollection());
340 a->setWhatsThis(i18n("Configure the application's keyboard shortcut assignments."));
341
342 a = KStandardAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection());
343 a->setWhatsThis(i18n("Configure which items should appear in the toolbar(s)."));
344
345 QAction* settingsConfigure = KStandardAction::preferences(this, SLOT(slotConfigure()), actionCollection());
346 settingsConfigure->setWhatsThis(i18n("Configure various aspects of this application and the editing component."));
347
348 // tip of the day :-)
349 actionCollection()->addAction( KStandardAction::TipofDay, this, SLOT(tipOfTheDay()) )
350 ->setWhatsThis(i18n("This shows useful tips on the use of this application."));
351
352 if (KatePluginManager::self()->pluginList().count() > 0)
353 {
354 a = actionCollection()->addAction( "help_plugins_contents" );
355 a->setText( i18n("&Plugins Handbook") );
356 connect( a, SIGNAL(triggered()), this, SLOT(pluginHelp()) );
357 a->setWhatsThis(i18n("This shows help files for various available plugins."));
358 }
359
360 a = actionCollection()->addAction( "help_about_editor" );
361 a->setText( i18n("&About Editor Component") );
362 connect( a, SIGNAL(triggered()), this, SLOT(aboutEditor()) );
363
364 connect(m_viewManager, SIGNAL(viewChanged()), m_mainWindow, SIGNAL(viewChanged()));
365 connect(m_viewManager, SIGNAL(viewCreated(KTextEditor::View*)), m_mainWindow, SIGNAL(viewCreated(KTextEditor::View*)));
366 connect(m_viewManager, SIGNAL(viewChanged()), this, SLOT(slotWindowActivated()));
367 connect(m_viewManager, SIGNAL(viewChanged()), this, SLOT(slotUpdateOpenWith()));
368 connect(m_viewManager, SIGNAL(viewChanged()), this, SLOT(slotUpdateBottomViewBar()));
369 connect(m_viewManager, SIGNAL(viewChanged()), this, SLOT(slotUpdateTopViewBar()));
370
371 slotWindowActivated ();
372
373 // session actions
374 a = actionCollection()->addAction( "sessions_new" );
375 a->setIcon( KIcon("document-new") );
376 a->setText( i18nc("Menu entry Session->New", "&New") );
377 // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008)
378 connect( a, SIGNAL(triggered()), KateSessionManager::self(), SLOT(sessionNew()), Qt::QueuedConnection );
379 a = actionCollection()->addAction( "sessions_open" );
380 a->setIcon( KIcon("document-open") );
381 a->setText( i18n("&Open Session") );
382 // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008)
383 connect( a, SIGNAL(triggered()), KateSessionManager::self(), SLOT(sessionOpen()), Qt::QueuedConnection );
384 a = actionCollection()->addAction( "sessions_save" );
385 a->setIcon( KIcon("document-save") );
386 a->setText( i18n("&Save Session") );
387 connect( a, SIGNAL(triggered()), KateSessionManager::self(), SLOT(sessionSave()) );
388 a = actionCollection()->addAction( "sessions_save_as" );
389 a->setIcon( KIcon("document-save-as") );
390 a->setText( i18n("Save Session &As...") );
391 connect( a, SIGNAL(triggered()), KateSessionManager::self(), SLOT(sessionSaveAs()) );
392 a = actionCollection()->addAction( "sessions_manage" );
393 a->setIcon( KIcon("view-choose") );
394 a->setText( i18n("&Manage Sessions...") );
395 // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008)
396 connect( a, SIGNAL(triggered()), KateSessionManager::self(), SLOT(sessionManage()), Qt::QueuedConnection );
397
398 // quick open menu ;)
399 a = new KateSessionsAction (i18n("&Quick Open Session"), this);
400 actionCollection()->addAction("sessions_list", a);
401}
402
403void KateMainWindow::slotDocumentCloseAll()
404{
405 if ( KateDocManager::self()->documents() >= 1 && KMessageBox::warningContinueCancel(this,
406 i18n ("This will close all open documents. Are you sure you want to continue?"),
407 i18n ("Close all documents"),
408 KStandardGuiItem::cont(),
409 KStandardGuiItem::cancel(),
410 QString("closeAll")) != KMessageBox::Cancel)
411 {
412 if (queryClose_internal())
413 KateDocManager::self()->closeAllDocuments(false);
414 }
415}
416
417
418void KateMainWindow::slotDocumentCloseOther(KTextEditor::Document *document)
419{
420 if (queryClose_internal(document))
421 KateDocManager::self()->closeOtherDocuments(document);
422}
423
424void KateMainWindow::slotDocumentCloseSelected(const QList<KTextEditor::Document*> &docList)
425{
426 QList<KTextEditor::Document *> documents;
427 foreach(KTextEditor::Document *doc, docList)
428 {
429 if(queryClose_internal(doc))
430 documents.append(doc);
431 }
432
433 KateDocManager::self()->closeDocuments(documents);
434}
435
436void KateMainWindow::slotDocumentCloseOther()
437{
438 if (queryClose_internal(m_viewManager->activeView()->document()))
439 KateDocManager::self()->closeOtherDocuments(m_viewManager->activeView()->document());
440}
441
442bool KateMainWindow::queryClose_internal(KTextEditor::Document* doc)
443{
444 uint documentCount = KateDocManager::self()->documents();
445
446 if ( ! showModOnDiskPrompt() )
447 return false;
448
449 QList<KTextEditor::Document*> modifiedDocuments = KateDocManager::self()->modifiedDocumentList();
450 modifiedDocuments.removeAll(doc);
451 bool shutdown = (modifiedDocuments.count() == 0);
452
453 if (!shutdown)
454 {
455 shutdown = KateSaveModifiedDialog::queryClose(this, modifiedDocuments);
456 }
457
458 if ( KateDocManager::self()->documents() > documentCount )
459 {
460 KMessageBox::information (this,
461 i18n ("New file opened while trying to close Kate, closing aborted."),
462 i18n ("Closing Aborted"));
463 shutdown = false;
464 }
465
466 return shutdown;
467}
468
469/**
470 * queryClose(), take care that after the last mainwindow the stuff is closed
471 */
472bool KateMainWindow::queryClose()
473{
474 // session saving, can we close all views ?
475 // just test, not close them actually
476 if (KateApp::self()->sessionSaving())
477 {
478 return queryClose_internal ();
479 }
480
481 // normal closing of window
482 // allow to close all windows until the last without restrictions
483 if ( KateApp::self()->mainWindows () > 1 )
484 return true;
485
486 // last one: check if we can close all documents, try run
487 // and save docs if we really close down !
488 if ( queryClose_internal () )
489 {
490 KateApp::self()->sessionManager()->saveActiveSession(true);
491 return true;
492 }
493
494 return false;
495}
496
497void KateMainWindow::newWindow ()
498{
499 KateApp::self()->newMainWindow (KateApp::self()->sessionManager()->activeSession()->configRead());
500}
501
502void KateMainWindow::slotEditToolbars()
503{
504 saveMainWindowSettings(KConfigGroup(KGlobal::config(), "MainWindow"));
505 KEditToolBar dlg( factory() );
506
507 connect( &dlg, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()) );
508 dlg.exec();
509}
510
511void KateMainWindow::slotNewToolbarConfig()
512{
513 applyMainWindowSettings(KConfigGroup(KGlobal::config(), "MainWindow"));
514}
515
516void KateMainWindow::slotFileQuit()
517{
518 KateApp::self()->shutdownKate (this);
519}
520
521void KateMainWindow::slotFileClose()
522{
523 m_viewManager->slotDocumentClose();
524}
525
526void KateMainWindow::slotOpenDocument(KUrl url)
527{
528 m_viewManager->openUrl(url,
529 QString(),
530 true,
531 false);
532}
533
534void KateMainWindow::readOptions ()
535{
536 KSharedConfig::Ptr config = KGlobal::config();
537
538 const KConfigGroup generalGroup(config, "General");
539 modNotification = generalGroup.readEntry("Modified Notification", false);
540 KateDocManager::self()->setSaveMetaInfos(generalGroup.readEntry("Save Meta Infos", true));
541 KateDocManager::self()->setDaysMetaInfos(generalGroup.readEntry("Days Meta Infos", 30));
542
543 m_paShowPath->setChecked (generalGroup.readEntry("Show Full Path in Title", false));
544 m_paShowStatusBar->setChecked (generalGroup.readEntry("Show Status Bar", true));
545 m_paShowMenuBar->setChecked(generalGroup.readEntry("Show Menu Bar", true));
546
547 // emit signal to hide/show statusbars
548 toggleShowStatusBar ();
549
550 // update visibility of menu bar
551 toggleShowMenuBar(false);
552}
553
554void KateMainWindow::saveOptions ()
555{
556 KSharedConfig::Ptr config = KGlobal::config();
557
558 KConfigGroup generalGroup(config, "General");
559
560 generalGroup.writeEntry("Save Meta Infos", KateDocManager::self()->getSaveMetaInfos());
561
562 generalGroup.writeEntry("Days Meta Infos", KateDocManager::self()->getDaysMetaInfos());
563
564 generalGroup.writeEntry("Show Full Path in Title", m_paShowPath->isChecked());
565 generalGroup.writeEntry("Show Status Bar", m_paShowStatusBar->isChecked());
566 generalGroup.writeEntry("Show Menu Bar", m_paShowMenuBar->isChecked());
567}
568
569void KateMainWindow::toggleShowMenuBar(bool showMessage)
570{
571 if (m_paShowMenuBar->isChecked()) {
572 menuBar()->show();
573 removeMenuBarActionFromContextMenu();
574 } else {
575 if (showMessage) {
576 const QString accel = m_paShowMenuBar->shortcut().toString();
577 KMessageBox::information(this, i18n("This will hide the menu bar completely."
578 " You can show it again by typing %1.", accel),
579 i18n("Hide menu bar"), QLatin1String("HideMenuBarWarning"));
580 }
581 menuBar()->hide();
582 addMenuBarActionToContextMenu();
583 }
584}
585
586void KateMainWindow::addMenuBarActionToContextMenu()
587{
588 if (m_viewManager->activeView()) {
589 m_viewManager->activeView()->contextMenu()->addAction(m_paShowMenuBar);
590 }
591}
592
593void KateMainWindow::removeMenuBarActionFromContextMenu()
594{
595 if (m_viewManager->activeView()) {
596 m_viewManager->activeView()->contextMenu()->removeAction(m_paShowMenuBar);
597 }
598}
599
600void KateMainWindow::toggleShowStatusBar ()
601{
602 emit statusBarToggled ();
603}
604
605bool KateMainWindow::showStatusBar ()
606{
607 return m_paShowStatusBar->isChecked ();
608}
609
610void KateMainWindow::slotWindowActivated ()
611{
612 if (m_viewManager->activeView())
613 updateCaption (m_viewManager->activeView()->document());
614
615 // show view manager in any case
616 if (m_mainStackedWidget->currentWidget () != m_viewManager)
617 m_mainStackedWidget->setCurrentWidget (m_viewManager);
618
619 // update proxy
620 centralWidget()->setFocusProxy (m_viewManager->activeView());
621}
622
623void KateMainWindow::slotUpdateOpenWith()
624{
625 if (m_viewManager->activeView())
626 documentOpenWith->setEnabled(!m_viewManager->activeView()->document()->url().isEmpty());
627 else
628 documentOpenWith->setEnabled(false);
629}
630
631void KateMainWindow::dragEnterEvent( QDragEnterEvent *event )
632{
633 if (!event->mimeData()) return;
634 const bool accept = KUrl::List::canDecode(event->mimeData()) // files
635 || event->mimeData()->hasText(); // text
636 event->setAccepted(accept);
637}
638
639void KateMainWindow::dropEvent( QDropEvent *event )
640{
641 slotDropEvent(event);
642}
643
644void KateMainWindow::slotDropEvent( QDropEvent * event )
645{
646 if (event->mimeData() == 0) return;
647
648 //
649 // are we dropping files?
650 //
651 if (KUrl::List::canDecode(event->mimeData())) {
652 KUrl::List textlist = KUrl::List::fromMimeData(event->mimeData());
653
654 // Try to get the KTextEditor::View that sent this, and activate it, so that the file opens in the
655 // view where it was dropped
656 KTextEditor::View *kVsender = qobject_cast<KTextEditor::View *>(QObject::sender());
657 if (kVsender != 0) {
658 QWidget *parent = kVsender->parentWidget();
659 if (parent != 0) {
660 KateViewSpace* vs = qobject_cast<KateViewSpace *>(parent->parentWidget());
661 if (vs != 0) m_viewManager->setActiveSpace(vs);
662 }
663 }
664
665 for (KUrl::List::Iterator i = textlist.begin(); i != textlist.end(); ++i)
666 {
667 // if url has no file component, try and recursively scan dir
668 KFileItem kitem( KFileItem::Unknown, KFileItem::Unknown, *i, true );
669 if( kitem.isDir() ) {
670 KIO::ListJob *list_job = KIO::listRecursive(*i, KIO::DefaultFlags, false);
671 connect(list_job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
672 this, SLOT(slotListRecursiveEntries(KIO::Job*,KIO::UDSEntryList)));
673 }
674 else {
675 m_viewManager->openUrl (*i);
676 }
677 }
678 }
679 //
680 // or are we dropping text?
681 //
682 else if (event->mimeData()->hasText()) {
683 KTextEditor::Document * doc =
684 KateDocManager::self()->createDoc();
685 doc->setText(event->mimeData()->text());
686 m_viewManager->activateView(doc);
687 }
688}
689
690void KateMainWindow::slotListRecursiveEntries(KIO::Job *job, const KIO::UDSEntryList &list)
691{
692 const KUrl dir = static_cast<KIO::SimpleJob*>( job )->url();
693 foreach( const KIO::UDSEntry &entry, list )
694 {
695 KUrl currentUrl = dir;
696 currentUrl.addPath( entry.stringValue( KIO::UDSEntry::UDS_NAME ) );
697 if( !entry.isDir() )
698 {
699 m_viewManager->openUrl(currentUrl);
700 }
701 }
702}
703
704void KateMainWindow::editKeys()
705{
706 KShortcutsDialog dlg ( KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this );
707
708 QList<KXMLGUIClient*> clients = guiFactory()->clients();
709
710 foreach(KXMLGUIClient *client, clients) {
711 // FIXME there appear to be invalid clients after session switching
712// kDebug(13001)<<"adding client to shortcut editor";
713// kDebug(13001)<<client;
714// kDebug(13001)<<client->actionCollection();
715// kDebug(13001)<<client->componentData().aboutData();
716// kDebug(13001)<<client->componentData().aboutData()->programName();
717 dlg.addCollection ( client->actionCollection(), client->componentData().aboutData()->programName() );
718 }
719 dlg.configure();
720
721 QList<KTextEditor::Document*> l = KateDocManager::self()->documentList();
722 for (int i = 0;i < l.count();i++)
723 {
724// kDebug(13001)<<"reloading Keysettings for document "<<i;
725 l.at(i)->reloadXML();
726 QList<KTextEditor::View *> l1 = l.at(i)->views ();
727 for (int i1 = 0;i1 < l1.count();i1++)
728 {
729 l1.at(i1)->reloadXML();
730// kDebug(13001)<<"reloading Keysettings for view "<<i<<"/"<<i1;
731 }
732 }
733}
734
735void KateMainWindow::openUrl (const QString &name)
736{
737 m_viewManager->openUrl (KUrl(name));
738}
739
740void KateMainWindow::slotConfigure()
741{
742 showPluginConfigPage(0,0);
743}
744
745void KateMainWindow::showPluginConfigPage(Kate::PluginConfigPageInterface *configpageinterface,uint id)
746{
747 if (!m_viewManager->activeView())
748 return;
749
750 KateConfigDialog* dlg = new KateConfigDialog (this, m_viewManager->activeView());
751 if (configpageinterface) {
752 dlg->showAppPluginPage(configpageinterface,id);
753 }
754 dlg->exec();
755
756 delete dlg;
757
758 m_viewManager->reactivateActiveView(); // gui (toolbars...) needs to be updated, because
759 // of possible changes that the configure dialog
760 // could have done on it, specially for plugins.
761}
762
763KUrl KateMainWindow::activeDocumentUrl()
764{
765 // anders: i make this one safe, as it may be called during
766 // startup (by the file selector)
767 KTextEditor::View *v = m_viewManager->activeView();
768 if ( v )
769 return v->document()->url();
770 return KUrl();
771}
772
773void KateMainWindow::mSlotFixOpenWithMenu()
774{
775 // dh: in bug #307699, this slot is called when launching the Kate application
776 // unfortunately, noone ever could reproduce except users.
777 KTextEditor::View *activeView = m_viewManager->activeView();
778 if (! activeView)
779 return;
780
781 // cleanup menu
782 KMenu *menu = documentOpenWith->menu();
783 menu->clear();
784
785 // get a list of appropriate services.
786 KMimeType::Ptr mime = KMimeType::mimeType(activeView->document()->mimeType());
787 //kDebug(13001) << "mime type: " << mime->name();
788
789 QAction *a = 0;
790 KService::List offers = KMimeTypeTrader::self()->query(mime->name(), "Application");
791 // add all default open-with-actions except "Kate"
792 for(KService::List::Iterator it = offers.begin(); it != offers.end(); ++it)
793 {
794 KService::Ptr service = *it;
795 if (service->name() == "Kate") continue;
796 a = menu->addAction(KIcon(service->icon()), service->name());
797 a->setData(service->entryPath());
798 }
799 // append "Other..." to call the KDE "open with" dialog.
800 a = documentOpenWith->menu()->addAction(i18n("&Other..."));
801 a->setData(QString());
802}
803
804void KateMainWindow::slotOpenWithMenuAction(QAction* a)
805{
806 KUrl::List list;
807 list.append( m_viewManager->activeView()->document()->url() );
808
809 const QString openWith = a->data().toString();
810 if (openWith.isEmpty())
811 {
812 // display "open with" dialog
813 KOpenWithDialog dlg(list);
814 if (dlg.exec())
815 KRun::run(*dlg.service(), list, this);
816 return;
817 }
818
819 KService::Ptr app = KService::serviceByDesktopPath(openWith);
820 if (app)
821 {
822 KRun::run(*app, list, this);
823 }
824 else
825 {
826 KMessageBox::error(this, i18n("Application '%1' not found.", openWith), i18n("Application not found"));
827 }
828}
829
830void KateMainWindow::pluginHelp()
831{
832 KToolInvocation::invokeHelp (QString(), "kate-plugins");
833}
834
835void KateMainWindow::aboutEditor()
836{
837 KAboutApplicationDialog ad(KateDocManager::self()->editor()->aboutData(),this);
838 ad.exec();
839}
840
841void KateMainWindow::tipOfTheDay()
842{
843 KTipDialog::showTip( /*0*/this, QString(), true );
844}
845
846void KateMainWindow::slotFullScreen(bool t)
847{
848 KToggleFullScreenAction::setFullScreen(this, t);
849}
850
851bool KateMainWindow::showModOnDiskPrompt()
852{
853 KTextEditor::Document *doc;
854
855 DocVector list;
856 list.reserve( KateDocManager::self()->documents() );
857 foreach( doc, KateDocManager::self()->documentList())
858 {
859 if ( KateDocManager::self()->documentInfo( doc )->modifiedOnDisc )
860 {
861 list.append( doc );
862 }
863 }
864
865 if ( !list.isEmpty() && !m_modignore )
866 {
867 KateMwModOnHdDialog mhdlg( list, this );
868 m_modignore = true;
869 bool res = mhdlg.exec();
870 m_modignore = false;
871
872 return res;
873 }
874 return true;
875}
876
877void KateMainWindow::slotDocumentCreated (KTextEditor::Document *doc)
878{
879 connect(doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(updateCaption(KTextEditor::Document*)));
880 connect(doc, SIGNAL(readWriteChanged(KTextEditor::Document*)), this, SLOT(updateCaption(KTextEditor::Document*)));
881 connect(doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(updateCaption(KTextEditor::Document*)));
882 connect(doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(slotUpdateOpenWith()));
883
884 updateCaption (doc);
885}
886
887void KateMainWindow::updateCaption ()
888{
889 if (m_viewManager->activeView())
890 updateCaption(m_viewManager->activeView()->document());
891}
892
893void KateMainWindow::updateCaption (KTextEditor::Document *doc)
894{
895 if (!m_viewManager->activeView())
896 {
897 setCaption ("", false);
898 return;
899 }
900
901 // block signals from inactive docs
902 if (!((KTextEditor::Document*)m_viewManager->activeView()->document() == doc))
903 return;
904
905 QString c;
906 if (m_viewManager->activeView()->document()->url().isEmpty() || (!m_paShowPath || !m_paShowPath->isChecked()))
907 {
908 c = ((KTextEditor::Document*)m_viewManager->activeView()->document())->documentName();
909 }
910 else
911 {
912 c = m_viewManager->activeView()->document()->url().pathOrUrl();
913 }
914
915 QString sessName = KateApp::self()->sessionManager()->activeSession()->sessionName();
916 if ( !sessName.isEmpty() )
917 sessName = QString("%1: ").arg( sessName );
918
919 QString readOnlyCaption;
920 if (!m_viewManager->activeView()->document()->isReadWrite())
921 readOnlyCaption=i18n(" [read only]");
922
923 setCaption( sessName + c+readOnlyCaption,
924 m_viewManager->activeView()->document()->isModified());
925}
926
927void KateMainWindow::saveProperties(KConfigGroup& config)
928{
929 saveSession(config);
930
931 // store all plugin view states
932 int id = KateApp::self()->mainWindowID (this);
933 foreach(const KatePluginInfo &item, KatePluginManager::self()->pluginList())
934 {
935 if (item.plugin && pluginViews().contains(item.plugin)) {
936 pluginViews().value(item.plugin)->writeSessionConfig (config.config(),
937 QString("Plugin:%1:MainWindow:%2").arg(item.saveName()).arg(id) );
938 }
939 }
940
941 fileOpenRecent->saveEntries( KConfigGroup(config.config(), "Recent Files" ) );
942 m_viewManager->saveViewConfiguration (config);
943}
944
945void KateMainWindow::readProperties(const KConfigGroup& config)
946{
947 // KDE5: TODO startRestore should take a const KConfigBase*, or even just a const KConfigGroup&,
948 // but this propagates down to interfaces/kate/plugin.h so all plugins have to be ported
949 KConfigBase* configBase = const_cast<KConfig *>(config.config());
950 startRestore(configBase, config.name());
951
952 // perhaps enable plugin guis
953 KatePluginManager::self()->enableAllPluginsGUI (this, configBase);
954
955 finishRestore ();
956
957 fileOpenRecent->loadEntries( KConfigGroup(config.config(), "Recent Files" ) );
958 m_viewManager->restoreViewConfiguration (config);
959}
960
961void KateMainWindow::saveGlobalProperties( KConfig* sessionConfig )
962{
963 KateDocManager::self()->saveDocumentList (sessionConfig);
964
965 KConfigGroup cg( sessionConfig, "General");
966 cg.writeEntry ("Last Session", KateApp::self()->sessionManager()->activeSession()->sessionFileRelative());
967
968 // save plugin config !!
969 KateApp::self()->pluginManager()->writeConfig (sessionConfig);
970
971}
972
973void KateMainWindow::saveWindowConfig(const KConfigGroup &_config)
974{
975 KConfigGroup config( _config );
976 saveMainWindowSettings(config);
977 saveWindowSize(config);
978 config.writeEntry("WindowState", int(((KParts::MainWindow*)this)->windowState()));
979 config.sync();
980}
981
982void KateMainWindow::restoreWindowConfig(const KConfigGroup &config)
983{
984 setWindowState(Qt::WindowNoState);
985 applyMainWindowSettings(config);
986 restoreWindowSize(config);
987 setWindowState( QFlags<Qt::WindowState>(config.readEntry("WindowState", int(Qt::WindowActive))) );
988}
989
990void KateMainWindow::slotUpdateBottomViewBar()
991{
992 //kDebug()<<"slotUpdateHorizontalViewBar()"<<endl;
993 KTextEditor::View *view=m_viewManager->activeView();
994 BarState bs=m_bottomViewBarMapping[view];
995 if (bs.bar() && bs.state()) {
996 m_bottomContainerStack->setCurrentWidget(bs.bar());
997 m_bottomContainerStack->currentWidget()->show();
998 m_bottomViewBarContainer->show();
999 } else {
1000 QWidget *wid=m_bottomContainerStack->currentWidget();
1001 if (wid) wid->hide();
1002 //kDebug()<<wid<<"hiding container"<<endl;
1003 m_bottomViewBarContainer->hide();
1004 }
1005}
1006
1007
1008void KateMainWindow::slotUpdateTopViewBar()
1009{
1010 //kDebug()<<"slotUpdateHorizontalViewBar()"<<endl;
1011 KTextEditor::View *view=m_viewManager->activeView();
1012 BarState bs=m_topViewBarMapping[view];
1013 if (bs.bar() && bs.state()) {
1014 m_topContainerStack->setCurrentWidget(bs.bar());
1015 m_topContainerStack->currentWidget()->show();
1016 m_topViewBarContainer->show();
1017 } else {
1018 QWidget *wid=m_topContainerStack->currentWidget();
1019 if (wid) wid->hide();
1020 //kDebug()<<wid<<"hiding container"<<endl;
1021 m_topViewBarContainer->hide();
1022 }
1023}
1024
1025
1026void KateMainWindow::queueModifiedOnDisc(KTextEditor::Document *doc)
1027{
1028 if (!modNotification) return;
1029
1030 if (s_modOnHdDialog==0) {
1031 DocVector list;
1032 list.append(doc);
1033
1034 s_modOnHdDialog= new KateMwModOnHdDialog( list, this );
1035 m_modignore = true;
1036 KWindowSystem::setOnAllDesktops( s_modOnHdDialog->winId(), true);
1037 s_modOnHdDialog->exec();
1038 delete s_modOnHdDialog; // s_modOnHdDialog is set to 0 in destructor of KateMwModOnHdDialog (jowenn!!!)
1039 m_modignore = false;
1040 } else {
1041 s_modOnHdDialog->addDocument(doc);
1042 }
1043}
1044
1045bool KateMainWindow::event( QEvent *e )
1046{
1047 if (e->type() == QEvent::ShortcutOverride) {
1048 QKeyEvent *k = static_cast<QKeyEvent *>(e);
1049 emit unhandledShortcutOverride (k);
1050 }
1051 return KateMDI::MainWindow::event(e);
1052}
1053
1054Kate::PluginView *KateMainWindow::pluginView (const QString &name)
1055{
1056 Kate::Plugin *plugin = KateApp::self()->pluginManager()->plugin (name);
1057 if (!plugin)
1058 return 0;
1059
1060 return m_pluginViews.contains(plugin) ? m_pluginViews.value (plugin) : 0;
1061}
1062
1063void KateMainWindow::slotQuickOpen ()
1064{
1065 /**
1066 * show quick open and pass focus to it
1067 */
1068 m_quickOpen->update ();
1069 m_mainStackedWidget->setCurrentWidget (m_quickOpen);
1070 centralWidget()->setFocusProxy (m_quickOpen);
1071 m_quickOpen->setFocus ();
1072}
1073
1074// kate: space-indent on; indent-width 2; replace-tabs on;
1075