2 * Copyright (C) 2006 Aaron Seigo <aseigo@kde.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Library General Public License version 2 as
6 * published by the Free Software Foundation
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details
13 * You should have received a copy of the GNU Library General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include <QApplication>
22 #include <QListWidget>
23 #include <QVBoxLayout>
24 #include <QHBoxLayout>
28 #include <QGraphicsItem>
30 #include <KActionCollection>
35 #include <KGlobalSettings>
36 #include <KPushButton>
37 #include <KStandardGuiItem>
38 #include <KTitleWidget>
39 #include <KWindowSystem>
41 #include <plasma/abstractrunner.h>
43 #include <QSvgRenderer>
44 #include <QResizeEvent>
49 #include <plasma/widgets/lineedit.h>
51 #include "runners/services/servicerunner.h"
52 #include "runners/sessions/sessionrunner.h"
53 #include "runners/shell/shellrunner.h"
54 #include "collapsiblewidget.h"
55 #include "interface.h"
56 #include "interfaceadaptor.h"
57 #include "krunnerapp.h"
61 #define KRUNNER_SIZE 350, 101
64 // A little hack of a class to let us easily activate a match
66 class SearchMatch
: public QListWidgetItem
69 SearchMatch( QAction
* action
, Plasma::AbstractRunner
* runner
, QListWidget
* parent
)
70 : QListWidgetItem( parent
),
80 m_action
->activate( QAction::Trigger
);
85 return m_action
->isEnabled();
88 void setAction( QAction
* action
)
91 setIcon( m_action
->icon() );
92 setText( i18n("%1 (%2)",
94 m_runner
->objectName() ) );
96 // in case our new action is now enabled and the old one wasn't, or
98 setDefault( m_default
);
101 Plasma::AbstractRunner
* runner()
106 void setDefault( bool def
) {
107 if ( m_default
== def
) {
114 if ( m_action
->isEnabled() ) {
115 setText( text().prepend( i18n("Default: ") ) );
118 setText( text().mid( 9 ) );
125 Plasma::AbstractRunner
* m_runner
;
128 GenericItem::GenericItem(Plasma::Svg
*renderer
, Interface
*qgv
)
130 m_background
= renderer
;
135 void GenericItem::setElement(const QString
&element
)
138 m_background
->resize(350, 101);
141 QRectF
GenericItem::boundingRect() const
143 if (m_element
.isEmpty()) {
144 return QRectF(0, 0, 0, 0);
146 // kDebug() << QRectF(0, 0, m_background->elementSize(m_element).width(), m_background->elementSize(m_element).height()) << endl;
147 // return QRectF(0, 0, m_background->elementSize(m_element).width(), m_background->elementSize(m_element).height());
149 if (m_element
== "top-left") {
150 paintRect
= QRectF(0, 0, 9, 50);
151 } else if (m_element
== "top") {
152 paintRect
= QRectF(0, 0, 332, 50);
153 } else if (m_element
== "top-right") {
154 paintRect
= QRectF(0, 0, 9, 50);
155 } else if (m_element
== "separator") {
156 paintRect
= QRectF(0, 0, 348, 1);
157 } else if (m_element
== "bottom-left") {
158 paintRect
= QRectF(0, 0, 9, 50);
159 } else if (m_element
== "bottom") {
160 paintRect
= QRectF(0, 0, 332, 50);
161 } else if (m_element
== "bottom-right") {
162 paintRect
= QRectF(0, 0, 9, 50);
167 void GenericItem::paint(QPainter
*painter
,
168 const QStyleOptionGraphicsItem
*option
,
172 if (m_element
== "top-left") {
173 paintRect
= QRectF(0, 0, 9, 50);
174 } else if (m_element
== "top") {
175 paintRect
= QRectF(0, 0, 332, 50);
176 } else if (m_element
== "top-right") {
177 paintRect
= QRectF(0, 0, 9, 50);
178 } else if (m_element
== "separator") {
179 paintRect
= QRectF(0, 0, 348, 1);
180 } else if (m_element
== "bottom-left") {
181 paintRect
= QRectF(0, 0, 9, 50);
182 } else if (m_element
== "bottom") {
183 paintRect
= QRectF(0, 0, 332, 50);
184 } else if (m_element
== "bottom-right") {
185 paintRect
= QRectF(0, 0, 9, 50);
187 m_background
->paint(painter
, paintRect
, m_element
);
191 // MainItem::MainItem()
193 // m_background = new Plasma::Svg( "dialogs/krunner", this );
194 // m_background->setContentType(Plasma::Svg::ImageSet);
195 // // connect( m_background, SIGNAL(repaintNeeded()), this, SLOT(update()) );
196 // m_background->resize(KRUNNER_SIZE);
199 // void MainItem::paint(QPainter *painter,
200 // const QStyleOptionGraphicsItem *option,
203 // // m_background->paint( painter, QRectF(0, 0, 9, 50), "top-left" );
204 // // m_background->paint( painter, QRectF(9, 0, 332, 50), "top" );
205 // // m_background->paint( painter, QRectF(341, 0, 9, 50), "top-right" );
206 // // m_background->paint( painter, QRectF(1, 50, 348, 1), "separator" );
207 // // m_background->paint( painter, QRectF(0, 51, 9, 50), "bottom-left" );
208 // // m_background->paint( painter, QRectF(9, 51, 332, 50), "bottom" );
209 // // m_background->paint( painter, QRectF(341, 51, 9, 50), "bottom-right" );
210 // // m_background->paint( painter, 0, 0 );
213 void PushIcon::setIcon(const QString
&icon
)
218 void PushIcon::paint(QPainter
*painter
,
219 const QStyleOptionGraphicsItem
*option
,
223 painter
->drawPixmap(0, 0, exec
.pixmap(22, 22));
224 // m_background->paint( painter, QRectF(0, 0, 9, 50), "top-left" );
225 // m_background->paint( painter, QRectF(9, 0, 332, 50), "top" );
226 // m_background->paint( painter, QRectF(341, 0, 9, 50), "top-right" );
227 // m_background->paint( painter, QRectF(1, 50, 348, 1), "separator" );
228 // m_background->paint( painter, QRectF(0, 51, 9, 50), "bottom-left" );
229 // m_background->paint( painter, QRectF(9, 51, 332, 50), "bottom" );
230 // m_background->paint( painter, QRectF(341, 51, 9, 50), "bottom-right" );
231 // m_background->paint( painter, 0, 0 );
234 Interface::Interface(QWidget
* parent
)
235 : QGraphicsView( parent
),
240 m_background
= new Plasma::Svg( "dialogs/krunner", this );
241 m_background
->setContentType(Plasma::Svg::ImageSet
);
242 m_background
->resize(350, 101);
243 connect( m_background
, SIGNAL(repaintNeeded()), this, SLOT(update()) );
245 setWindowTitle( i18n("Run Command") );
247 connect( &m_searchTimer
, SIGNAL(timeout()),
248 this, SLOT(fuzzySearch()) );
250 GenericItem
*item
= new GenericItem(m_background
, this);
251 item
->setElement("top-left");
252 GenericItem
*top
= new GenericItem(m_background
, this);
253 top
->setElement("top");
254 GenericItem
*topright
= new GenericItem(m_background
, this);
255 topright
->setElement("top-right");
256 GenericItem
*separator
= new GenericItem(m_background
, this);
257 separator
->setElement("separator");
258 GenericItem
*bottomleft
= new GenericItem(m_background
, this);
259 bottomleft
->setElement("bottom-left");
260 GenericItem
*bottom
= new GenericItem(m_background
, this);
261 bottom
->setElement("bottom");
262 GenericItem
*bottomright
= new GenericItem(m_background
, this);
263 bottomright
->setElement("bottom-right");
265 // MainItem *item = new MainItem;
266 Plasma::LineEdit
*lineEdit
= new Plasma::LineEdit
;
267 m_toggleExpand
= new PushIcon(this);
268 m_toggleExpand
->setIcon("arrow-down");
269 PushIcon
*close
= new PushIcon(this);
270 close
->setIcon("no");
272 QGraphicsScene
*scene
= new QGraphicsScene(this);
273 scene
->setItemIndexMethod(QGraphicsScene::NoIndex
);
274 scene
->setSceneRect(0, 0, KRUNNER_SIZE
);
276 setCacheMode(CacheBackground
);
277 setRenderHint(QPainter::Antialiasing
);
279 scene
->addItem(item
);
281 scene
->addItem(topright
);
282 scene
->addItem(separator
);
283 scene
->addItem(bottomleft
);
284 scene
->addItem(bottom
);
285 scene
->addItem(bottomright
);
286 scene
->addItem(lineEdit
);
287 scene
->addItem(m_toggleExpand
);
288 scene
->addItem(close
);
292 topright
->setPos(341, 0);
293 separator
->setPos(1, 50);
294 bottomleft
->setPos(0, 51);
295 bottom
->setPos(9, 51);
296 bottomright
->setPos(341, 51);
297 m_toggleExpand
->setPos(30, 16);
298 lineEdit
->setPos(75, 15);
299 lineEdit
->setZValue(12);
300 lineEdit
->setTextWidth(200);
301 lineEdit
->setDefaultText("Type something here please..."); //FIXME: the label doesn't
302 // really behaves correctly (try to write some more text in).
303 close
->setPos(305, 16);
305 connect(close
, SIGNAL(clicked()), this, SLOT(close()));
306 connect(m_toggleExpand
, SIGNAL(clicked()), this, SLOT(expand()));
308 resize(KRUNNER_SIZE
);
310 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
311 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
312 setFrameShape(QFrame::NoFrame
);
313 new QShortcut( QKeySequence( Qt::Key_Escape
), this, SLOT(hide()) );
317 void Interface::resizeEvent( QResizeEvent
*e
)
319 QGraphicsView::resizeEvent( e
);
320 QBitmap
bitmap(350, 101);
321 bitmap
.fill(Qt::color0
);
323 m_background
->resize(350, 101);
324 m_background
->paint( &p
, QRectF(0, 0, 9, 50), "top-left" );
325 m_background
->paint( &p
, QRectF(9, 0, 332, 50), "top" );
326 m_background
->paint( &p
, QRectF(341, 0, 9, 50), "top-right" );
327 m_background
->paint( &p
, QRectF(1, 50, 348, 1), "separator" );
330 m_background
->paint( &p
, QRectF(1, 51, 348, 49), "separator" );
331 m_background
->paint( &p
, QRectF(0, 101, 9, 50), "bottom-left" );
332 m_background
->paint( &p
, QRectF(9, 101, 332, 50), "bottom" );
333 m_background
->paint( &p
, QRectF(341, 101, 9, 50), "bottom-right" );
335 m_background
->paint( &p
, QRectF(0, 51, 9, 50), "bottom-left" );
336 m_background
->paint( &p
, QRectF(9, 51, 332, 50), "bottom" );
337 m_background
->paint( &p
, QRectF(341, 51, 9, 50), "bottom-right" );
343 Interface::~Interface()
347 void Interface::expand()
349 disconnect(m_toggleExpand
, SIGNAL(clicked()), this, SLOT(expand()));
350 connect(m_toggleExpand
, SIGNAL(clicked()), this, SLOT(collapse()));
351 kDebug() << "expanding..." << endl
;
354 m_toggleExpand
->setIcon("arrow-up");
357 void Interface::collapse()
359 disconnect(m_toggleExpand
, SIGNAL(clicked()), this, SLOT(collapse()));
360 connect(m_toggleExpand
, SIGNAL(clicked()), this, SLOT(expand()));
361 kDebug() << "collapsing..." << endl
;
364 m_toggleExpand
->setIcon("arrow-down");
367 void Interface::display( const QString
& term
)
369 kDebug() << "display() called, are we visible? " << isVisible() << endl
;
370 // m_searchTerm->setFocus();
372 if ( !term
.isEmpty() ) {
373 // m_searchTerm->setText( term );
376 KWindowSystem::setOnDesktop( winId(), KWindowSystem::currentDesktop() );
377 KDialog::centerOnScreen( this );
379 KWindowSystem::forceActiveWindow( winId() );
381 kDebug() << "about to match now that we've shown " << isVisible() << endl
;
386 void Interface::switchUser()
388 Plasma::AbstractRunner
*sessionrunner
= 0;
389 foreach (Plasma::AbstractRunner
* runner
, m_runners
) {
390 if (qstrcmp(runner
->metaObject()->className(), "SessionRunner") == 0) {
391 sessionrunner
= runner
;
396 if (!sessionrunner
) {
397 kDebug() << "Could not find the Sessionrunner; not showing any sessions!" << endl
;
402 // m_header->setText(i18n("Switch users"));
403 // m_header->setPixmap("user");
404 KActionCollection
*matches
= sessionrunner
->matches("SESSIONS", 0, 0);
406 foreach (QAction
*action
, matches
->actions()) {
407 bool makeDefault
= !m_defaultMatch
&& action
->isEnabled();
409 // SearchMatch *match = new SearchMatch(action, sessionrunner, m_matchList);
410 // m_searchMatches.append(match);
413 // m_defaultMatch = match;
414 m_defaultMatch
->setDefault(true);
415 // m_runButton->setEnabled(true);
416 // m_optionsButton->setEnabled(sessionrunner->hasOptions());
420 if (!m_defaultMatch
) {
421 // m_matchList->addItem(i18n("No desktop sessions available"));
425 void Interface::setWidgetPalettes()
427 // a nice palette to use with the widgets
428 QPalette widgetPalette
= palette();
429 QColor headerBgColor
= widgetPalette
.color( QPalette::Active
,
431 headerBgColor
.setAlpha( 200 );
432 widgetPalette
.setColor( QPalette::Base
, headerBgColor
);
434 // m_header->setPalette( widgetPalette );
435 // m_searchTerm->setPalette( widgetPalette );
436 // m_matchList->setPalette( widgetPalette );
439 void Interface::resetInterface()
441 // m_header->setText(i18n("Enter the name of an application, location or search term below."));
442 // m_header->setPixmap("system-search");
443 // m_searchTerm->clear();
445 m_searchMatches
.clear();
446 // m_matchList->clear();
447 // m_runButton->setEnabled( false );
448 // m_optionsButton->setEnabled( false );
449 showOptions( false );
452 void Interface::showEvent( QShowEvent
* e
)
456 kDebug() << "show event" << endl
;
457 QGraphicsView::showEvent( e
);
460 void Interface::hideEvent( QHideEvent
* e
)
462 kDebug() << "hide event" << endl
;
467 void Interface::matchActivated(QListWidgetItem
* item
)
469 SearchMatch
* match
= dynamic_cast<SearchMatch
*>(item
);
470 // m_optionsButton->setEnabled( match && match->runner()->hasOptions() );
472 if ( match
&& match
->actionEnabled() ) {
473 //kDebug() << "match activated! " << match->text() << endl;
479 void Interface::match(const QString
& t
)
481 m_searchTimer
.stop();
484 QString term
= t
.trimmed();
486 if ( term
.isEmpty() ) {
491 QMap
<Plasma::AbstractRunner
*, SearchMatch
*> matches
;
495 // get the exact matches
496 foreach ( Plasma::AbstractRunner
* runner
, m_runners
) {
497 //kDebug() << "\trunner: " << runner->objectName() << endl;
498 QAction
* exactMatch
= runner
->exactMatch( term
) ;
501 SearchMatch
* match
= 0;
502 bool makeDefault
= !m_defaultMatch
&& exactMatch
->isEnabled();
504 QMap
<Plasma::AbstractRunner
*, SearchMatch
*>::iterator it
= m_matches
.find( runner
);
505 if ( it
!= m_matches
.end() ) {
507 match
->setAction( exactMatch
);
508 matches
[runner
] = match
;
509 m_matches
.erase( it
);
511 match
= new SearchMatch( exactMatch
, runner
, 0 );
512 // m_matchList->insertItem( matchCount, match );
516 match
->setDefault( true );
517 m_defaultMatch
= match
;
518 // m_optionsButton->setEnabled( runner->hasOptions() );
519 // m_runButton->setEnabled( true );
523 matches
[runner
] = match
;
527 if ( !m_defaultMatch
) {
528 showOptions( false );
529 // m_runButton->setEnabled( false );
532 qDeleteAll(m_matches
);
534 m_searchTimer
.start( 200 );
537 void Interface::fuzzySearch()
539 m_searchTimer
.stop();
541 // TODO: we may want to stop this from flickering about as well,
542 // similar to match above
543 foreach ( SearchMatch
* match
, m_searchMatches
) {
547 m_searchMatches
.clear();
549 // QString term = m_searchTerm->text().trimmed();
551 // get the inexact matches
552 // foreach ( Plasma::AbstractRunner* runner, m_runners ) {
553 // KActionCollection* matches = runner->matches( term, 10, 0 );
554 //kDebug() << "\t\tturned up " << matches->actions().count() << " matches " << endl;
555 // foreach ( QAction* action, matches->actions() ) {
556 // bool makeDefault = !m_defaultMatch && action->isEnabled();
557 //kDebug() << "\t\t " << action << ": " << action->text() << " " << !m_defaultMatch << " " << action->isEnabled() << endl;
558 // SearchMatch* match = new SearchMatch( action, runner, m_matchList );
559 // m_searchMatches.append( match );
561 // if ( makeDefault ) {
562 // m_defaultMatch = match;
563 // m_defaultMatch->setDefault( true );
564 // m_runButton->setEnabled( true );
565 // m_optionsButton->setEnabled( runner->hasOptions() );
571 void Interface::updateMatches()
576 void Interface::exec()
578 // SearchMatch* match = dynamic_cast<SearchMatch*>( m_matchList->currentItem() );
580 // if ( match && match->actionEnabled() ) {
581 // matchActivated( match );
582 // } else if ( m_defaultMatch ) {
583 // matchActivated( m_defaultMatch );
587 void Interface::showOptions(bool show
)
589 //TODO: in the case where we are no longer showing options
590 // should we have the runner delete it's options?
592 if ( !m_defaultMatch
|| !m_defaultMatch
->runner()->hasOptions() ) {
593 // in this case, there is nothing to show
597 // if ( !m_expander ) {
598 //kDebug() << "creating m_expander" << endl;
599 // m_expander = new CollapsibleWidget( this );
600 // connect( m_expander, SIGNAL( collapseCompleted() ),
601 // m_expander, SLOT( hide() ) );
602 // m_layout->insertWidget( 3, m_expander );
605 //kDebug() << "set inner widget to " << m_defaultMatch->runner()->options() << endl;
606 // m_expander->setInnerWidget( m_defaultMatch->runner()->options() );
607 // m_expander->show();
608 // m_optionsButton->setText( i18n( "Hide Options" ) );
610 // m_optionsButton->setText( i18n( "Show Options" ) );
611 // resize( 400, 250 );
612 resize(KRUNNER_SIZE
);
615 // if ( m_expander ) {
616 //TODO: we need to insert an element into the krunner dialog
617 // that is big enough for the options. this will prevent
618 // other items in the dialog from moving around and look
619 // more "natural"; it should appear as if a "drawer" is
620 // being pulled open, e.g. an expander.
621 // m_expander->setExpanded( show );
623 // m_optionsButton->setChecked( show );
626 void Interface::setDefaultItem( QListWidgetItem
* item
)
632 if ( m_defaultMatch
) {
633 m_defaultMatch
->setDefault( false );
636 m_defaultMatch
= dynamic_cast<SearchMatch
*>( item
);
638 bool hasOptions
= m_defaultMatch
&& m_defaultMatch
->runner()->hasOptions();
639 // m_optionsButton->setEnabled( hasOptions );
641 // if ( m_expander && !hasOptions ) {
642 // m_expander->hide();
646 #include "interface.moc"