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 | |
83 | uint KateMainWindow::uniqueID = 1; |
84 | KateMwModOnHdDialog *KateMainWindow::s_modOnHdDialog=0; |
85 | |
86 | KateContainerStackedLayout::KateContainerStackedLayout(QWidget* parent) |
87 | : QStackedLayout(parent) |
88 | {} |
89 | |
90 | QSize KateContainerStackedLayout::sizeHint() const |
91 | { |
92 | if (currentWidget()) |
93 | return currentWidget()->sizeHint(); |
94 | return QStackedLayout::sizeHint(); |
95 | } |
96 | |
97 | QSize KateContainerStackedLayout::minimumSize() const |
98 | { |
99 | if (currentWidget()) |
100 | return currentWidget()->minimumSize(); |
101 | return QStackedLayout::minimumSize(); |
102 | } |
103 | |
104 | |
105 | KateMainWindow::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 | |
211 | KateMainWindow::~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 | |
226 | void 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 | |
239 | void 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 | |
265 | void 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 | |
403 | void 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 | |
418 | void KateMainWindow::slotDocumentCloseOther(KTextEditor::Document *document) |
419 | { |
420 | if (queryClose_internal(document)) |
421 | KateDocManager::self()->closeOtherDocuments(document); |
422 | } |
423 | |
424 | void 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 | |
436 | void KateMainWindow::slotDocumentCloseOther() |
437 | { |
438 | if (queryClose_internal(m_viewManager->activeView()->document())) |
439 | KateDocManager::self()->closeOtherDocuments(m_viewManager->activeView()->document()); |
440 | } |
441 | |
442 | bool 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 | */ |
472 | bool 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 | |
497 | void KateMainWindow::newWindow () |
498 | { |
499 | KateApp::self()->newMainWindow (KateApp::self()->sessionManager()->activeSession()->configRead()); |
500 | } |
501 | |
502 | void 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 | |
511 | void KateMainWindow::slotNewToolbarConfig() |
512 | { |
513 | applyMainWindowSettings(KConfigGroup(KGlobal::config(), "MainWindow" )); |
514 | } |
515 | |
516 | void KateMainWindow::slotFileQuit() |
517 | { |
518 | KateApp::self()->shutdownKate (this); |
519 | } |
520 | |
521 | void KateMainWindow::slotFileClose() |
522 | { |
523 | m_viewManager->slotDocumentClose(); |
524 | } |
525 | |
526 | void KateMainWindow::slotOpenDocument(KUrl url) |
527 | { |
528 | m_viewManager->openUrl(url, |
529 | QString(), |
530 | true, |
531 | false); |
532 | } |
533 | |
534 | void 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 | |
554 | void 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 | |
569 | void 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 | |
586 | void KateMainWindow::addMenuBarActionToContextMenu() |
587 | { |
588 | if (m_viewManager->activeView()) { |
589 | m_viewManager->activeView()->contextMenu()->addAction(m_paShowMenuBar); |
590 | } |
591 | } |
592 | |
593 | void KateMainWindow::removeMenuBarActionFromContextMenu() |
594 | { |
595 | if (m_viewManager->activeView()) { |
596 | m_viewManager->activeView()->contextMenu()->removeAction(m_paShowMenuBar); |
597 | } |
598 | } |
599 | |
600 | void KateMainWindow::toggleShowStatusBar () |
601 | { |
602 | emit statusBarToggled (); |
603 | } |
604 | |
605 | bool KateMainWindow::showStatusBar () |
606 | { |
607 | return m_paShowStatusBar->isChecked (); |
608 | } |
609 | |
610 | void 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 | |
623 | void 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 | |
631 | void 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 | |
639 | void KateMainWindow::dropEvent( QDropEvent *event ) |
640 | { |
641 | slotDropEvent(event); |
642 | } |
643 | |
644 | void 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 | |
690 | void 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 | |
704 | void 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 | |
735 | void KateMainWindow::openUrl (const QString &name) |
736 | { |
737 | m_viewManager->openUrl (KUrl(name)); |
738 | } |
739 | |
740 | void KateMainWindow::slotConfigure() |
741 | { |
742 | showPluginConfigPage(0,0); |
743 | } |
744 | |
745 | void 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 | |
763 | KUrl 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 | |
773 | void 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 * = 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 | |
804 | void 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 | |
830 | void KateMainWindow::pluginHelp() |
831 | { |
832 | KToolInvocation::invokeHelp (QString(), "kate-plugins" ); |
833 | } |
834 | |
835 | void KateMainWindow::aboutEditor() |
836 | { |
837 | KAboutApplicationDialog ad(KateDocManager::self()->editor()->aboutData(),this); |
838 | ad.exec(); |
839 | } |
840 | |
841 | void KateMainWindow::tipOfTheDay() |
842 | { |
843 | KTipDialog::showTip( /*0*/this, QString(), true ); |
844 | } |
845 | |
846 | void KateMainWindow::slotFullScreen(bool t) |
847 | { |
848 | KToggleFullScreenAction::setFullScreen(this, t); |
849 | } |
850 | |
851 | bool 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 | |
877 | void 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 | |
887 | void KateMainWindow::updateCaption () |
888 | { |
889 | if (m_viewManager->activeView()) |
890 | updateCaption(m_viewManager->activeView()->document()); |
891 | } |
892 | |
893 | void 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 | |
927 | void 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 | |
945 | void 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 | |
961 | void 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 | |
973 | void 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 | |
982 | void 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 | |
990 | void 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 | |
1008 | void 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 | |
1026 | void 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 | |
1045 | bool 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 | |
1054 | Kate::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 | |
1063 | void 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 | |