1 /* This file is part of the KDE libraries
2 Copyright
3 (C) 2000 Reginald Stadlbauer (reggie@kde.org)
4 (C) 1997 Stephan Kulow (coolo@kde.org)
5 (C) 1997-2000 Sven Radej (radej@kde.org)
6 (C) 1997-2000 Matthias Ettrich (ettrich@kde.org)
7 (C) 1999 Chris Schlaeger (cs@kde.org)
8 (C) 2002 Joseph Wenninger (jowenn@kde.org)
9 (C) 2005-2006 Hamish Rodda (rodda@kde.org)
10 (C) 2000-2008 David Faure (faure@kde.org)
11
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Library General Public
14 License version 2 as published by the Free Software Foundation.
15
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Library General Public License for more details.
20
21 You should have received a copy of the GNU Library General Public License
22 along with this library; see the file COPYING.LIB. If not, write to
23 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 Boston, MA 02110-1301, USA.
25 */
26
27#include "kmainwindow.h"
28#include "kmainwindow_p.h"
29#include "kmainwindowiface_p.h"
30#include "ktoolbarhandler_p.h"
31#include "kcmdlineargs.h"
32#include "ktoggleaction.h"
33#include "ksessionmanager.h"
34#include "kstandardaction.h"
35
36#include <QtCore/QList>
37#include <QtCore/QObject>
38#include <QtCore/QTimer>
39#include <QtGui/QCloseEvent>
40#include <QtGui/QDesktopWidget>
41#include <QtGui/QDockWidget>
42#include <QtGui/QLayout>
43#include <QtGui/QSessionManager>
44#include <QtGui/QStyle>
45#include <QtGui/QWidget>
46
47#include <kaction.h>
48#include <kapplication.h>
49#include <kauthorized.h>
50#include <kconfig.h>
51#include <kdebug.h>
52#include <kdialog.h>
53#include <khelpmenu.h>
54#include <klocale.h>
55#include <kmenubar.h>
56#include <kstandarddirs.h>
57#include <kstatusbar.h>
58#include <ktoolbar.h>
59#include <kwindowsystem.h>
60#include <kconfiggroup.h>
61#include <kglobalsettings.h>
62
63#if defined Q_WS_X11
64#include <qx11info_x11.h>
65#include <netwm.h>
66#endif
67
68#include <stdlib.h>
69#include <ctype.h>
70#include <assert.h>
71
72#include <config.h>
73
74static bool no_query_exit = false;
75
76static KMenuBar *internalMenuBar(KMainWindow *mw)
77{
78 return KGlobal::findDirectChild<KMenuBar *>(mw);
79}
80
81static KStatusBar *internalStatusBar(KMainWindow *mw)
82{
83 // Don't use qFindChild here, it's recursive!
84 // (== slow, but also finds konqueror's per-view statusbars)
85 return KGlobal::findDirectChild<KStatusBar *>(mw);
86}
87
88/**
89 * Listens to resize events from QDockWidgets. The KMainWindow
90 * settings are set as dirty, as soon as at least one resize
91 * event occurred. The listener is attached to the dock widgets
92 * by dock->installEventFilter(dockResizeListener) inside
93 * KMainWindow::event().
94 */
95class DockResizeListener : public QObject
96{
97public:
98 DockResizeListener(KMainWindow *win);
99 virtual ~DockResizeListener();
100 virtual bool eventFilter(QObject *watched, QEvent *event);
101
102private:
103 KMainWindow *m_win;
104};
105
106DockResizeListener::DockResizeListener(KMainWindow *win) :
107 QObject(win),
108 m_win(win)
109{
110}
111
112DockResizeListener::~DockResizeListener()
113{
114}
115
116bool DockResizeListener::eventFilter(QObject *watched, QEvent *event)
117{
118 switch( event->type() ) {
119 case QEvent::Resize:
120 case QEvent::Move:
121 case QEvent::Hide:
122 m_win->k_ptr->setSettingsDirty(KMainWindowPrivate::CompressCalls);
123 break;
124
125 default:
126 break;
127 }
128
129 return QObject::eventFilter(watched, event);
130}
131
132class KMWSessionManager : public KSessionManager
133{
134public:
135 KMWSessionManager()
136 {
137 }
138 ~KMWSessionManager()
139 {
140 }
141 bool dummyInit() { return true; }
142 bool saveState( QSessionManager& )
143 {
144 KConfig* config = KApplication::kApplication()->sessionConfig();
145 if ( KMainWindow::memberList().count() ){
146 // According to Jochen Wilhelmy <digisnap@cs.tu-berlin.de>, this
147 // hook is useful for better document orientation
148 KMainWindow::memberList().first()->saveGlobalProperties(config);
149 }
150
151 int n = 0;
152 foreach (KMainWindow* mw, KMainWindow::memberList()) {
153 n++;
154 mw->savePropertiesInternal(config, n);
155 }
156
157 KConfigGroup group( config, "Number" );
158 group.writeEntry("NumberOfWindows", n );
159 return true;
160 }
161
162 bool commitData( QSessionManager& sm )
163 {
164 // not really a fast method but the only compatible one
165 if ( sm.allowsInteraction() ) {
166 bool canceled = false;
167 ::no_query_exit = true;
168
169 foreach (KMainWindow *window, KMainWindow::memberList()) {
170 if ( !window->testAttribute( Qt::WA_WState_Hidden ) ) {
171 QCloseEvent e;
172 QApplication::sendEvent( window, &e );
173 canceled = !e.isAccepted();
174 if (canceled)
175 break;
176 /* Don't even think_about deleting widgets with
177 Qt::WDestructiveClose flag set at this point. We
178 are faking a close event, but we are *not*_
179 closing the window. The purpose of the faked
180 close event is to prepare the application so it
181 can safely be quit without the user losing data
182 (possibly showing a message box "do you want to
183 save this or that?"). It is possible that the
184 session manager quits the application later
185 (emitting QApplication::aboutToQuit() when this
186 happens), but it is also possible that the user
187 cancels the shutdown, so the application will
188 continue to run.
189 */
190 }
191 }
192 ::no_query_exit = false;
193 if (canceled)
194 return false;
195
196 KMainWindow* last = 0;
197 foreach (KMainWindow *window, KMainWindow::memberList()) {
198 if ( !window->testAttribute( Qt::WA_WState_Hidden ) ) {
199 last = window;
200 }
201 }
202 if ( last )
203 return last->queryExit();
204 // else
205 return true;
206 }
207
208 // the user wants it, the user gets it
209 return true;
210 }
211};
212
213K_GLOBAL_STATIC(KMWSessionManager, ksm)
214K_GLOBAL_STATIC(QList<KMainWindow*>, sMemberList)
215static bool being_first = true;
216
217KMainWindow::KMainWindow( QWidget* parent, Qt::WindowFlags f )
218 : QMainWindow(parent, f), k_ptr(new KMainWindowPrivate)
219{
220 k_ptr->init(this);
221}
222
223KMainWindow::KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WindowFlags f)
224 : QMainWindow(parent, f), k_ptr(&dd)
225{
226 k_ptr->init(this);
227}
228
229void KMainWindowPrivate::init(KMainWindow *_q)
230{
231 KGlobal::ref();
232
233 // We set allow quit to true when the first mainwindow is created, so that when the refcounting
234 // reaches 0 the application can quit. We don't want this to happen before the first mainwindow
235 // is created, otherwise running a job in main would exit the app too early.
236 KGlobal::setAllowQuit(true);
237
238 q = _q;
239
240 q->setAnimated(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects);
241
242 q->setAttribute( Qt::WA_DeleteOnClose );
243
244 // We handle this functionality (quitting the app) ourselves, with KGlobal::ref/deref.
245 // This makes apps stay alive even if they only have a systray icon visible, or
246 // a progress widget with "keep open" checked, for instance.
247 // So don't let the default Qt mechanism allow any toplevel widget to just quit the app on us.
248 // Setting WA_QuitOnClose to false for all KMainWindows is not enough, any progress widget
249 // or dialog box would still quit the app...
250 if (qApp)
251 qApp->setQuitOnLastWindowClosed(false);
252
253 helpMenu = 0;
254
255 //actionCollection()->setWidget( this );
256 QObject::connect(qApp, SIGNAL(aboutToQuit()), q, SLOT(_k_shuttingDown()));
257 QObject::connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)),
258 q, SLOT(_k_slotSettingsChanged(int)));
259
260 // force KMWSessionManager creation - someone a better idea?
261 ksm->dummyInit();
262
263 sMemberList->append( q );
264
265 settingsDirty = false;
266 autoSaveSettings = false;
267 autoSaveWindowSize = true; // for compatibility
268 //d->kaccel = actionCollection()->kaccel();
269 settingsTimer = 0;
270 sizeTimer = 0;
271 shuttingDown = false;
272 if ((care_about_geometry = being_first)) {
273 being_first = false;
274
275 QString geometry;
276 KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde");
277 if (args && args->isSet("geometry"))
278 geometry = args->getOption("geometry");
279
280 if ( geometry.isNull() ) // if there is no geometry, it doesn't matter
281 care_about_geometry = false;
282 else
283 q->parseGeometry(false);
284 }
285
286 q->setWindowTitle( KGlobal::caption() );
287
288 dockResizeListener = new DockResizeListener(_q);
289 letDirtySettings = true;
290
291 sizeApplied = false;
292}
293
294static bool endsWithHashNumber( const QString& s )
295{
296 for( int i = s.length() - 1;
297 i > 0;
298 --i )
299 {
300 if( s[ i ] == '#' && i != s.length() - 1 )
301 return true; // ok
302 if( !s[ i ].isDigit())
303 break;
304 }
305 return false;
306}
307
308static inline bool isValidDBusObjectPathCharacter(const QChar &c)
309{
310 register ushort u = c.unicode();
311 return (u >= 'a' && u <= 'z')
312 || (u >= 'A' && u <= 'Z')
313 || (u >= '0' && u <= '9')
314 || (u == '_') || (u == '/');
315}
316
317void KMainWindowPrivate::polish(KMainWindow *q)
318{
319 // Set a unique object name. Required by session management, window management, and for the dbus interface.
320 QString objname;
321 QString s;
322 int unusedNumber = 1;
323 const QString name = q->objectName();
324 bool startNumberingImmediately = true;
325 bool tryReuse = false;
326 if ( name.isEmpty() )
327 { // no name given
328 objname = "MainWindow#";
329 }
330 else if( name.endsWith( QLatin1Char( '#' ) ) )
331 { // trailing # - always add a number - KWin uses this for better grouping
332 objname = name;
333 }
334 else if( endsWithHashNumber( name ))
335 { // trailing # with a number - like above, try to use the given number first
336 objname = name;
337 tryReuse = true;
338 startNumberingImmediately = false;
339 }
340 else
341 {
342 objname = name;
343 startNumberingImmediately = false;
344 }
345
346 s = objname;
347 if ( startNumberingImmediately )
348 s += '1';
349
350 for(;;) {
351 const QList<QWidget*> list = qApp->topLevelWidgets();
352 bool found = false;
353 foreach ( QWidget* w, list ) {
354 if( w != q && w->objectName() == s )
355 {
356 found = true;
357 break;
358 }
359 }
360 if( !found )
361 break;
362 if( tryReuse ) {
363 objname = name.left( name.length() - 1 ); // lose the hash
364 unusedNumber = 0; // start from 1 below
365 tryReuse = false;
366 }
367 s.setNum( ++unusedNumber );
368 s = objname + s;
369 }
370 q->setObjectName( s );
371 q->winId(); // workaround for setWindowRole() crashing, and set also window role, just in case TT
372 q->setWindowRole( s ); // will keep insisting that object name suddenly should not be used for window role
373
374 dbusName = '/' + qApp->applicationName() + '/' + q->objectName().replace(QLatin1Char('/'), QLatin1Char('_'));
375 // Clean up for dbus usage: any non-alphanumeric char should be turned into '_'
376 const int len = dbusName.length();
377 for ( int i = 0; i < len; ++i ) {
378 if ( !isValidDBusObjectPathCharacter( dbusName[i] ) )
379 dbusName[i] = QLatin1Char('_');
380 }
381
382 QDBusConnection::sessionBus().registerObject(dbusName, q, QDBusConnection::ExportScriptableSlots |
383 QDBusConnection::ExportScriptableProperties |
384 QDBusConnection::ExportNonScriptableSlots |
385 QDBusConnection::ExportNonScriptableProperties |
386 QDBusConnection::ExportAdaptors);
387}
388
389void KMainWindowPrivate::setSettingsDirty(CallCompression callCompression)
390{
391 if (!letDirtySettings) {
392 return;
393 }
394
395 settingsDirty = true;
396 if (autoSaveSettings) {
397 if (callCompression == CompressCalls) {
398 if (!settingsTimer) {
399 settingsTimer = new QTimer(q);
400 settingsTimer->setInterval(500);
401 settingsTimer->setSingleShot(true);
402 QObject::connect(settingsTimer, SIGNAL(timeout()), q, SLOT(saveAutoSaveSettings()));
403 }
404 settingsTimer->start();
405 } else {
406 q->saveAutoSaveSettings();
407 }
408 }
409}
410
411void KMainWindowPrivate::setSizeDirty()
412{
413 if (autoSaveWindowSize) {
414 if (!sizeTimer) {
415 sizeTimer = new QTimer(q);
416 sizeTimer->setInterval(500);
417 sizeTimer->setSingleShot(true);
418 QObject::connect(sizeTimer, SIGNAL(timeout()), q, SLOT(_k_slotSaveAutoSaveSize()));
419 }
420 sizeTimer->start();
421 }
422}
423
424void KMainWindow::parseGeometry(bool parsewidth)
425{
426 K_D(KMainWindow);
427 QString cmdlineGeometry;
428 KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde");
429 if (args->isSet("geometry"))
430 cmdlineGeometry = args->getOption("geometry");
431
432 assert ( !cmdlineGeometry.isNull() );
433 assert ( d->care_about_geometry );
434 Q_UNUSED(d); // fix compiler warning in release mode
435
436#if defined Q_WS_X11
437 int x, y;
438 int w, h;
439 int m = XParseGeometry( cmdlineGeometry.toLatin1(), &x, &y, (unsigned int*)&w, (unsigned int*)&h);
440 if (parsewidth) {
441 const QSize minSize = minimumSize();
442 const QSize maxSize = maximumSize();
443 if ( !(m & WidthValue) )
444 w = width();
445 if ( !(m & HeightValue) )
446 h = height();
447 w = qMin(w,maxSize.width());
448 h = qMin(h,maxSize.height());
449 w = qMax(w,minSize.width());
450 h = qMax(h,minSize.height());
451 resize(w, h);
452 } else {
453 if ( (m & XNegative) )
454 x = KApplication::desktop()->width() + x - w;
455 else if ( !(m & XValue) )
456 x = geometry().x();
457 if ( (m & YNegative) )
458 y = KApplication::desktop()->height() + y - h;
459 else if ( !(m & YValue) )
460 y = geometry().y();
461
462 move(x, y);
463 }
464#endif
465}
466
467KMainWindow::~KMainWindow()
468{
469 sMemberList->removeAll( this );
470 delete static_cast<QObject *>(k_ptr->dockResizeListener); //so we don't get anymore events after k_ptr is destroyed
471 delete k_ptr;
472 KGlobal::deref();
473}
474
475KMenu* KMainWindow::helpMenu( const QString &aboutAppText, bool showWhatsThis )
476{
477 K_D(KMainWindow);
478 if(!d->helpMenu) {
479 if ( aboutAppText.isEmpty() )
480 d->helpMenu = new KHelpMenu( this, KGlobal::mainComponent().aboutData(), showWhatsThis);
481 else
482 d->helpMenu = new KHelpMenu( this, aboutAppText, showWhatsThis );
483
484 if (!d->helpMenu)
485 return 0;
486 }
487
488 return d->helpMenu->menu();
489}
490
491KMenu* KMainWindow::customHelpMenu( bool showWhatsThis )
492{
493 K_D(KMainWindow);
494 if (!d->helpMenu) {
495 d->helpMenu = new KHelpMenu( this, QString(), showWhatsThis );
496 connect(d->helpMenu, SIGNAL(showAboutApplication()),
497 this, SLOT(showAboutApplication()));
498 }
499
500 return d->helpMenu->menu();
501}
502
503bool KMainWindow::canBeRestored( int number )
504{
505 if ( !qApp->isSessionRestored() )
506 return false;
507 KConfig *config = kapp->sessionConfig();
508 if ( !config )
509 return false;
510
511 KConfigGroup group( config, "Number" );
512 const int n = group.readEntry( "NumberOfWindows", 1 );
513 return number >= 1 && number <= n;
514}
515
516const QString KMainWindow::classNameOfToplevel( int number )
517{
518 if ( !qApp->isSessionRestored() )
519 return QString();
520 KConfig *config = kapp->sessionConfig();
521 if ( !config )
522 return QString();
523 QString s;
524 s.setNum( number );
525 s.prepend( QLatin1String("WindowProperties") );
526
527 KConfigGroup group( config, s );
528 if ( !group.hasKey( "ClassName" ) )
529 return QString();
530 else
531 return group.readEntry( "ClassName" );
532}
533
534bool KMainWindow::restore( int number, bool show )
535{
536 if ( !canBeRestored( number ) )
537 return false;
538 KConfig *config = kapp->sessionConfig();
539 if ( readPropertiesInternal( config, number ) ){
540 if ( show )
541 KMainWindow::show();
542 return false;
543 }
544 return false;
545}
546
547void KMainWindow::setCaption( const QString &caption )
548{
549 setPlainCaption( KDialog::makeStandardCaption( caption, this ) );
550}
551
552void KMainWindow::setCaption( const QString &caption, bool modified )
553{
554 KDialog::CaptionFlags flags = KDialog::HIGCompliantCaption;
555
556 if ( modified )
557 {
558 flags |= KDialog::ModifiedCaption;
559 }
560
561 setPlainCaption( KDialog::makeStandardCaption(caption, this, flags) );
562}
563
564void KMainWindow::setPlainCaption( const QString &caption )
565{
566 setWindowTitle(caption);
567}
568
569void KMainWindow::appHelpActivated( void )
570{
571 K_D(KMainWindow);
572 if( !d->helpMenu ) {
573 d->helpMenu = new KHelpMenu( this );
574 if ( !d->helpMenu )
575 return;
576 }
577 d->helpMenu->appHelpActivated();
578}
579
580void KMainWindow::closeEvent ( QCloseEvent *e )
581{
582 K_D(KMainWindow);
583
584 // Save settings if auto-save is enabled, and settings have changed
585 if (d->settingsTimer && d->settingsTimer->isActive()) {
586 d->settingsTimer->stop();
587 saveAutoSaveSettings();
588 }
589 if (d->sizeTimer && d->sizeTimer->isActive()) {
590 d->sizeTimer->stop();
591 d->_k_slotSaveAutoSaveSize();
592 }
593
594 if (queryClose()) {
595 e->accept();
596
597 int not_withdrawn = 0;
598 foreach (KMainWindow* mw, KMainWindow::memberList()) {
599 if ( !mw->isHidden() && mw->isTopLevel() && mw != this )
600 not_withdrawn++;
601 }
602
603 if ( !no_query_exit && not_withdrawn <= 0 ) { // last window close accepted?
604 if (!( queryExit() && ( !kapp || !kapp->sessionSaving() ) && !d->shuttingDown )) {
605 // cancel closing, it's stupid to end up with no windows at all....
606 e->ignore();
607 }
608 }
609 } else e->ignore(); //if the window should not be closed, don't close it
610}
611
612bool KMainWindow::queryExit()
613{
614 return true;
615}
616
617bool KMainWindow::queryClose()
618{
619 return true;
620}
621
622void KMainWindow::saveGlobalProperties( KConfig* )
623{
624}
625
626void KMainWindow::readGlobalProperties( KConfig* )
627{
628}
629
630void KMainWindow::showAboutApplication()
631{
632}
633
634void KMainWindow::savePropertiesInternal( KConfig *config, int number )
635{
636 K_D(KMainWindow);
637 const bool oldASWS = d->autoSaveWindowSize;
638 d->autoSaveWindowSize = true; // make saveMainWindowSettings save the window size
639
640 QString s;
641 s.setNum(number);
642 s.prepend(QLatin1String("WindowProperties"));
643 KConfigGroup cg(config, s);
644
645 // store objectName, className, Width and Height for later restoring
646 // (Only useful for session management)
647 cg.writeEntry(QLatin1String("ObjectName"), objectName());
648 cg.writeEntry(QLatin1String("ClassName"), metaObject()->className());
649
650 saveMainWindowSettings(cg); // Menubar, statusbar and Toolbar settings.
651
652 s.setNum(number);
653 cg = KConfigGroup(config, s);
654 saveProperties(cg);
655
656 d->autoSaveWindowSize = oldASWS;
657}
658
659void KMainWindow::saveMainWindowSettings(const KConfigGroup &_cg)
660{
661 K_D(KMainWindow);
662 //kDebug(200) << "KMainWindow::saveMainWindowSettings " << _cg.name();
663
664 // Called by session management - or if we want to save the window size anyway
665 if ( d->autoSaveWindowSize )
666 saveWindowSize( _cg );
667
668 KConfigGroup cg(_cg); // for saving
669
670 // One day will need to save the version number, but for now, assume 0
671 // Utilise the QMainWindow::saveState() functionality.
672 const QByteArray state = saveState();
673 cg.writeEntry(QString("State"), state.toBase64());
674
675 QStatusBar* sb = internalStatusBar(this);
676 if (sb) {
677 if(!cg.hasDefault("StatusBar") && !sb->isHidden() )
678 cg.revertToDefault("StatusBar");
679 else
680 cg.writeEntry("StatusBar", sb->isHidden() ? "Disabled" : "Enabled");
681 }
682
683 QMenuBar* mb = internalMenuBar(this);
684 if (mb) {
685 const QString MenuBar = QLatin1String("MenuBar");
686 if(!cg.hasDefault("MenuBar") && !mb->isHidden() )
687 cg.revertToDefault("MenuBar");
688 else
689 cg.writeEntry("MenuBar", mb->isHidden() ? "Disabled" : "Enabled");
690 }
691
692 if ( !autoSaveSettings() || cg.name() == autoSaveGroup() ) { // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name
693 if(!cg.hasDefault("ToolBarsMovable") && !KToolBar::toolBarsLocked())
694 cg.revertToDefault("ToolBarsMovable");
695 else
696 cg.writeEntry("ToolBarsMovable", KToolBar::toolBarsLocked() ? "Disabled" : "Enabled");
697 }
698
699 int n = 1; // Toolbar counter. toolbars are counted from 1,
700 foreach (KToolBar* toolbar, toolBars()) {
701 QString group("Toolbar");
702 // Give a number to the toolbar, but prefer a name if there is one,
703 // because there's no real guarantee on the ordering of toolbars
704 group += (toolbar->objectName().isEmpty() ? QString::number(n) : QString(" ")+toolbar->objectName());
705
706 KConfigGroup toolbarGroup(&cg, group);
707 toolbar->saveSettings(toolbarGroup);
708 n++;
709 }
710}
711
712bool KMainWindow::readPropertiesInternal( KConfig *config, int number )
713{
714 K_D(KMainWindow);
715
716 const bool oldLetDirtySettings = d->letDirtySettings;
717 d->letDirtySettings = false;
718
719 if ( number == 1 )
720 readGlobalProperties( config );
721
722 // in order they are in toolbar list
723 QString s;
724 s.setNum(number);
725 s.prepend(QLatin1String("WindowProperties"));
726
727 KConfigGroup cg(config, s);
728
729 // restore the object name (window role)
730 if ( cg.hasKey(QLatin1String("ObjectName" )) )
731 setObjectName( cg.readEntry("ObjectName").toLatin1()); // latin1 is right here
732
733 d->sizeApplied = false; // since we are changing config file, reload the size of the window
734 // if necessary. Do it before the call to applyMainWindowSettings.
735 applyMainWindowSettings(cg); // Menubar, statusbar and toolbar settings.
736
737 s.setNum(number);
738 KConfigGroup grp(config, s);
739 readProperties(grp);
740
741 d->letDirtySettings = oldLetDirtySettings;
742
743 return true;
744}
745
746void KMainWindow::applyMainWindowSettings(const KConfigGroup &cg, bool force)
747{
748 K_D(KMainWindow);
749 kDebug(200) << "KMainWindow::applyMainWindowSettings " << cg.name();
750
751 QWidget *focusedWidget = QApplication::focusWidget();
752
753 const bool oldLetDirtySettings = d->letDirtySettings;
754 d->letDirtySettings = false;
755
756 if (!d->sizeApplied) {
757 restoreWindowSize(cg);
758 d->sizeApplied = true;
759 }
760
761 QStatusBar* sb = internalStatusBar(this);
762 if (sb) {
763 QString entry = cg.readEntry("StatusBar", "Enabled");
764 if ( entry == "Disabled" )
765 sb->hide();
766 else
767 sb->show();
768 }
769
770 QMenuBar* mb = internalMenuBar(this);
771 if (mb) {
772 QString entry = cg.readEntry ("MenuBar", "Enabled");
773 if ( entry == "Disabled" )
774 mb->hide();
775 else
776 mb->show();
777 }
778
779 if ( !autoSaveSettings() || cg.name() == autoSaveGroup() ) { // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name
780 QString entry = cg.readEntry ("ToolBarsMovable", "Disabled");
781 if ( entry == "Disabled" )
782 KToolBar::setToolBarsLocked(true);
783 else
784 KToolBar::setToolBarsLocked(false);
785 }
786
787 int n = 1; // Toolbar counter. toolbars are counted from 1,
788 foreach (KToolBar* toolbar, toolBars()) {
789 QString group("Toolbar");
790 // Give a number to the toolbar, but prefer a name if there is one,
791 // because there's no real guarantee on the ordering of toolbars
792 group += (toolbar->objectName().isEmpty() ? QString::number(n) : QString(" ")+toolbar->objectName());
793
794 KConfigGroup toolbarGroup(&cg, group);
795 toolbar->applySettings(toolbarGroup, force);
796 n++;
797 }
798
799 QByteArray state;
800 if (cg.hasKey("State")) {
801 state = cg.readEntry("State", state);
802 state = QByteArray::fromBase64(state);
803 // One day will need to load the version number, but for now, assume 0
804 restoreState(state);
805 }
806
807 if (focusedWidget) {
808 focusedWidget->setFocus();
809 }
810
811 d->settingsDirty = false;
812 d->letDirtySettings = oldLetDirtySettings;
813}
814
815#ifdef Q_WS_WIN
816
817/*
818 The win32 implementation for restoring/savin windows size differs
819 from the unix/max implementation in three topics:
820
8211. storing and restoring the position, which may not work on x11
822 see http://doc.trolltech.com/4.3/geometry.html#x11-peculiarities
8232. using QWidget::saveGeometry() and QWidget::restoreGeometry()
824 this would probably be usable on x11 and/or on mac, but I'm unable to
825 check this on unix/mac, so I leave this fix to the x11/mac experts.
8263. store geometry separately for each resolution -> on unix/max the size
827 and with of the window are already saved separately on non windows
828 system although not using ...Geometry functions -> could also be
829 fixed by x11/mac experts.
830*/
831void KMainWindow::restoreWindowSize( const KConfigGroup & _cg )
832{
833 K_D(KMainWindow);
834
835 int scnum = QApplication::desktop()->screenNumber(window());
836 QRect desk = QApplication::desktop()->screenGeometry(scnum);
837
838 QString geometryKey = QString::fromLatin1("geometry-%1-%2").arg(desk.width()).arg(desk.height());
839 QByteArray geometry = _cg.readEntry( geometryKey, QByteArray() );
840 // if first time run, center window
841 if (!restoreGeometry( QByteArray::fromBase64(geometry) ))
842 move( (desk.width()-width())/2, (desk.height()-height())/2 );
843}
844
845void KMainWindow::saveWindowSize( const KConfigGroup & _cg ) const
846{
847 K_D(const KMainWindow);
848 int scnum = QApplication::desktop()->screenNumber(window());
849 QRect desk = QApplication::desktop()->screenGeometry(scnum);
850
851 // geometry is saved separately for each resolution
852 QString geometryKey = QString::fromLatin1("geometry-%1-%2").arg(desk.width()).arg(desk.height());
853 QByteArray geometry = saveGeometry();
854 KConfigGroup cg(_cg);
855 cg.writeEntry( geometryKey, geometry.toBase64() );
856}
857#else
858void KMainWindow::saveWindowSize( const KConfigGroup & _cg ) const
859{
860 K_D(const KMainWindow);
861 int scnum = QApplication::desktop()->screenNumber(window());
862 QRect desk = QApplication::desktop()->screenGeometry(scnum);
863
864 int w, h;
865#if defined Q_WS_X11
866 // save maximalization as desktop size + 1 in that direction
867 KWindowInfo info = KWindowSystem::windowInfo( winId(), NET::WMState );
868 w = info.state() & NET::MaxHoriz ? desk.width() + 1 : width();
869 h = info.state() & NET::MaxVert ? desk.height() + 1 : height();
870#else
871 if (isMaximized()) {
872 w = desk.width() + 1;
873 h = desk.height() + 1;
874 } else {
875 w = width();
876 h = height();
877 }
878 //TODO: add "Maximized" property instead "+1" hack
879#endif
880 KConfigGroup cg(_cg);
881
882 QRect size( desk.width(), w, desk.height(), h );
883 bool defaultSize = (size == d->defaultWindowSize);
884 QString widthString = QString::fromLatin1("Width %1").arg(desk.width());
885 QString heightString = QString::fromLatin1("Height %1").arg(desk.height());
886 if (!cg.hasDefault(widthString) && defaultSize)
887 cg.revertToDefault(widthString);
888 else
889 cg.writeEntry(widthString, w );
890
891 if (!cg.hasDefault(heightString) && defaultSize)
892 cg.revertToDefault(heightString);
893 else
894 cg.writeEntry(heightString, h );
895}
896
897void KMainWindow::restoreWindowSize( const KConfigGroup & config )
898{
899 K_D(KMainWindow);
900 if (d->care_about_geometry) {
901 parseGeometry(true);
902 } else {
903 // restore the size
904 const int scnum = QApplication::desktop()->screenNumber(window());
905 QRect desk = QApplication::desktop()->screenGeometry(scnum);
906
907 if ( d->defaultWindowSize.isNull() ) // only once
908 d->defaultWindowSize = QRect(desk.width(), width(), desk.height(), height()); // store default values
909 const QSize size( config.readEntry( QString::fromLatin1("Width %1").arg(desk.width()), 0 ),
910 config.readEntry( QString::fromLatin1("Height %1").arg(desk.height()), 0 ) );
911 if ( !size.isEmpty() ) {
912#ifdef Q_WS_X11
913 int state = ( size.width() > desk.width() ? NET::MaxHoriz : 0 )
914 | ( size.height() > desk.height() ? NET::MaxVert : 0 );
915 if(( state & NET::Max ) == NET::Max )
916 resize( desk.width(), desk.height() ); // WORKAROUND: this should not be needed. KWindowSystem::setState
917 // should be enough for maximizing. (ereslibre)
918 else if(( state & NET::MaxHoriz ) == NET::MaxHoriz )
919 resize( desk.width(), size.height() );
920 else if(( state & NET::MaxVert ) == NET::MaxVert )
921 resize( size.width(), desk.height() );
922 else
923 resize( size );
924 // QWidget::showMaximized() is both insufficient and broken
925 KWindowSystem::setState( winId(), state );
926#else
927 if (size.width() > desk.width() || size.height() > desk.height())
928 setWindowState( Qt::WindowMaximized );
929 else
930 resize( size );
931#endif
932 }
933 }
934}
935#endif
936
937bool KMainWindow::initialGeometrySet() const
938{
939 K_D(const KMainWindow);
940 return d->care_about_geometry;
941}
942
943void KMainWindow::ignoreInitialGeometry()
944{
945 K_D(KMainWindow);
946 d->care_about_geometry = false;
947}
948
949void KMainWindow::setSettingsDirty()
950{
951 K_D(KMainWindow);
952 d->setSettingsDirty();
953}
954
955bool KMainWindow::settingsDirty() const
956{
957 K_D(const KMainWindow);
958 return d->settingsDirty;
959}
960
961void KMainWindow::setAutoSaveSettings( const QString & groupName, bool saveWindowSize )
962{
963 setAutoSaveSettings(KConfigGroup(KGlobal::config(), groupName), saveWindowSize);
964}
965
966void KMainWindow::setAutoSaveSettings( const KConfigGroup & group,
967 bool saveWindowSize )
968{
969 K_D(KMainWindow);
970 d->autoSaveSettings = true;
971 d->autoSaveGroup = group;
972 d->autoSaveWindowSize = saveWindowSize;
973
974 if (!saveWindowSize && d->sizeTimer) {
975 d->sizeTimer->stop();
976 }
977
978 // Now read the previously saved settings
979 applyMainWindowSettings(d->autoSaveGroup);
980}
981
982void KMainWindow::resetAutoSaveSettings()
983{
984 K_D(KMainWindow);
985 d->autoSaveSettings = false;
986 if (d->settingsTimer) {
987 d->settingsTimer->stop();
988 }
989}
990
991bool KMainWindow::autoSaveSettings() const
992{
993 K_D(const KMainWindow);
994 return d->autoSaveSettings;
995}
996
997QString KMainWindow::autoSaveGroup() const
998{
999 K_D(const KMainWindow);
1000 return d->autoSaveSettings ? d->autoSaveGroup.name() : QString();
1001}
1002
1003KConfigGroup KMainWindow::autoSaveConfigGroup() const
1004{
1005 K_D(const KMainWindow);
1006 return d->autoSaveSettings ? d->autoSaveGroup : KConfigGroup();
1007}
1008
1009void KMainWindow::saveAutoSaveSettings()
1010{
1011 K_D(KMainWindow);
1012 Q_ASSERT( d->autoSaveSettings );
1013 //kDebug(200) << "KMainWindow::saveAutoSaveSettings -> saving settings";
1014 saveMainWindowSettings(d->autoSaveGroup);
1015 d->autoSaveGroup.sync();
1016 d->settingsDirty = false;
1017}
1018
1019bool KMainWindow::event( QEvent* ev )
1020{
1021 K_D(KMainWindow);
1022 switch( ev->type() ) {
1023#ifdef Q_WS_WIN
1024 case QEvent::Move:
1025#endif
1026 case QEvent::Resize:
1027 d->setSizeDirty();
1028 break;
1029 case QEvent::Polish:
1030 d->polish(this);
1031 break;
1032 case QEvent::ChildPolished:
1033 {
1034 QChildEvent *event = static_cast<QChildEvent*>(ev);
1035 QDockWidget *dock = qobject_cast<QDockWidget*>(event->child());
1036 KToolBar *toolbar = qobject_cast<KToolBar*>(event->child());
1037 QMenuBar *menubar = qobject_cast<QMenuBar*>(event->child());
1038 if (dock) {
1039 connect(dock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
1040 this, SLOT(setSettingsDirty()));
1041 connect(dock, SIGNAL(visibilityChanged(bool)),
1042 this, SLOT(setSettingsDirty()), Qt::QueuedConnection);
1043 connect(dock, SIGNAL(topLevelChanged(bool)),
1044 this, SLOT(setSettingsDirty()));
1045
1046 // there is no signal emitted if the size of the dock changes,
1047 // hence install an event filter instead
1048 dock->installEventFilter(k_ptr->dockResizeListener);
1049 } else if (toolbar) {
1050 // there is no signal emitted if the size of the toolbar changes,
1051 // hence install an event filter instead
1052 toolbar->installEventFilter(k_ptr->dockResizeListener);
1053 } else if (menubar) {
1054 // there is no signal emitted if the size of the menubar changes,
1055 // hence install an event filter instead
1056 menubar->installEventFilter(k_ptr->dockResizeListener);
1057 }
1058 }
1059 break;
1060 case QEvent::ChildRemoved:
1061 {
1062 QChildEvent *event = static_cast<QChildEvent*>(ev);
1063 QDockWidget *dock = qobject_cast<QDockWidget*>(event->child());
1064 KToolBar *toolbar = qobject_cast<KToolBar*>(event->child());
1065 QMenuBar *menubar = qobject_cast<QMenuBar*>(event->child());
1066 if (dock) {
1067 disconnect(dock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
1068 this, SLOT(setSettingsDirty()));
1069 disconnect(dock, SIGNAL(visibilityChanged(bool)),
1070 this, SLOT(setSettingsDirty()));
1071 disconnect(dock, SIGNAL(topLevelChanged(bool)),
1072 this, SLOT(setSettingsDirty()));
1073 dock->removeEventFilter(k_ptr->dockResizeListener);
1074 } else if (toolbar) {
1075 toolbar->removeEventFilter(k_ptr->dockResizeListener);
1076 } else if (menubar) {
1077 menubar->removeEventFilter(k_ptr->dockResizeListener);
1078 }
1079 }
1080 break;
1081 default:
1082 break;
1083 }
1084 return QMainWindow::event( ev );
1085}
1086
1087bool KMainWindow::hasMenuBar()
1088{
1089 return internalMenuBar(this);
1090}
1091
1092KMenuBar *KMainWindow::menuBar()
1093{
1094 KMenuBar * mb = internalMenuBar(this);
1095 if ( !mb ) {
1096 mb = new KMenuBar( this );
1097 // trigger a re-layout and trigger a call to the private
1098 // setMenuBar method.
1099 setMenuBar(mb);
1100 }
1101 return mb;
1102}
1103
1104KStatusBar *KMainWindow::statusBar()
1105{
1106 KStatusBar * sb = internalStatusBar(this);
1107 if ( !sb ) {
1108 sb = new KStatusBar( this );
1109 // trigger a re-layout and trigger a call to the private
1110 // setStatusBar method.
1111 setStatusBar(sb);
1112 }
1113 return sb;
1114}
1115
1116void KMainWindowPrivate::_k_shuttingDown()
1117{
1118 // Needed for Qt <= 3.0.3 at least to prevent reentrancy
1119 // when queryExit() shows a dialog. Check before removing!
1120 static bool reentrancy_protection = false;
1121 if (!reentrancy_protection)
1122 {
1123 reentrancy_protection = true;
1124 shuttingDown = true;
1125 // call the virtual queryExit
1126 q->queryExit();
1127 reentrancy_protection = false;
1128 }
1129}
1130
1131void KMainWindowPrivate::_k_slotSettingsChanged(int category)
1132{
1133 Q_UNUSED(category);
1134
1135 // This slot will be called when the style KCM changes settings that need
1136 // to be set on the already running applications.
1137
1138 // At this level (KMainWindow) the only thing we need to restore is the
1139 // animations setting (whether the user wants builtin animations or not).
1140
1141 q->setAnimated(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects);
1142}
1143
1144void KMainWindowPrivate::_k_slotSaveAutoSaveSize()
1145{
1146 if (autoSaveGroup.isValid()) {
1147 q->saveWindowSize(autoSaveGroup);
1148 }
1149}
1150
1151KToolBar *KMainWindow::toolBar( const QString& name )
1152{
1153 QString childName = name;
1154 if (childName.isEmpty())
1155 childName = "mainToolBar";
1156
1157 KToolBar *tb = findChild<KToolBar*>(childName);
1158 if ( tb )
1159 return tb;
1160
1161 KToolBar* toolbar = new KToolBar(childName, this); // non-XMLGUI toolbar
1162 return toolbar;
1163}
1164
1165QList<KToolBar*> KMainWindow::toolBars() const
1166{
1167 QList<KToolBar*> ret;
1168
1169 foreach (QObject* child, children())
1170 if (KToolBar* toolBar = qobject_cast<KToolBar*>(child))
1171 ret.append(toolBar);
1172
1173 return ret;
1174}
1175
1176QList<KMainWindow*> KMainWindow::memberList() { return *sMemberList; }
1177
1178QString KMainWindow::dbusName() const
1179{
1180 return k_func()->dbusName;
1181}
1182
1183#include "kmainwindow.moc"
1184
1185