2 * Copyright (C) 2006 Aaron Seigo <aseigo@kde.org>
3 * Copyright (C) 2007 Riccardo Iaconelli <riccardo@kde.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Library General Public License version 2 as
7 * published by the Free Software Foundation
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include <QApplication>
23 #include <QListWidget>
24 #include <QVBoxLayout>
25 #include <QHBoxLayout>
29 #include <QGraphicsItem>
32 #include <KActionCollection>
37 #include <KGlobalSettings>
38 #include <KPushButton>
39 #include <KStandardGuiItem>
40 #include <KTitleWidget>
41 #include <KWindowSystem>
43 #include <plasma/abstractrunner.h>
45 #include <QSvgRenderer>
46 #include <QResizeEvent>
51 #include <plasma/widgets/lineedit.h>
53 #include "runners/services/servicerunner.h"
54 #include "runners/sessions/sessionrunner.h"
55 #include "runners/shell/shellrunner.h"
56 #include "collapsiblewidget.h"
57 #include "interface.h"
58 #include "interfaceadaptor.h"
59 #include "krunnerapp.h"
63 #define KRUNNER_SIZE 350, 101
66 // A little hack of a class to let us easily activate a match
68 class SearchMatch
: public QListWidgetItem
71 SearchMatch( QAction
* action
, Plasma::AbstractRunner
* runner
, QListWidget
* parent
)
72 : QListWidgetItem( parent
),
82 m_action
->activate( QAction::Trigger
);
87 return m_action
->isEnabled();
90 void setAction( QAction
* action
)
93 setIcon( m_action
->icon() );
94 setText( i18n("%1 (%2)",
96 m_runner
->objectName() ) );
98 // in case our new action is now enabled and the old one wasn't, or
100 setDefault( m_default
);
103 Plasma::AbstractRunner
* runner()
108 void setDefault( bool def
) {
109 if ( m_default
== def
) {
116 if ( m_action
->isEnabled() ) {
117 setText( text().prepend( i18n("Default: ") ) );
120 setText( text().mid( 9 ) );
127 Plasma::AbstractRunner
* m_runner
;
130 GenericItem::GenericItem(Plasma::Svg
*renderer
, Interface
*qgv
)
132 m_background
= renderer
;
137 void GenericItem::setElement(const QString
&element
)
140 m_background
->resize(350, 101);
144 QRectF
GenericItem::itemRectF() const
147 if (m_element
== "top-left" || m_element
== "top-right" ||
148 m_element
== "bottom-left" || m_element
== "bottom-right") {
150 rect
= QRectF(0, 0, 9, 50);
152 } else if (m_element
== "top" || m_element
== "bottom") {
154 rect
= QRectF(0, 0, 332, 50);
156 } else if (m_element
== "separator") {
157 rect
= QRectF(0, 0, 348, 1);
158 } else if (m_element
== "background") {
159 rect
= QRectF(0, 0, 350, m_h
);
164 QRectF
GenericItem::boundingRect() const
166 if (m_element
.isEmpty()) {
167 return QRectF(0, 0, 0, 0);
172 void GenericItem::paint(QPainter
*painter
,
173 const QStyleOptionGraphicsItem
*option
,
176 m_background
->paint(painter
, itemRectF(), m_element
);
180 // MainItem::MainItem()
182 // m_background = new Plasma::Svg( "dialogs/krunner", this );
183 // m_background->setContentType(Plasma::Svg::ImageSet);
184 // // connect( m_background, SIGNAL(repaintNeeded()), this, SLOT(update()) );
185 // m_background->resize(KRUNNER_SIZE);
188 // void MainItem::paint(QPainter *painter,
189 // const QStyleOptionGraphicsItem *option,
192 // // m_background->paint( painter, QRectF(0, 0, 9, 50), "top-left" );
193 // // m_background->paint( painter, QRectF(9, 0, 332, 50), "top" );
194 // // m_background->paint( painter, QRectF(341, 0, 9, 50), "top-right" );
195 // // m_background->paint( painter, QRectF(1, 50, 348, 1), "separator" );
196 // // m_background->paint( painter, QRectF(0, 51, 9, 50), "bottom-left" );
197 // // m_background->paint( painter, QRectF(9, 51, 332, 50), "bottom" );
198 // // m_background->paint( painter, QRectF(341, 51, 9, 50), "bottom-right" );
199 // // m_background->paint( painter, 0, 0 );
202 void PushIcon::setIcon(const QString
&icon
)
207 void PushIcon::paint(QPainter
*painter
,
208 const QStyleOptionGraphicsItem
*option
,
212 painter
->drawPixmap(0, 0, exec
.pixmap(22, 22));
215 Interface::Interface(QWidget
* parent
)
216 : QGraphicsView( parent
),
221 m_background
= new Plasma::Svg("dialogs/krunner", this);
222 m_background
->setContentType(Plasma::Svg::ImageSet
);
223 m_background
->resize(350, 101);
224 connect(m_background
, SIGNAL(repaintNeeded()), this, SLOT(update()));
226 setWindowTitle(i18n("Run Command"));
228 connect(&m_searchTimer
, SIGNAL(timeout()),
229 this, SLOT(fuzzySearch()));
231 topleft
= new GenericItem(m_background
, this);
232 topleft
->setElement("top-left");
233 top
= new GenericItem(m_background
, this);
234 top
->setElement("top");
235 topright
= new GenericItem(m_background
, this);
236 topright
->setElement("top-right");
237 separator
= new GenericItem(m_background
, this);
238 separator
->setElement("separator");
239 separator2
= new GenericItem(m_background
, this);
240 separator2
->setElement("separator");
241 background
= new GenericItem(m_background
, this);
242 background
->setElement("background");
243 bottomleft
= new GenericItem(m_background
, this);
244 bottomleft
->setElement("bottom-left");
245 bottom
= new GenericItem(m_background
, this);
246 bottom
->setElement("bottom");
247 bottomright
= new GenericItem(m_background
, this);
248 bottomright
->setElement("bottom-right");
249 lineEdit
= new Plasma::LineEdit
;
250 m_toggleExpand
= new PushIcon(this);
251 m_toggleExpand
->setIcon("arrow-down");
252 close
= new PushIcon(this);
253 close
->setIcon("no");
255 QGraphicsScene
*scene
= new QGraphicsScene(this);
256 scene
->setItemIndexMethod(QGraphicsScene::NoIndex
);
257 scene
->setSceneRect(0, 0, KRUNNER_SIZE
);
259 setCacheMode(CacheBackground
);
260 setRenderHint(QPainter::Antialiasing
);
263 scene
->addItem(background
);
264 scene
->addItem(topleft
);
266 scene
->addItem(topright
);
267 scene
->addItem(separator
);
268 scene
->addItem(separator2
);
269 scene
->addItem(bottomleft
);
270 scene
->addItem(bottom
);
271 scene
->addItem(bottomright
);
272 scene
->addItem(lineEdit
);
273 scene
->addItem(m_toggleExpand
);
274 scene
->addItem(close
);
276 topleft
->setPos(0, 0);
278 topright
->setPos(341, 0);
279 separator
->setPos(1, 50);
280 separator2
->setPos(1, 52);
281 separator2
->setVisible(false);
282 background
->setPos(0, 51);
283 background
->setVisible(false);
284 // background->setZValue(0);
285 bottomleft
->setPos(0, 51);
286 // bottomleft->setZValue(135);
287 bottom
->setPos(9, 51);
288 // bottom->setZValue(134);
289 bottomright
->setPos(341, 51);
290 // bottomright->setZValue(133);
291 m_toggleExpand
->setPos(30, 16);
292 lineEdit
->setPos(75, 15);
293 lineEdit
->setZValue(5);
294 lineEdit
->setTextWidth(200);
295 //FIXME: the label doesn't really behaves correctly (try to write some more text in).
296 lineEdit
->setDefaultText("Type something here please...");
297 close
->setPos(305, 16);
299 connect(close
, SIGNAL(clicked()), this, SLOT(close()));
300 connect(m_toggleExpand
, SIGNAL(clicked()), this, SLOT(expand()));
302 resize(KRUNNER_SIZE
);
304 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
305 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
306 setFrameShape(QFrame::NoFrame
);
307 new QShortcut(QKeySequence(Qt::Key_Escape
), this, SLOT(hide()));
308 KDialog::centerOnScreen(this);
312 void Interface::resizeEvent( QResizeEvent
*e
)
314 // QGraphicsView::resizeEvent( e );
315 int addedHeight
= e
->size().height()-101;
316 QBitmap
bitmap(350, 101+addedHeight
);
317 bitmap
.fill(Qt::color0
);
319 p
.setBrush(Qt::black
);
321 // NOTE: this is just for the clipping, so I don't really care if the look of the stuff painted
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" );
328 p
.drawRect(0, 50, 349, addedHeight
);
329 m_background
->paint( &p
, QRectF(0, 51+addedHeight
, 9, 50), "bottom-left" );
330 m_background
->paint( &p
, QRectF(9, 51+addedHeight
, 332, 50), "bottom" );
331 m_background
->paint( &p
, QRectF(341, 51+addedHeight
, 9, 50), "bottom-right" );
336 topleft
->setPos(0, 0);
338 topright
->setPos(341, 0);
339 m_toggleExpand
->setPos(30, 16);
340 lineEdit
->setPos(75, 15);
341 separator
->setPos(1, 50);
342 if (addedHeight
> 0) { //if expanded
343 separator
->setPos(1, 50);
344 background
->setPos(0, 51);
345 background
->setSize(350, addedHeight
-1);
346 background
->setVisible(true);
347 separator2
->setPos(1, 50+addedHeight
);
348 separator2
->setVisible(true);
350 background
->setVisible(false);
351 separator2
->setVisible(false);
353 bottomleft
->setPos(0, 51+addedHeight
);
354 bottom
->setPos(9, 51+addedHeight
);
355 bottomright
->setPos(341, 51+addedHeight
);
358 Interface::~Interface()
362 void Interface::expand()
364 disconnect(m_toggleExpand
, SIGNAL(clicked()), this, SLOT(expand()));
365 connect(m_toggleExpand
, SIGNAL(clicked()), this, SLOT(collapse()));
366 kDebug() << "expanding..." << endl
;
368 QTimeLine
*tl
= new QTimeLine(200, this);
369 tl
->setFrameRange(10, 30);
371 connect(tl
, SIGNAL(frameChanged(int)), this, SLOT(slotResize(int)));
372 m_toggleExpand
->setIcon("arrow-up");
375 void Interface::collapse()
377 disconnect(m_toggleExpand
, SIGNAL(clicked()), this, SLOT(collapse()));
378 connect(m_toggleExpand
, SIGNAL(clicked()), this, SLOT(expand()));
379 kDebug() << "collapsing..." << endl
;
381 QTimeLine
*tl
= new QTimeLine(200, this);
382 tl
->setFrameRange(30, 10);
384 connect(tl
, SIGNAL(frameChanged(int)), this, SLOT(slotResize(int)));
385 m_toggleExpand
->setIcon("arrow-down");
388 void Interface::slotResize(int size
)
390 resize(350, size
*101*0.1);
393 void Interface::display( const QString
& term
)
395 kDebug() << "display() called, are we visible? " << isVisible() << endl
;
396 // m_searchTerm->setFocus();
398 if ( !term
.isEmpty() ) {
399 // m_searchTerm->setText( term );
402 KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop());
403 KDialog::centerOnScreen( this );
405 KWindowSystem::forceActiveWindow(winId());
407 kDebug() << "about to match now that we've shown " << isVisible() << endl
;
412 void Interface::switchUser()
414 Plasma::AbstractRunner
*sessionrunner
= 0;
415 foreach (Plasma::AbstractRunner
* runner
, m_runners
) {
416 if (qstrcmp(runner
->metaObject()->className(), "SessionRunner") == 0) {
417 sessionrunner
= runner
;
422 if (!sessionrunner
) {
423 kDebug() << "Could not find the Sessionrunner; not showing any sessions!" << endl
;
428 // m_header->setText(i18n("Switch users"));
429 // m_header->setPixmap("user");
430 KActionCollection
*matches
= sessionrunner
->matches("SESSIONS", 0, 0);
432 foreach (QAction
*action
, matches
->actions()) {
433 bool makeDefault
= !m_defaultMatch
&& action
->isEnabled();
435 // SearchMatch *match = new SearchMatch(action, sessionrunner, m_matchList);
436 // m_searchMatches.append(match);
439 // m_defaultMatch = match;
440 m_defaultMatch
->setDefault(true);
441 // m_runButton->setEnabled(true);
442 // m_optionsButton->setEnabled(sessionrunner->hasOptions());
446 if (!m_defaultMatch
) {
447 // m_matchList->addItem(i18n("No desktop sessions available"));
451 void Interface::setWidgetPalettes()
453 // a nice palette to use with the widgets
454 QPalette widgetPalette
= palette();
455 QColor headerBgColor
= widgetPalette
.color( QPalette::Active
,
457 headerBgColor
.setAlpha( 200 );
458 widgetPalette
.setColor( QPalette::Base
, headerBgColor
);
460 // m_header->setPalette( widgetPalette );
461 // m_searchTerm->setPalette( widgetPalette );
462 // m_matchList->setPalette( widgetPalette );
465 void Interface::resetInterface()
467 // m_header->setText(i18n("Enter the name of an application, location or search term below."));
468 // m_header->setPixmap("system-search");
469 // m_searchTerm->clear();
471 m_searchMatches
.clear();
472 // m_matchList->clear();
473 // m_runButton->setEnabled( false );
474 // m_optionsButton->setEnabled( false );
475 showOptions( false );
478 void Interface::showEvent( QShowEvent
* e
)
482 kDebug() << "show event" << endl
;
483 QGraphicsView::showEvent( e
);
486 void Interface::hideEvent( QHideEvent
* e
)
488 kDebug() << "hide event" << endl
;
493 void Interface::matchActivated(QListWidgetItem
* item
)
495 SearchMatch
* match
= dynamic_cast<SearchMatch
*>(item
);
496 // m_optionsButton->setEnabled( match && match->runner()->hasOptions() );
498 if ( match
&& match
->actionEnabled() ) {
499 //kDebug() << "match activated! " << match->text() << endl;
505 void Interface::match(const QString
& t
)
507 m_searchTimer
.stop();
510 QString term
= t
.trimmed();
512 if ( term
.isEmpty() ) {
517 QMap
<Plasma::AbstractRunner
*, SearchMatch
*> matches
;
521 // get the exact matches
522 foreach ( Plasma::AbstractRunner
* runner
, m_runners
) {
523 //kDebug() << "\trunner: " << runner->objectName() << endl;
524 QAction
* exactMatch
= runner
->exactMatch( term
) ;
527 SearchMatch
* match
= 0;
528 bool makeDefault
= !m_defaultMatch
&& exactMatch
->isEnabled();
530 QMap
<Plasma::AbstractRunner
*, SearchMatch
*>::iterator it
= m_matches
.find( runner
);
531 if ( it
!= m_matches
.end() ) {
533 match
->setAction( exactMatch
);
534 matches
[runner
] = match
;
535 m_matches
.erase( it
);
537 match
= new SearchMatch( exactMatch
, runner
, 0 );
538 // m_matchList->insertItem( matchCount, match );
542 match
->setDefault( true );
543 m_defaultMatch
= match
;
544 // m_optionsButton->setEnabled( runner->hasOptions() );
545 // m_runButton->setEnabled( true );
549 matches
[runner
] = match
;
553 if ( !m_defaultMatch
) {
554 showOptions( false );
555 // m_runButton->setEnabled( false );
558 qDeleteAll(m_matches
);
560 m_searchTimer
.start( 200 );
563 void Interface::fuzzySearch()
565 m_searchTimer
.stop();
567 // TODO: we may want to stop this from flickering about as well,
568 // similar to match above
569 foreach ( SearchMatch
* match
, m_searchMatches
) {
573 m_searchMatches
.clear();
575 // QString term = m_searchTerm->text().trimmed();
577 // get the inexact matches
578 // foreach ( Plasma::AbstractRunner* runner, m_runners ) {
579 // KActionCollection* matches = runner->matches( term, 10, 0 );
580 //kDebug() << "\t\tturned up " << matches->actions().count() << " matches " << endl;
581 // foreach ( QAction* action, matches->actions() ) {
582 // bool makeDefault = !m_defaultMatch && action->isEnabled();
583 //kDebug() << "\t\t " << action << ": " << action->text() << " " << !m_defaultMatch << " " << action->isEnabled() << endl;
584 // SearchMatch* match = new SearchMatch( action, runner, m_matchList );
585 // m_searchMatches.append( match );
587 // if ( makeDefault ) {
588 // m_defaultMatch = match;
589 // m_defaultMatch->setDefault( true );
590 // m_runButton->setEnabled( true );
591 // m_optionsButton->setEnabled( runner->hasOptions() );
597 void Interface::updateMatches()
602 void Interface::exec()
604 // SearchMatch* match = dynamic_cast<SearchMatch*>( m_matchList->currentItem() );
606 // if ( match && match->actionEnabled() ) {
607 // matchActivated( match );
608 // } else if ( m_defaultMatch ) {
609 // matchActivated( m_defaultMatch );
613 void Interface::showOptions(bool show
)
615 //TODO: in the case where we are no longer showing options
616 // should we have the runner delete it's options?
618 if ( !m_defaultMatch
|| !m_defaultMatch
->runner()->hasOptions() ) {
619 // in this case, there is nothing to show
623 // if ( !m_expander ) {
624 //kDebug() << "creating m_expander" << endl;
625 // m_expander = new CollapsibleWidget( this );
626 // connect( m_expander, SIGNAL( collapseCompleted() ),
627 // m_expander, SLOT( hide() ) );
628 // m_layout->insertWidget( 3, m_expander );
631 //kDebug() << "set inner widget to " << m_defaultMatch->runner()->options() << endl;
632 // m_expander->setInnerWidget( m_defaultMatch->runner()->options() );
633 // m_expander->show();
634 // m_optionsButton->setText( i18n( "Hide Options" ) );
636 // m_optionsButton->setText( i18n( "Show Options" ) );
637 // resize( 400, 250 );
638 resize(KRUNNER_SIZE
);
641 // if ( m_expander ) {
642 //TODO: we need to insert an element into the krunner dialog
643 // that is big enough for the options. this will prevent
644 // other items in the dialog from moving around and look
645 // more "natural"; it should appear as if a "drawer" is
646 // being pulled open, e.g. an expander.
647 // m_expander->setExpanded( show );
649 // m_optionsButton->setChecked( show );
652 void Interface::setDefaultItem( QListWidgetItem
* item
)
658 if ( m_defaultMatch
) {
659 m_defaultMatch
->setDefault( false );
662 m_defaultMatch
= dynamic_cast<SearchMatch
*>( item
);
664 bool hasOptions
= m_defaultMatch
&& m_defaultMatch
->runner()->hasOptions();
665 // m_optionsButton->setEnabled( hasOptions );
667 // if ( m_expander && !hasOptions ) {
668 // m_expander->hide();
672 #include "interface.moc"