1 /***************************************************************************
2 * Copyright 2006 by Aaron Seigo <aseigo@kde.org> *
3 * Copyright 2008 by Davide Bettio <davide.bettio@kdemail.net> *
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. *
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. *
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"
24 #include <QApplication>
26 #include <QDesktopWidget>
27 #include <QGraphicsView>
28 #include <QHBoxLayout>
32 #include <QToolButton>
33 #include <QVBoxLayout>
35 #include <KActionCollection>
36 #include <KHistoryComboBox>
37 #include <KCompletion>
38 #include <KCompletionBox>
43 #include <KGlobalSettings>
44 #include <KPushButton>
45 #include <KTitleWidget>
46 #include <KWindowSystem>
48 #include <Plasma/AbstractRunner>
49 #include <Plasma/RunnerManager>
50 #include <Plasma/Theme>
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
),
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\"><<</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\">>></a>");
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()));
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
);
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();
304 KWindowSystem::forceActiveWindow(winId());
306 if (!term
.isEmpty()) {
307 m_searchTerm
->setItemText(0, term
);
311 void Interface::centerOnScreen()
314 if (QApplication::desktop()->numScreens() > 1) {
315 screen
= QApplication::desktop()->screenNumber(QCursor::pos());
318 if (m_resultsView
->isVisibleTo(this)) {
319 KDialog::centerOnScreen(this, screen
);
323 // center it as if the results view was already visible
324 QDesktopWidget
*desktop
= qApp
->desktop();
325 QRect r
= desktop
->screenGeometry(screen
);
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
,
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();
354 m_resultsScene
->clearQuery();
355 m_resultsView
->hide();
356 m_dividerLine
->hide();
357 setMinimumSize(QSize(MIN_WIDTH
, 0));
359 //kDebug() << size() << minimumSizeHint();
360 resize(minimumSizeHint());
363 void Interface::setStaticQueryMode(bool staticQuery
)
366 m_statusLayout
->addWidget(m_closeButton
);
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
)
384 m_delayedRun
= false;
385 m_resultsView
->hide();
386 m_descriptionLabel
->hide();
387 m_previousPage
->hide();
389 m_dividerLine
->hide();
390 setMinimumSize(QSize(MIN_WIDTH
, 0));
396 void Interface::run(ResultItem
*item
)
398 if (!item
|| item
->group() < Plasma::QueryMatch::PossibleMatch
) {
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
);
420 m_resultsScene
->run(item
);
425 void Interface::runDefaultResultItem()
427 if (m_queryRunning
) {
430 run(m_resultsScene
->defaultResultItem());
434 void Interface::queryTextEdited(const QString
&query
)
436 m_delayedRun
= false;
438 if (query
.isEmpty()) {
440 m_queryRunning
= false;
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());
454 m_descriptionLabel
->setText(" ");
455 } else if (item
->description().isEmpty()) {
456 m_descriptionLabel
->setText(item
->name());
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();
477 if (m_resultsView
->isVisible() == show
) {
482 //kDebug() << "showing!";
483 m_resultsView
->show();
484 setMinimumSize(QSize(MIN_WIDTH
, 0));
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();
500 m_dividerLine
->hide();
501 m_searchTerm
->setFocus();
502 setMinimumSize(QSize(MIN_WIDTH
, 0));
504 resize(minimumSizeHint());
507 #include "interface.moc"