add more spacing
[personal-kdebase.git] / workspace / krunner / interfaces / default / interface.cpp
blob29615fbb7334041687433a778e12db04ed89167b
1 /***************************************************************************
2 * Copyright 2006 by Aaron Seigo <aseigo@kde.org> *
3 * Copyright 2008 by Davide Bettio <davide.bettio@kdemail.net> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
19 ***************************************************************************/
21 #include "interface.h"
23 #include <QAction>
24 #include <QApplication>
25 #include <QClipboard>
26 #include <QDesktopWidget>
27 #include <QGraphicsView>
28 #include <QHBoxLayout>
29 #include <QHideEvent>
30 #include <QLabel>
31 #include <QShortcut>
32 #include <QToolButton>
33 #include <QVBoxLayout>
35 #include <KActionCollection>
36 #include <KHistoryComboBox>
37 #include <KCompletion>
38 #include <KCompletionBox>
39 #include <KDebug>
40 #include <KDialog>
41 #include <KLineEdit>
42 #include <KLocale>
43 #include <KGlobalSettings>
44 #include <KPushButton>
45 #include <KTitleWidget>
46 #include <KWindowSystem>
48 #include <Plasma/AbstractRunner>
49 #include <Plasma/RunnerManager>
50 #include <Plasma/Theme>
51 #include <Plasma/Svg>
53 #include "krunnersettings.h"
54 #include "interfaces/default/resultscene.h"
55 #include "interfaces/default/resultitem.h"
57 static const int MIN_WIDTH = 400;
59 Interface::Interface(Plasma::RunnerManager *runnerManager, QWidget *parent)
60 : KRunnerDialog(runnerManager, parent),
61 m_delayedRun(false),
62 m_running(false),
63 m_queryRunning(false)
65 m_hideResultsTimer.setSingleShot(true);
66 connect(&m_hideResultsTimer, SIGNAL(timeout()), this, SLOT(hideResultsArea()));
68 QWidget *w = mainWidget();
69 m_layout = new QVBoxLayout(w);
70 m_layout->setMargin(0);
72 m_buttonContainer = new QWidget(w);
73 QHBoxLayout *bottomLayout = new QHBoxLayout(m_buttonContainer);
74 bottomLayout->setMargin(0);
76 m_configButton = new QToolButton(m_buttonContainer);
77 m_configButton->setText(i18n("Settings"));
78 m_configButton->setToolTip(i18n("Settings"));
79 m_configButton->setIcon(m_iconSvg->pixmap("configure"));
80 connect(m_configButton, SIGNAL(clicked()), SLOT(showConfigDialog()));
81 bottomLayout->addWidget( m_configButton );
84 KPushButton *m_optionsButton = new KPushButton(KStandardGuiItem::configure(), m_buttonContainer);
85 m_optionsButton->setDefault(false);
86 m_optionsButton->setAutoDefault(false);
87 m_optionsButton->setText(i18n("Show Options"));
88 m_optionsButton->setEnabled(false);
89 m_optionsButton->setCheckable(true);
90 connect(m_optionsButton, SIGNAL(toggled(bool)), SLOT(showOptions(bool)));
91 bottomLayout->addWidget( m_optionsButton );
94 m_activityButton = new QToolButton(m_buttonContainer);
95 // m_activityButton->setDefault(false);
96 // m_activityButton->setAutoDefault(false);
97 m_activityButton->setText(i18n("Show System Activity"));
98 m_activityButton->setToolTip(i18n("Show System Activity"));
99 m_activityButton->setIcon(m_iconSvg->pixmap("status"));
100 connect(m_activityButton, SIGNAL(clicked()), qApp, SLOT(showTaskManager()));
101 connect(m_activityButton, SIGNAL(clicked()), this, SLOT(close()));
102 bottomLayout->addWidget(m_activityButton);
103 //bottomLayout->addStretch(10);
105 m_closeButton = new QToolButton(m_buttonContainer);
106 KGuiItem guiItem = KStandardGuiItem::close();
107 m_closeButton->setText(guiItem.text());
108 m_closeButton->setToolTip(guiItem.text().remove('&'));
109 m_closeButton->setIcon(m_iconSvg->pixmap("close"));
110 // m_closeButton->setDefault(false);
111 // m_closeButton->setAutoDefault(false);
112 connect(m_closeButton, SIGNAL(clicked(bool)), SLOT(close()));
113 bottomLayout->addWidget(m_closeButton);
115 m_layout->addWidget(m_buttonContainer);
117 m_searchTerm = new KHistoryComboBox(false, w);
118 m_searchTerm->setDuplicatesEnabled(false);
120 KLineEdit *lineEdit = new KLineEdit(m_searchTerm);
121 QAction *focusEdit = new QAction(this);
122 focusEdit->setShortcut(Qt::Key_F6);
124 // in therory, the widget should detect the direction from the content
125 // but this is not available in Qt4.4/KDE 4.2, so the best default for this widget
126 // is LTR: as it's more or less a "command line interface"
127 // FIXME remove this code when KLineEdit has automatic direction detection of the "paragraph"
128 m_searchTerm->setLayoutDirection( Qt::LeftToRight );
130 connect(focusEdit, SIGNAL(triggered(bool)), lineEdit, SLOT(setFocus()));
131 addAction(focusEdit);
133 // the order of these next few lines if very important.
134 // QComboBox::setLineEdit sets the autoComplete flag on the lineedit,
135 // and KComboBox::setAutoComplete resets the autocomplete mode! ugh!
136 m_searchTerm->setLineEdit(lineEdit);
138 m_completion = new KCompletion();
139 lineEdit->setCompletionObject(m_completion);
140 lineEdit->setCompletionMode(static_cast<KGlobalSettings::Completion>(KRunnerSettings::queryTextCompletionMode()));
141 lineEdit->setClearButtonShown(true);
142 QStringList pastQueryItems = KRunnerSettings::pastQueries();
143 m_searchTerm->setHistoryItems(pastQueryItems);
144 m_completion->insertItems(pastQueryItems);
145 bottomLayout->insertWidget(2, m_searchTerm, 10);
147 m_statusLayout = new QHBoxLayout();
148 m_descriptionLabel = new QLabel(w);
149 m_descriptionLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
150 m_descriptionLabel->hide();
151 m_statusLayout->addWidget(m_descriptionLabel, 10, Qt::AlignLeft | Qt::AlignTop);
153 m_previousPage = new QLabel(w);
154 m_previousPage->setText("<a href=\"prev\">&lt;&lt;</a>");
155 m_previousPage->hide();
156 m_statusLayout->addWidget(m_previousPage, 0, Qt::AlignLeft | Qt::AlignTop);
158 m_nextPage = new QLabel(w);
159 m_nextPage->setText("<a href=\"next\">&gt;&gt;</a>");
160 m_nextPage->hide();
161 m_statusLayout->addWidget(m_nextPage, 0, Qt::AlignLeft | Qt::AlignTop);
163 m_layout->addLayout(m_statusLayout);
165 m_dividerLine = new QWidget(w);
166 m_dividerLine->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum);
167 m_dividerLine->setFixedHeight(1);
168 m_dividerLine->setAutoFillBackground(true);
169 m_dividerLine->hide();
170 m_layout->addWidget(m_dividerLine);
172 m_resultsView = new QGraphicsView(w);
173 m_resultsView->setFrameStyle(QFrame::NoFrame);
174 m_resultsView->viewport()->setAutoFillBackground(false);
175 m_resultsView->setInteractive(true);
176 m_resultsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
177 m_resultsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
178 m_resultsView->setOptimizationFlag(QGraphicsView::DontSavePainterState);
179 m_resultsView->setAlignment(Qt::AlignLeft | Qt::AlignTop);
181 //kDebug() << "size:" << m_resultsView->size() << m_resultsView->minimumSize();
182 m_resultsScene = new ResultScene(runnerManager, this);
183 m_resultsView->setScene(m_resultsScene);
184 m_resultsView->setMinimumSize(m_resultsScene->minimumSizeHint());
185 connect(m_resultsScene, SIGNAL(matchCountChanged(int)), this, SLOT(matchCountChanged(int)));
186 connect(m_resultsScene, SIGNAL(itemActivated(ResultItem *)), this, SLOT(run(ResultItem *)));
187 connect(m_resultsScene, SIGNAL(itemHoverEnter(ResultItem *)), this, SLOT(updateDescriptionLabel(ResultItem *)));
188 connect(m_resultsScene, SIGNAL(itemHoverLeave(ResultItem *)), m_descriptionLabel, SLOT(clear()));
189 connect(m_previousPage, SIGNAL(linkActivated(const QString&)), m_resultsScene, SLOT(previousPage()));
190 connect(m_nextPage, SIGNAL(linkActivated(const QString&)), m_resultsScene, SLOT(nextPage()));
192 m_layout->addWidget(m_resultsView);
194 connect(m_searchTerm, SIGNAL(editTextChanged(QString)), this, SLOT(queryTextEdited(QString)));
195 connect(m_searchTerm, SIGNAL(returnPressed()), this, SLOT(runDefaultResultItem()));
197 themeUpdated();
198 connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeUpdated()));
200 new QShortcut(QKeySequence( Qt::Key_Escape ), this, SLOT(close()));
202 m_layout->addStretch(1);
203 setTabOrder(0, m_configButton);
204 setTabOrder(m_configButton, m_activityButton);
205 setTabOrder(m_activityButton, m_searchTerm);
206 setTabOrder(m_searchTerm, m_previousPage);
207 setTabOrder(m_previousPage, m_nextPage);
208 setTabOrder(m_nextPage, m_resultsView);
209 setTabOrder(m_resultsView, m_closeButton);
211 //kDebug() << "size:" << m_resultsView->size() << m_resultsView->minimumSize();
212 // we restore the original size, which will set the results view back to its
213 // normal size, then we hide the results view and resize the dialog
214 setMinimumSize(QSize(MIN_WIDTH , 0));
215 if (KGlobal::config()->hasGroup("Interface")) {
216 KConfigGroup interfaceConfig(KGlobal::config(), "Interface");
217 restoreDialogSize(interfaceConfig);
220 //kDebug() << "size:" << minimumSizeHint() << minimumSize();
221 QTimer::singleShot(0, this, SLOT(resetInterface()));
224 void Interface::resizeEvent(QResizeEvent *event)
226 Plasma::Theme *theme = Plasma::Theme::defaultTheme();
227 int gradientWidth = contentsRect().width() - KDialog::marginHint()*2;
228 QLinearGradient gr(0, 0, gradientWidth, 0);
229 gr.setColorAt(0, theme->color(Plasma::Theme::BackgroundColor));
230 gr.setColorAt(.35, theme->color(Plasma::Theme::TextColor));
231 gr.setColorAt(.65, theme->color(Plasma::Theme::TextColor));
232 gr.setColorAt(1, theme->color(Plasma::Theme::BackgroundColor));
234 QPalette p = palette();
235 p.setBrush(QPalette::Background, gr);
236 m_dividerLine->setPalette(p);
238 m_resultsScene->resize(m_resultsView->width(), m_resultsView->height());
239 KRunnerDialog::resizeEvent(event);
242 Interface::~Interface()
244 KRunnerSettings::setPastQueries(m_searchTerm->historyItems());
245 KRunnerSettings::setQueryTextCompletionMode(m_searchTerm->completionMode());
246 KRunnerSettings::self()->writeConfig();
247 KConfigGroup interfaceConfig(KGlobal::config(), "Interface");
248 saveDialogSize(interfaceConfig);
249 KGlobal::config()->sync();
252 void Interface::themeUpdated()
254 Plasma::Theme *theme = Plasma::Theme::defaultTheme();
255 QColor buttonBgColor = theme->color(Plasma::Theme::BackgroundColor);
256 QString buttonStyleSheet = QString("QToolButton { border: 1px solid %4; border-radius: 4px; padding: 2px;"
257 " background-color: rgba(%1, %2, %3, %5); }")
258 .arg(buttonBgColor.red())
259 .arg(buttonBgColor.green())
260 .arg(buttonBgColor.blue())
261 .arg(theme->color(Plasma::Theme::HighlightColor).name(), "50%");
262 buttonBgColor = theme->color(Plasma::Theme::TextColor);
263 buttonStyleSheet += QString("QToolButton:hover { border: 2px solid %1; }")
264 .arg(theme->color(Plasma::Theme::HighlightColor).name());
265 buttonStyleSheet += QString("QToolButton:focus { border: 2px solid %1; }")
266 .arg(theme->color(Plasma::Theme::HighlightColor).name());
267 m_configButton->setStyleSheet(buttonStyleSheet);
268 m_activityButton->setStyleSheet(buttonStyleSheet);
269 m_closeButton->setStyleSheet(buttonStyleSheet);
270 //kDebug() << "stylesheet is" << buttonStyleSheet;
272 QPalette p = m_descriptionLabel->palette();
273 p.setColor(QPalette::WindowText, theme->color(Plasma::Theme::TextColor));
274 p.setColor(QPalette::Link, theme->color(Plasma::Theme::TextColor));
275 p.setColor(QPalette::LinkVisited, theme->color(Plasma::Theme::TextColor));
276 m_descriptionLabel->setPalette(p);
277 m_previousPage->setPalette(p);
278 m_nextPage->setPalette(p);
280 //reset the icons
281 m_configButton->setIcon(m_iconSvg->pixmap("configure"));
282 m_activityButton->setIcon(m_iconSvg->pixmap("status"));
283 m_closeButton->setIcon(m_iconSvg->pixmap("close"));
286 void Interface::clearHistory()
288 m_searchTerm->clearHistory();
289 KRunnerSettings::setPastQueries(m_searchTerm->historyItems());
292 void Interface::display(const QString &term)
294 m_searchTerm->setFocus();
296 KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop());
298 // TODO: set a nice welcome string when the string freeze lifts
299 m_descriptionLabel->clear();
301 show();
302 resetInterface();
303 centerOnScreen();
304 KWindowSystem::forceActiveWindow(winId());
306 if (!term.isEmpty()) {
307 m_searchTerm->setItemText(0, term);
311 void Interface::centerOnScreen()
313 int screen = 0;
314 if (QApplication::desktop()->numScreens() > 1) {
315 screen = QApplication::desktop()->screenNumber(QCursor::pos());
318 if (m_resultsView->isVisibleTo(this)) {
319 KDialog::centerOnScreen(this, screen);
320 return;
323 // center it as if the results view was already visible
324 QDesktopWidget *desktop = qApp->desktop();
325 QRect r = desktop->screenGeometry(screen);
326 int w = width();
327 int h = height() + m_resultsView->height();
328 move(r.left() + (r.width() / 2) - (w / 2),
329 r.top() + (r.height() / 2) - (h / 2));
330 //kDebug() << "moved to" << pos();
333 void Interface::setWidgetPalettes()
335 // a nice palette to use with the widgets
336 QPalette widgetPalette = palette();
337 QColor bgColor = widgetPalette.color( QPalette::Active,
338 QPalette::Base );
339 bgColor.setAlpha( 200 );
340 widgetPalette.setColor( QPalette::Base, bgColor );
342 m_searchTerm->setPalette( widgetPalette );
345 void Interface::resetInterface()
347 setStaticQueryMode(false);
348 m_delayedRun = false;
349 m_searchTerm->setCurrentItem(QString(), true, 0);
350 m_descriptionLabel->clear();
351 m_descriptionLabel->hide();
352 m_previousPage->hide();
353 m_nextPage->hide();
354 m_resultsScene->clearQuery();
355 m_resultsView->hide();
356 m_dividerLine->hide();
357 setMinimumSize(QSize(MIN_WIDTH, 0));
358 adjustSize();
359 //kDebug() << size() << minimumSizeHint();
360 resize(minimumSizeHint());
363 void Interface::setStaticQueryMode(bool staticQuery)
365 if (staticQuery) {
366 m_statusLayout->addWidget(m_closeButton);
367 } else {
368 m_buttonContainer->layout()->addWidget(m_closeButton);
371 // don't show the search and other control buttons in the case of a static query
372 m_buttonContainer->setVisible(!staticQuery);
373 /* m_configButton->setVisible(visible);
374 m_activityButton->setVisible(visible);
375 m_closeButton->setVisible(visible);
376 m_searchTerm->setVisible(visible);*/
379 void Interface::closeEvent(QCloseEvent *e)
381 if (!m_running) {
382 resetInterface();
383 } else {
384 m_delayedRun = false;
385 m_resultsView->hide();
386 m_descriptionLabel->hide();
387 m_previousPage->hide();
388 m_nextPage->hide();
389 m_dividerLine->hide();
390 setMinimumSize(QSize(MIN_WIDTH, 0));
391 adjustSize();
393 e->accept();
396 void Interface::run(ResultItem *item)
398 if (!item || item->group() < Plasma::QueryMatch::PossibleMatch) {
399 m_delayedRun = true;
400 return;
403 kDebug() << item->name() << item->id();
404 m_delayedRun = false;
405 m_searchTerm->addToHistory(m_searchTerm->currentText());
407 if (item->group() == Plasma::QueryMatch::InformationalMatch) {
408 QString info = item->data();
410 if (!info.isEmpty()) {
411 m_searchTerm->setItemText(0, info);
412 m_searchTerm->setCurrentIndex(0);
413 QApplication::clipboard()->setText(info);
415 return;
418 m_running = true;
419 close();
420 m_resultsScene->run(item);
421 m_running = false;
422 resetInterface();
425 void Interface::runDefaultResultItem()
427 if (m_queryRunning) {
428 m_delayedRun = true;
429 } else {
430 run(m_resultsScene->defaultResultItem());
434 void Interface::queryTextEdited(const QString &query)
436 m_delayedRun = false;
438 if (query.isEmpty()) {
439 resetInterface();
440 m_queryRunning = false;
441 } else {
442 m_resultsScene->launchQuery(query);
443 m_queryRunning = true;
447 void Interface::updateDescriptionLabel(ResultItem *item)
449 // we want it always visible once we start showing it
450 // so that the interface isn't jumping all around
451 m_descriptionLabel->setVisible(m_resultsView->isVisible());
452 m_dividerLine->setVisible(m_resultsView->isVisible());
453 if (!item) {
454 m_descriptionLabel->setText(" ");
455 } else if (item->description().isEmpty()) {
456 m_descriptionLabel->setText(item->name());
457 } else {
458 m_descriptionLabel->setText(i18n("%1: %2",item->name() ,item->description()));
462 void Interface::matchCountChanged(int count)
464 m_queryRunning = false;
465 bool show = count > 0;
466 m_hideResultsTimer.stop();
467 bool pages = m_resultsScene->pageCount() > 1;
468 m_previousPage->setVisible(pages);
469 m_nextPage->setVisible(pages);
471 if (show && m_delayedRun) {
472 kDebug() << "delayed run with" << count << "items";
473 runDefaultResultItem();
474 return;
477 if (m_resultsView->isVisible() == show) {
478 return;
481 if (show) {
482 //kDebug() << "showing!";
483 m_resultsView->show();
484 setMinimumSize(QSize(MIN_WIDTH, 0));
485 adjustSize();
486 } else {
487 //kDebug() << "hiding ... eventually";
488 m_delayedRun = false;
489 m_hideResultsTimer.start(2000);
493 void Interface::hideResultsArea()
495 m_resultsView->hide();
496 m_descriptionLabel->hide();
497 m_descriptionLabel->clear();
498 m_previousPage->hide();
499 m_nextPage->hide();
500 m_dividerLine->hide();
501 m_searchTerm->setFocus();
502 setMinimumSize(QSize(MIN_WIDTH, 0));
503 adjustSize();
504 resize(minimumSizeHint());
507 #include "interface.moc"