2 * This file is part of the System Settings package
3 * Copyright (C) 2005 Benjamin C Meyer (ben+systempreferences at meyerhome dot net)
4 * (C) 2007 Will Stephenson <wstephenson@kde.org>
5 * (C) 2007 Michael Jansen <kde@michael-jansen.biz>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
23 #include "mainwindow.h"
25 #include <QApplication>
29 #include <QStackedWidget>
32 #include <KActionCollection>
33 #include <KCModuleInfo>
34 #include <KCModuleProxy>
36 #include <KDialog> // for spacing
37 #include <KGlobalSettings>
41 #include <KServiceTypeTrader>
42 #include <KStandardAction>
44 #include <kcategorizedsortfilterproxymodel.h>
45 #include <kcategorizedview.h>
46 #include <kcategorydrawer.h>
47 #include <kiconloader.h>
48 #include <kfileitemdelegate.h>
50 #include "kcmodulemodel.h"
51 #include "kcmultiwidget.h"
53 #include "moduleiconitem.h"
55 Q_DECLARE_METATYPE(MenuItem
*)
57 MainWindow::MainWindow(QWidget
*parent
) :
58 KXmlGuiWindow(parent
),
59 categories( KServiceTypeTrader::self()->query("SystemSettingsCategory") ),
60 modules( KServiceTypeTrader::self()->query("KCModule") ),
61 rootItem(new MenuItem( true, 0 )),
65 // Load the menu structure in from disk.
67 qStableSort( rootItem
->children
.begin(), rootItem
->children
.end(), pageLessThan
); // sort tabs by weight
68 moduleTabs
= new KTabWidget(this);
71 // We hide the menubar. So ensure the toolbar is always visible because you cannot get it back
72 setupGUI(Save
|Create
,QString());
75 connect(moduleTabs
, SIGNAL(currentChanged(int)), SLOT(widgetChange()));
78 MainWindow::~MainWindow()
84 QSize
MainWindow::sizeHint() const
86 return QSize(780, 580);
89 void MainWindow::readMenu( MenuItem
* parent
)
91 // look for any categories inside this level, and recurse into them
93 MenuItem
* current
= parent
;
94 while ( current
&& current
->parent
) {
96 current
= current
->parent
;
100 space
.fill( ' ', depth
* 2 );
101 kDebug() << space
<< "Looking for children in '" << parent
->name
<< "'";
102 for (int i
= 0; i
< categories
.size(); ++i
) {
103 KService::Ptr entry
= categories
.at(i
);
104 QString parentCategory
= entry
->property("X-KDE-System-Settings-Parent-Category").toString();
105 QString category
= entry
->property("X-KDE-System-Settings-Category").toString();
106 //kDebug() << "Examining category " << parentCategory << "/" << category;
107 if ( parentCategory
== parent
->name
) {
108 KCModuleInfo
module( entry
->entryPath() );
110 MenuItem
* menuItem
= new MenuItem(true, parent
);
111 menuItem
->name
= category
;
112 menuItem
->service
= entry
;
113 menuItem
->item
= module
;
114 readMenu( menuItem
);
118 // scan for any modules at this level and add them
119 for (int i
= 0; i
< modules
.size(); ++i
) {
120 KService::Ptr entry
= modules
.at(i
);
121 QString category
= entry
->property("X-KDE-System-Settings-Parent-Category").toString();
122 //kDebug() << "Examining module " << category;
123 if(!parent
->name
.isEmpty() && category
== parent
->name
) {
124 kDebug() << space
<< "found module '" << entry
->name() << "' " << entry
->entryPath();
125 // Add the module info to the menu
126 KCModuleInfo
module(entry
->entryPath());
127 kDebug() << space
<< "filename is " << module
.fileName();
129 MenuItem
* infoItem
= new MenuItem(false, parent
);
130 infoItem
->name
= category
;
131 infoItem
->service
= entry
;
132 infoItem
->item
= module
;
136 parent
->sortChildrenByWeight();
141 bool MainWindow::queryClose()
144 return groupWidget
->queryClose();
150 void MainWindow::buildMainWidget()
152 windowStack
= new QStackedWidget(this);
158 foreach ( MenuItem
* item
, rootItem
->children
) {
159 model
= new KCModuleModel( item
, this );
160 KCategoryDrawer
* drawer
= new KCategoryDrawer
;
161 KCategorizedView
* tv
= new KCategorizedView( this );
162 tv
->setSelectionMode(QAbstractItemView::SingleSelection
);
163 tv
->setSpacing(KDialog::spacingHint());
164 tv
->setCategoryDrawer( drawer
);
165 tv
->setViewMode( QListView::IconMode
);
166 tv
->setMouseTracking( true );
167 tv
->viewport()->setAttribute( Qt::WA_Hover
);
168 tv
->setItemDelegate( new KFileItemDelegate( tv
) );
169 KCategorizedSortFilterProxyModel
* kcsfpm
= new SystemSettingsProxyModel( this );
170 kcsfpm
->setCategorizedModel( true );
171 kcsfpm
->setSourceModel( model
);
172 kcsfpm
->setFilterRole( KCModuleModel::UserFilterRole
);
173 kcsfpm
->setFilterCaseSensitivity( Qt::CaseInsensitive
);
175 tv
->setModel( kcsfpm
);
177 SIGNAL(activated(const QModelIndex
&)),
178 SLOT(selectionChanged(const QModelIndex
&)) );
179 if (KGlobalSettings::singleClick()) {
180 connect( tv
, SIGNAL(clicked(const QModelIndex
&)),
181 SLOT(selectionChanged(const QModelIndex
&)));
183 connect( tv
, SIGNAL(doubleClicked(const QModelIndex
&)),
184 SLOT(selectionChanged(const QModelIndex
&)));
186 connect( search
, SIGNAL(textChanged(const QString
&)),
187 kcsfpm
, SLOT(setFilterRegExp(const QString
&)));
188 connect( kcsfpm
, SIGNAL(layoutChanged()),
189 this, SLOT(updateSearchHits()) );
190 moduleTabs
->addTab(tv
, item
->service
->name() );
194 // record the index of the newly added tab so that we can later update the label showing
195 // number of search hits
196 modelToTabHash
.insert( kcsfpm
, moduleTabs
->count() - 1 );
198 windowStack
->addWidget(moduleTabs
);
199 windowStack
->setCurrentWidget(moduleTabs
);
200 setCentralWidget(windowStack
);
203 void MainWindow::buildActions()
205 actionCollection()->addAction(
206 KStandardAction::Quit
,
207 qobject_cast
<QObject
*>(this),
210 showOverviewAction
= actionCollection()->addAction(
211 KStandardAction::Back
,
212 qobject_cast
<QObject
*>(this),
213 SLOT(showOverview()));
214 showOverviewAction
->setText(
215 i18n("Overview (%1)", showOverviewAction
->shortcut().primary().toString()));
216 showOverviewAction
->setEnabled(false);
218 QWidget
*searchWid
= new QWidget( this );
219 QLabel
* searchIcon
= new QLabel( searchWid
);
220 searchIcon
->setPixmap( BarIcon( "system-search" ) );
221 QLabel
*searchLabel
= new QLabel( searchWid
);
222 searchLabel
->setObjectName( QLatin1String("SearchLabel"));
223 searchLabel
->setText( i18n("S&earch:") );
224 searchLabel
->setFont(KGlobalSettings::toolBarFont());
225 searchLabel
->setMargin(2);
226 QHBoxLayout
* hlay
= new QHBoxLayout( searchWid
);
227 hlay
->addWidget( searchIcon
);
228 hlay
->addWidget( searchLabel
);
229 searchWid
->setLayout( hlay
);
231 searchText
= new KAction( this );
232 searchText
->setDefaultWidget(searchWid
);
234 actionCollection()->addAction( "searchText", searchText
);
235 searchText
->setShortcut(Qt::Key_F6
);
237 // Search edit box and result labels
238 QWidget
*hbox
= new QWidget( this );
240 search
= new KLineEdit( hbox
);
241 search
->setObjectName(QLatin1String("search"));
242 search
->setClearButtonShown( true );
243 search
->setFocusPolicy( Qt::StrongFocus
);
244 searchLabel
->setBuddy( search
);
245 // Is that needed? I thought that's what a buddy is for?
246 connect(searchText
, SIGNAL(triggered()), search
, SLOT(setFocus()));
248 QWidget
* vbox
= new QWidget(hbox
);
249 // Set a non empty content to prevent the toolbar from getting taller when
250 // starting a search (at least with Oxygen style).
251 generalHitLabel
= new QLabel(" ", vbox
);
252 advancedHitLabel
= new QLabel(" ", vbox
);
254 QVBoxLayout
* vlayout
= new QVBoxLayout
;
255 vlayout
->setMargin(0);
256 vlayout
->setSpacing(0);
257 vlayout
->addWidget(generalHitLabel
);
258 vlayout
->addWidget(advancedHitLabel
);
259 vlayout
->setStretchFactor(generalHitLabel
,1);
260 vlayout
->setStretchFactor(advancedHitLabel
,1);
261 vbox
->setLayout(vlayout
);
263 QHBoxLayout
* hlayout
= new QHBoxLayout
;
264 hlayout
->setMargin(0);
265 hlayout
->addWidget(search
);
266 hlayout
->addWidget(vbox
);
267 hlayout
->setStretchFactor(search
,1);
268 hlayout
->setStretchFactor(vbox
,1);
269 hbox
->setLayout(hlayout
);
271 searchAction
= new KAction( "none", this );
272 searchAction
->setDefaultWidget(hbox
);
273 actionCollection()->addAction( "search", searchAction
);
274 searchAction
->setShortcutConfigurable( false );
275 hbox
->setWhatsThis( i18n("Search Bar<p>Enter a search term.</p>") );
280 void MainWindow::showOverview()
282 if (!groupWidget
->queryClose()) {
285 windowStack
->setCurrentWidget(moduleTabs
);
287 // Reset the widget for normal all widget viewing
291 showOverviewAction
->setEnabled(false);
293 searchText
->setEnabled(true);
294 search
->setEnabled(true);
295 searchAction
->setEnabled(true);
299 void MainWindow::selectionChanged( const QModelIndex
& selected
)
301 if ( !selected
.isValid() )
304 MenuItem
* mItem
= selected
.data( Qt::UserRole
).value
<MenuItem
*>();
306 kDebug() << "Selected item: " << mItem
->service
->name();
307 kDebug() << "Comment: " << mItem
->service
->comment();
309 kDebug() << ":'( Got dud pointer from " << selected
.data( Qt::DisplayRole
).toString();
310 Q_ASSERT(mItem
); // Would core dump below. Do it now
311 return; // For production
313 // Because some KCMultiWidgets take an age to load, it is possible
314 // for the user to click another ModuleIconItem while loading.
315 // This causes execution to return here while the first groupWidget is shown
319 groupWidget
= moduleItemToWidgetDict
[mItem
->service
];
322 groupWidget
= new KCMultiWidget(windowStack
, Qt::NonModal
); // THAT ZERO IS NEW (actually the 0 can go, jr)
323 windowStack
->addWidget(groupWidget
);
324 moduleItemToWidgetDict
.insert(mItem
->service
,groupWidget
);
326 // That shouldn't be needed.
327 connect(groupWidget
, SIGNAL(finished()), this, SLOT(showOverview()));
329 if ( ! mItem
->menu
) {
330 groupWidget
->addModule( mItem
->item
);
332 foreach ( MenuItem
* i
, mItem
->children
) {
333 kDebug() << "adding " , i
->item
.fileName();
334 groupWidget
->addModule( i
->item
);
339 // calling this with a shown KCMultiWidget sets groupWidget to 0
340 // which makes the show() call below segfault. The groupWidget test
341 // above should prevent execution reaching here while the KCMultiWidget is
343 windowStack
->setCurrentWidget( groupWidget
);
345 setCaption( mItem
->service
->name() );
346 showOverviewAction
->setEnabled(true);
347 searchText
->setEnabled(false);
348 search
->setEnabled(false);
349 searchAction
->setEnabled(false);
354 * This is called when we go back to the overview page. It resets the name and
355 * clears the selection
357 void MainWindow::widgetChange() {
359 setCaption(QString());
360 KCategorizedView
* currentView
= qobject_cast
<KCategorizedView
*>( moduleTabs
->currentWidget() );
361 currentView
->selectionModel()->clear();
366 /* :BUG: This is a nice idea. It just doesn't work. It looks like that
367 * layoutChanged() signal connected to this slot doesn't work the way it is
368 * expected here. It signals that the order of the items changed. I can't find
369 * a signal that informs us that set sortfilterproxy has done it's work.
371 void MainWindow::updateSearchHits()
373 // if the search lineedit is empty, clear the search labels
374 if ( search
->text().isEmpty() ) {
375 generalHitLabel
->setText(QString());
376 advancedHitLabel
->setText(QString());
377 } else { // otherwise update the tab for the sender()
378 for ( int i
= 0; i
< moduleTabs
->count(); i
++ ) {
379 const KCategorizedSortFilterProxyModel
* kcsfpm
= static_cast<KCategorizedSortFilterProxyModel
*>( sender() );
380 if (kcsfpm
&& modelToTabHash
.contains( kcsfpm
) ) {
381 switch ( modelToTabHash
[ kcsfpm
] ) {
383 generalHitLabel
->setText(i18np("%1 hit in General","%1 hits in General", kcsfpm
->rowCount()));
386 advancedHitLabel
->setText(i18np("%1 hit in Advanced","%1 hits in Advanced",kcsfpm
->rowCount()));
389 kDebug() << "Hits found in top level system settings other than General, Advanced, and the UI is hardcoded to only indicate hits in these tabs";
396 bool pageLessThan( MenuItem
*page1
, MenuItem
*page2
)
398 return page1
->item
.weight() < page2
->item
.weight();
401 #include "mainwindow.moc"