]> cloud.milkyroute.net Git - dolphin.git/blob - src/infosidebarpage.cpp
* Starts to add Nepomuk supports to dolphin (currently, only annotations are supported)
[dolphin.git] / src / infosidebarpage.cpp
1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
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. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * 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 *
18 ***************************************************************************/
19
20 #include "infosidebarpage.h"
21 #include <assert.h>
22
23 #include <qlayout.h>
24 #include <qpixmap.h>
25 #include <qlabel.h>
26 #include <qtimer.h>
27 #include <qpushbutton.h>
28
29 #include <q3vgroupbox.h>
30 #include <q3popupmenu.h>
31 #include <qpainter.h>
32 #include <qfontmetrics.h>
33 #include <q3grid.h>
34 #include <q3hgroupbox.h>
35 //Added by qt3to4:
36 #include <Q3ValueList>
37 #include <QEvent>
38 #include <Q3VBoxLayout>
39 #include <QInputDialog>
40
41 #include <kbookmarkmanager.h>
42 #include <klocale.h>
43 #include <kstandarddirs.h>
44 #include <kio/previewjob.h>
45 #include <kfileitem.h>
46 #include <kdialog.h>
47 #include <kglobalsettings.h>
48 #include <kfilemetainfo.h>
49 #include <kvbox.h>
50
51 #include "dolphinmainwindow.h"
52 #include "dolphinapplication.h"
53 #include "pixmapviewer.h"
54 #include "dolphinsettings.h"
55 #include "metadataloader.h"
56
57 InfoSidebarPage::InfoSidebarPage(DolphinMainWindow* mainWindow, QWidget* parent) :
58 SidebarPage(mainWindow, parent),
59 m_multipleSelection(false),
60 m_pendingPreview(false),
61 m_timer(0),
62 m_preview(0),
63 m_name(0),
64 m_infos(0),
65 m_metadata(DolphinApplication::app()->metadataLoader())
66 {
67 const int spacing = KDialog::spacingHint();
68
69 m_timer = new QTimer(this);
70 connect(m_timer, SIGNAL(timeout()),
71 this, SLOT(slotTimeout()));
72
73 QVBoxLayout* layout = new QVBoxLayout;
74 layout->setSpacing(spacing);
75
76 // preview
77 m_preview = new PixmapViewer(this);
78 m_preview->setMinimumWidth(K3Icon::SizeEnormous);
79 m_preview->setFixedHeight(K3Icon::SizeEnormous);
80
81 // name
82 m_name = new QLabel(this);
83 m_name->setTextFormat(Qt::RichText);
84 m_name->setAlignment(m_name->alignment() | Qt::AlignHCenter);
85 QFontMetrics fontMetrics(m_name->font());
86 m_name->setMinimumHeight(fontMetrics.height() * 3);
87 m_name->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
88
89 QWidget* sep1 = new Q3HGroupBox(this); // TODO: check whether default widget exist for this?
90 sep1->setFixedHeight(1);
91
92 // general information
93 m_infos = new QLabel(this);
94 m_infos->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
95 m_infos->setTextFormat(Qt::RichText);
96
97 // annotation
98 if (m_metadata->storageUp()) {
99 m_annotationLabel = new QLabel(this);
100 m_annotationLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
101 m_annotationLabel->setTextFormat(Qt::RichText);
102 m_annotationLabel->setWordWrap(true);
103 m_annotationButton = new QPushButton("", this);
104 m_annotationButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
105 connect(m_annotationButton, SIGNAL(released()), this, SLOT(changeAnnotation()));
106 }
107
108 QWidget* sep2 = new Q3HGroupBox(this); // TODO: check whether default widget exist for this?
109 sep2->setFixedHeight(1);
110
111 QWidget* sep3 = new Q3HGroupBox(this); // TODO: check whether default widget exist for this?
112 sep3->setFixedHeight(1);
113
114 // actions
115 m_actionBox = new KVBox(this);
116 m_actionBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
117
118 // Add a dummy widget with no restriction regarding a vertical resizing.
119 // This assures that information is always top aligned.
120 QWidget* dummy = new QWidget(this);
121
122 layout->addItem(new QSpacerItem(spacing, spacing, QSizePolicy::Preferred, QSizePolicy::Fixed));
123 layout->addWidget(m_preview);
124 layout->addWidget(m_name);
125 layout->addWidget(sep1);
126 layout->addWidget(m_infos);
127 layout->addWidget(sep2);
128 if (m_metadata->storageUp()) {
129 layout->addWidget(m_annotationLabel);
130 layout->addWidget(m_annotationButton);
131 layout->addWidget(sep3);
132 }
133 layout->addWidget(m_actionBox);
134 layout->addWidget(dummy);
135 setLayout(layout);
136 connect(mainWindow, SIGNAL(selectionChanged()),
137 this, SLOT(showItemInfo()));
138
139 connectToActiveView();
140 }
141
142 InfoSidebarPage::~InfoSidebarPage()
143 {
144 }
145
146 void InfoSidebarPage::activeViewChanged()
147 {
148 connectToActiveView();
149 }
150
151 void InfoSidebarPage::requestDelayedItemInfo(const KUrl& url)
152 {
153 cancelRequest();
154
155 if (!url.isEmpty() && !m_multipleSelection) {
156 m_urlCandidate = url;
157 m_timer->setSingleShot(true);
158 m_timer->start(300);
159 }
160 }
161
162 void InfoSidebarPage::requestItemInfo(const KUrl& url)
163 {
164 cancelRequest();
165
166 if (!url.isEmpty() && !m_multipleSelection) {
167 m_shownUrl = url;
168 showItemInfo();
169 }
170 }
171
172 void InfoSidebarPage::showItemInfo()
173 {
174 cancelRequest();
175
176 m_multipleSelection = false;
177
178 // show the preview...
179 DolphinView* view = mainWindow()->activeView();
180 const KFileItemList selectedItems = view->selectedItems();
181 if (selectedItems.count() > 1) {
182 m_multipleSelection = true;
183 }
184
185 if (m_multipleSelection) {
186 KIconLoader iconLoader;
187 QPixmap icon = iconLoader.loadIcon("exec",
188 K3Icon::NoGroup,
189 K3Icon::SizeEnormous);
190 m_preview->setPixmap(icon);
191 m_name->setText(i18n("%1 items selected",selectedItems.count()));
192 }
193 else if (!applyBookmark()) {
194 // try to get a preview pixmap from the item...
195 KUrl::List list;
196 list.append(m_shownUrl);
197
198 m_pendingPreview = true;
199 m_preview->setPixmap(QPixmap());
200
201 KIO::PreviewJob* job = KIO::filePreview(list,
202 m_preview->width(),
203 K3Icon::SizeEnormous,
204 0,
205 0,
206 true,
207 false);
208 job->setIgnoreMaximumSize(true);
209
210 connect(job, SIGNAL(gotPreview(const KFileItem*, const QPixmap&)),
211 this, SLOT(gotPreview(const KFileItem*, const QPixmap&)));
212 connect(job, SIGNAL(failed(const KFileItem*)),
213 this, SLOT(slotPreviewFailed(const KFileItem*)));
214
215 QString text("<b>");
216 text.append(m_shownUrl.fileName());
217 text.append("</b>");
218 m_name->setText(text);
219 }
220
221 createMetaInfo();
222 insertActions();
223 }
224
225 void InfoSidebarPage::slotTimeout()
226 {
227 m_shownUrl = m_urlCandidate;
228 showItemInfo();
229 }
230
231 void InfoSidebarPage::slotPreviewFailed(const KFileItem* item)
232 {
233 m_pendingPreview = false;
234 if (!applyBookmark()) {
235 m_preview->setPixmap(item->pixmap(K3Icon::SizeEnormous));
236 }
237 }
238
239 void InfoSidebarPage::gotPreview(const KFileItem* /* item */,
240 const QPixmap& pixmap)
241 {
242 if (m_pendingPreview) {
243 m_preview->setPixmap(pixmap);
244 m_pendingPreview = false;
245 }
246 }
247
248 void InfoSidebarPage::startService(int index)
249 {
250 DolphinView* view = mainWindow()->activeView();
251 if (view->hasSelection()) {
252 KUrl::List selectedUrls = view->selectedUrls();
253 KDEDesktopMimeType::executeService(selectedUrls, m_actionsVector[index]);
254 }
255 else {
256 KDEDesktopMimeType::executeService(m_shownUrl, m_actionsVector[index]);
257 }
258 }
259
260 void InfoSidebarPage::connectToActiveView()
261 {
262 cancelRequest();
263
264 DolphinView* view = mainWindow()->activeView();
265 connect(view, SIGNAL(requestItemInfo(const KUrl&)),
266 this, SLOT(requestDelayedItemInfo(const KUrl&)));
267 connect(view, SIGNAL(urlChanged(const KUrl&)),
268 this, SLOT(requestItemInfo(const KUrl&)));
269
270 m_shownUrl = view->url();
271 showItemInfo();
272 }
273
274 bool InfoSidebarPage::applyBookmark()
275 {
276 KBookmarkGroup root = DolphinSettings::instance().bookmarkManager()->root();
277 KBookmark bookmark = root.first();
278 while (!bookmark.isNull()) {
279 if (m_shownUrl.equals(bookmark.url(), KUrl::CompareWithoutTrailingSlash)) {
280 QString text("<b>");
281 text.append(bookmark.text());
282 text.append("</b>");
283 m_name->setText(text);
284
285 KIconLoader iconLoader;
286 QPixmap icon = iconLoader.loadIcon(bookmark.icon(),
287 K3Icon::NoGroup,
288 K3Icon::SizeEnormous);
289 m_preview->setPixmap(icon);
290 return true;
291 }
292 bookmark = root.next(bookmark);
293 }
294
295 return false;
296 }
297
298 void InfoSidebarPage::cancelRequest()
299 {
300 m_timer->stop();
301 m_pendingPreview = false;
302 }
303
304 void InfoSidebarPage::createMetaInfo()
305 {
306 beginInfoLines();
307 DolphinView* view = mainWindow()->activeView();
308 if (!view->hasSelection()) {
309 KFileItem fileItem(S_IFDIR, KFileItem::Unknown, m_shownUrl);
310 fileItem.refresh();
311
312 if (fileItem.isDir()) {
313 addInfoLine(i18n("Type:"), i18n("Directory"));
314 }
315 showAnnotation(m_shownUrl);
316 }
317 else if (view->selectedItems().count() == 1) {
318 KFileItem* fileItem = view->selectedItems()[0];
319 addInfoLine(i18n("Type:"), fileItem->mimeComment());
320
321 QString sizeText(KIO::convertSize(fileItem->size()));
322 addInfoLine(i18n("Size:"), sizeText);
323 addInfoLine(i18n("Modified:"), fileItem->timeString());
324
325 const KFileMetaInfo& metaInfo = fileItem->metaInfo();
326 if (metaInfo.isValid()) {
327 QStringList keys = metaInfo.supportedKeys();
328 for (QStringList::Iterator it = keys.begin(); it != keys.end(); ++it) {
329 if (showMetaInfo(*it)) {
330 KFileMetaInfoItem metaInfoItem = metaInfo.item(*it);
331 addInfoLine(*it, metaInfoItem.string());
332 }
333 }
334 }
335 showAnnotation(fileItem->url());
336 }
337 else {
338 showAnnotations(view->selectedItems().urlList());
339 unsigned long int totSize = 0;
340 foreach(KFileItem* item, view->selectedItems()) {
341 totSize += item->size(); //FIXME what to do with directories ? (same with the one-item-selected-code), item->size() does not return the size of the content : not very instinctive for users
342 }
343 addInfoLine(i18n("Total size:"), KIO::convertSize(totSize));
344 }
345 endInfoLines();
346 }
347
348 void InfoSidebarPage::beginInfoLines()
349 {
350 m_infoLines = QString("");
351 }
352
353 void InfoSidebarPage::endInfoLines()
354 {
355 m_infos->setText(m_infoLines);
356 }
357
358 bool InfoSidebarPage::showMetaInfo(const QString& key) const
359 {
360 // sorted list of keys, where it's data should be shown
361 static const char* keys[] = {
362 "Album",
363 "Artist",
364 "Author",
365 "Bitrate",
366 "Date",
367 "Dimensions",
368 "Genre",
369 "Length",
370 "Lines",
371 "Pages",
372 "Title",
373 "Words"
374 };
375
376 // do a binary search for the key...
377 int top = 0;
378 int bottom = sizeof(keys) / sizeof(char*) - 1;
379 while (top < bottom) {
380 const int middle = (top + bottom) / 2;
381 const int result = key.compare(keys[middle]);
382 if (result < 0) {
383 bottom = middle - 1;
384 }
385 else if (result > 0) {
386 top = middle + 1;
387 }
388 else {
389 return true;
390 }
391 }
392
393 return false;
394 }
395
396 void InfoSidebarPage::addInfoLine(const QString& labelText, const QString& infoText)
397 {
398 if (!m_infoLines.isEmpty())
399 m_infoLines += "<br/>";
400 m_infoLines += QString("<b>%1</b> %2").arg(labelText).arg(infoText);
401 }
402
403 void InfoSidebarPage::insertActions()
404 {
405 // delete all existing action widgets
406 // TODO: just use children() from QObject...
407 Q3PtrListIterator<QWidget> deleteIter(m_actionWidgets);
408 QWidget* widget = 0;
409 while ((widget = deleteIter.current()) != 0) {
410 widget->close();
411 widget->deleteLater();
412 ++deleteIter;
413 }
414
415 m_actionWidgets.clear();
416 m_actionsVector.clear();
417
418 int actionsIndex = 0;
419
420 // The algorithm for searching the available actions works on a list
421 // of KFileItems. If no selection is given, a temporary KFileItem
422 // by the given Url 'url' is created and added to the list.
423 KFileItem fileItem(S_IFDIR, KFileItem::Unknown, m_shownUrl);
424 KFileItemList itemList = mainWindow()->activeView()->selectedItems();
425 if (itemList.isEmpty()) {
426 fileItem.refresh();
427 itemList.append(&fileItem);
428 }
429
430 // 'itemList' contains now all KFileItems, where an item information should be shown.
431 // TODO: the following algorithm is quite equal to DolphinContextMenu::insertActionItems().
432 // It's open yet whether they should be merged or whether they have to work slightly different.
433 QStringList dirs = KGlobal::dirs()->findDirs("data", "dolphin/servicemenus/");
434 for (QStringList::ConstIterator dirIt = dirs.begin(); dirIt != dirs.end(); ++dirIt) {
435 QDir dir(*dirIt);
436 QStringList entries = dir.entryList(QStringList("*.desktop"), QDir::Files);
437
438 for (QStringList::ConstIterator entryIt = entries.begin(); entryIt != entries.end(); ++entryIt) {
439 KConfigGroup cfg(KSharedConfig::openConfig( *dirIt + *entryIt, KConfig::OnlyLocal ), "Desktop Entry" );
440 if ((cfg.hasKey("Actions") || cfg.hasKey("X-KDE-GetActionMenu")) && cfg.hasKey("ServiceTypes")) {
441 const QStringList types = cfg.readEntry("ServiceTypes", QStringList(), ',');
442 for (QStringList::ConstIterator it = types.begin(); it != types.end(); ++it) {
443 // check whether the mime type is equal or whether the
444 // mimegroup (e. g. image/*) is supported
445
446 bool insert = false;
447 if ((*it) == "all/allfiles") {
448 // The service type is valid for all files, but not for directories.
449 // Check whether the selected items only consist of files...
450 QListIterator<KFileItem*> mimeIt(itemList);
451 insert = true;
452 while (insert && mimeIt.hasNext()) {
453 KFileItem* item = mimeIt.next();
454 insert = !item->isDir();
455 }
456 }
457
458 if (!insert) {
459 // Check whether the MIME types of all selected files match
460 // to the mimetype of the service action. As soon as one MIME
461 // type does not match, no service menu is shown at all.
462 QListIterator<KFileItem*> mimeIt(itemList);
463 insert = true;
464 while (insert && mimeIt.hasNext()) {
465 KFileItem* item = mimeIt.next();
466 const QString mimeType(item->mimetype());
467 const QString mimeGroup(mimeType.left(mimeType.indexOf('/')));
468
469 insert = (*it == mimeType) ||
470 ((*it).right(1) == "*") &&
471 ((*it).left((*it).indexOf('/')) == mimeGroup);
472 }
473 }
474
475 if (insert) {
476 const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
477 Q3PopupMenu* popup = 0;
478 if (!submenuName.isEmpty()) {
479 // create a sub menu containing all actions
480 popup = new Q3PopupMenu();
481 connect(popup, SIGNAL(activated(int)),
482 this, SLOT(startService(int)));
483
484 QPushButton* button = new QPushButton(submenuName, m_actionBox);
485 button->setFlat(true);
486 button->setMenu(popup);
487 button->show();
488 m_actionWidgets.append(button);
489 }
490
491 Q3ValueList<KDEDesktopMimeType::Service> userServices =
492 KDEDesktopMimeType::userDefinedServices(*dirIt + *entryIt, true);
493
494 // iterate through all actions and add them to a widget
495 Q3ValueList<KDEDesktopMimeType::Service>::Iterator serviceIt;
496 for (serviceIt = userServices.begin(); serviceIt != userServices.end(); ++serviceIt) {
497 KDEDesktopMimeType::Service service = (*serviceIt);
498 if (popup == 0) {
499 ServiceButton* button = new ServiceButton(KIcon(service.m_strIcon),
500 service.m_strName,
501 m_actionBox,
502 actionsIndex);
503 connect(button, SIGNAL(requestServiceStart(int)),
504 this, SLOT(startService(int)));
505 m_actionWidgets.append(button);
506 button->show();
507 }
508 else {
509 popup->insertItem(KIcon(service.m_strIcon), service.m_strName, actionsIndex);
510 }
511
512 m_actionsVector.append(service);
513 ++actionsIndex;
514 }
515 }
516 }
517 }
518 }
519 }
520 }
521
522 void InfoSidebarPage::showAnnotation(const KUrl& file)
523 {
524 if(m_metadata->storageUp()) {
525 QString text = m_metadata->getAnnotation(file);
526 if (!text.isEmpty()) {
527 m_annotationLabel->show();
528 m_annotationLabel->setText(QString("<b>%1</b> :<br/>%2").arg(i18n("Annotation")).arg(text));
529 m_annotationButton->setText(i18n("Change annotation"));
530 } else {
531 m_annotationLabel->hide();
532 m_annotationButton->setText(i18n("Annotate file"));
533 }
534 }
535 }
536
537 void InfoSidebarPage::showAnnotations(const KUrl::List& files)
538 {
539 static unsigned int maxShownAnnot = 3;
540 if (m_metadata->storageUp()) {
541 bool hasAnnotation = false;
542 unsigned int annotateNum = 0;
543 QString firsts("<b>%1 :</b><br/>");
544 firsts.arg(i18n("Annotations"));
545 foreach (KUrl file, files) {
546 QString annotation = m_metadata->getAnnotation(file);
547 if (!annotation.isEmpty()) {
548 hasAnnotation = true;
549 if(annotateNum < maxShownAnnot) {
550 firsts += m_annotationLabel->fontMetrics().elidedText(QString("<b>%1</b> : %2<br/>").arg(file.fileName()).arg(annotation), Qt::ElideRight, width());
551 annotateNum++;
552 }
553 }
554 }
555 if (hasAnnotation) {
556 m_annotationLabel->show();
557 m_annotationLabel->setText(firsts);
558 } else m_annotationLabel->hide();
559 m_annotationButton->setText(hasAnnotation ? i18n("Change annotations") : i18n("Annotate files"));
560 }
561 }
562
563 void InfoSidebarPage::changeAnnotation()
564 {
565 bool ok = false;
566 KUrl::List files(mainWindow()->activeView()->selectedItems().urlList());
567 QString name, old;
568 if (files.isEmpty()) {
569 files << m_shownUrl;
570 }
571 else if (files.count() == 1) {
572 name = files[0].url();
573 old = m_metadata->getAnnotation(files[0]);
574 }
575 else {
576 name = QString("%1 files").arg(files.count());
577 old = QString();
578 }
579 QString text = QInputDialog::getText(this, "Annotate", QString("Set annotation for %1").arg(name), QLineEdit::Normal, old, &ok);//FIXME temporary, must move to a real dialog
580 if(ok) {
581 foreach(KUrl file, files) {
582 m_metadata->setAnnotation(file, text);
583 }
584 showAnnotation(files[0]);
585 }
586 }
587
588 ServiceButton::ServiceButton(const QIcon& icon,
589 const QString& text,
590 QWidget* parent,
591 int index) :
592 QPushButton(icon, text, parent),
593 m_hover(false),
594 m_index(index)
595 {
596 setEraseColor(palette().brush(QPalette::Background).color());
597 setFocusPolicy(Qt::NoFocus);
598 connect(this, SIGNAL(released()),
599 this, SLOT(slotReleased()));
600 }
601
602 ServiceButton::~ServiceButton()
603 {
604 }
605
606 void ServiceButton::paintEvent(QPaintEvent* event)
607 {
608 QPainter painter(this);
609 const int buttonWidth = width();
610 const int buttonHeight = height();
611
612 QColor backgroundColor;
613 QColor foregroundColor;
614 if (m_hover) {
615 backgroundColor = KGlobalSettings::highlightColor();
616 foregroundColor = KGlobalSettings::highlightedTextColor();
617 }
618 else {
619 backgroundColor = palette().brush(QPalette::Background).color();
620 foregroundColor = KGlobalSettings::buttonTextColor();
621 }
622
623 // draw button background
624 painter.setPen(Qt::NoPen);
625 painter.setBrush(backgroundColor);
626 painter.drawRect(0, 0, buttonWidth, buttonHeight);
627
628 const int spacing = KDialog::spacingHint();
629
630 // draw icon
631 int x = spacing;
632 const int y = (buttonHeight - K3Icon::SizeSmall) / 2;
633 const QIcon &set = icon();
634 if (!set.isNull()) {
635 painter.drawPixmap(x, y, set.pixmap(QIcon::Small, QIcon::Normal));
636 }
637 x += K3Icon::SizeSmall + spacing;
638
639 // draw text
640 painter.setPen(foregroundColor);
641
642 const int textWidth = buttonWidth - x;
643 QFontMetrics fontMetrics(font());
644 const bool clipped = fontMetrics.width(text()) >= textWidth;
645 //const int align = clipped ? Qt::AlignVCenter : Qt::AlignCenter;
646 painter.drawText(QRect(x, 0, textWidth, buttonHeight), Qt::AlignVCenter, text());
647
648 if (clipped) {
649 // Blend the right area of the text with the background, as the
650 // text is clipped.
651 // TODO #1: use alpha blending in Qt4 instead of drawing the text that often
652 // TODO #2: same code as in UrlNavigatorButton::drawButton() -> provide helper class?
653 const int blendSteps = 16;
654
655 QColor blendColor(backgroundColor);
656 const int redInc = (foregroundColor.red() - backgroundColor.red()) / blendSteps;
657 const int greenInc = (foregroundColor.green() - backgroundColor.green()) / blendSteps;
658 const int blueInc = (foregroundColor.blue() - backgroundColor.blue()) / blendSteps;
659 for (int i = 0; i < blendSteps; ++i) {
660 painter.setClipRect(QRect(x + textWidth - i, 0, 1, buttonHeight));
661 painter.setPen(blendColor);
662 painter.drawText(QRect(x, 0, textWidth, buttonHeight), Qt::AlignVCenter, text());
663
664 blendColor.setRgb(blendColor.red() + redInc,
665 blendColor.green() + greenInc,
666 blendColor.blue() + blueInc);
667 }
668 }
669 }
670
671 void ServiceButton::enterEvent(QEvent* event)
672 {
673 QPushButton::enterEvent(event);
674 m_hover = true;
675 update();
676 }
677
678 void ServiceButton::leaveEvent(QEvent* event)
679 {
680 QPushButton::leaveEvent(event);
681 m_hover = false;
682 update();
683 }
684
685 void ServiceButton::slotReleased()
686 {
687 emit requestServiceStart(m_index);
688 }
689
690 #include "infosidebarpage.moc"