]> cloud.milkyroute.net Git - dolphin.git/blob - src/infosidebarpage.cpp
Some basic fixes to stay synchronized between the tree view and the currently active...
[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 KUrl file;
182 if (selectedItems.count() > 1) {
183 m_multipleSelection = true;
184 } else if(selectedItems.count() == 0) {
185 file = m_shownUrl;
186 } else {
187 file = selectedItems[0]->url();
188 }
189 if (m_multipleSelection) {
190 KIconLoader iconLoader;
191 QPixmap icon = iconLoader.loadIcon("exec",
192 K3Icon::NoGroup,
193 K3Icon::SizeEnormous);
194 m_preview->setPixmap(icon);
195 m_name->setText(i18n("%1 items selected",selectedItems.count()));
196 }
197 else if (!applyBookmark(file)) {
198 // try to get a preview pixmap from the item...
199 KUrl::List list;
200 list.append(file);
201
202 m_pendingPreview = true;
203 m_preview->setPixmap(QPixmap());
204
205 KIO::PreviewJob* job = KIO::filePreview(list,
206 m_preview->width(),
207 K3Icon::SizeEnormous,
208 0,
209 0,
210 true,
211 false);
212 job->setIgnoreMaximumSize(true);
213
214 connect(job, SIGNAL(gotPreview(const KFileItem*, const QPixmap&)),
215 this, SLOT(gotPreview(const KFileItem*, const QPixmap&)));
216 connect(job, SIGNAL(failed(const KFileItem*)),
217 this, SLOT(slotPreviewFailed(const KFileItem*)));
218
219 QString text("<b>");
220 text.append(file.fileName());
221 text.append("</b>");
222 m_name->setText(text);
223 }
224
225 createMetaInfo();
226 insertActions();
227 }
228
229 void InfoSidebarPage::slotTimeout()
230 {
231 m_shownUrl = m_urlCandidate;
232 showItemInfo();
233 }
234
235 void InfoSidebarPage::slotPreviewFailed(const KFileItem* item)
236 {
237 m_pendingPreview = false;
238 if (!applyBookmark(item->url())) {
239 m_preview->setPixmap(item->pixmap(K3Icon::SizeEnormous));
240 }
241 }
242
243 void InfoSidebarPage::gotPreview(const KFileItem* /* item */,
244 const QPixmap& pixmap)
245 {
246 if (m_pendingPreview) {
247 m_preview->setPixmap(pixmap);
248 m_pendingPreview = false;
249 }
250 }
251
252 void InfoSidebarPage::startService(int index)
253 {
254 DolphinView* view = mainWindow()->activeView();
255 if (view->hasSelection()) {
256 KUrl::List selectedUrls = view->selectedUrls();
257 KDEDesktopMimeType::executeService(selectedUrls, m_actionsVector[index]);
258 }
259 else {
260 KDEDesktopMimeType::executeService(m_shownUrl, m_actionsVector[index]);
261 }
262 }
263
264 void InfoSidebarPage::connectToActiveView()
265 {
266 cancelRequest();
267
268 DolphinView* view = mainWindow()->activeView();
269 connect(view, SIGNAL(requestItemInfo(const KUrl&)),
270 this, SLOT(requestDelayedItemInfo(const KUrl&)));
271 connect(view, SIGNAL(urlChanged(const KUrl&)),
272 this, SLOT(requestItemInfo(const KUrl&)));
273
274 m_shownUrl = view->url();
275 showItemInfo();
276 }
277
278 bool InfoSidebarPage::applyBookmark(const KUrl& url)
279 {
280 KBookmarkGroup root = DolphinSettings::instance().bookmarkManager()->root();
281 KBookmark bookmark = root.first();
282 while (!bookmark.isNull()) {
283 if (url.equals(bookmark.url(), KUrl::CompareWithoutTrailingSlash)) {
284 QString text("<b>");
285 text.append(bookmark.text());
286 text.append("</b>");
287 m_name->setText(text);
288
289 KIconLoader iconLoader;
290 QPixmap icon = iconLoader.loadIcon(bookmark.icon(),
291 K3Icon::NoGroup,
292 K3Icon::SizeEnormous);
293 m_preview->setPixmap(icon);
294 return true;
295 }
296 bookmark = root.next(bookmark);
297 }
298
299 return false;
300 }
301
302 void InfoSidebarPage::cancelRequest()
303 {
304 m_timer->stop();
305 m_pendingPreview = false;
306 }
307
308 void InfoSidebarPage::createMetaInfo()
309 {
310 beginInfoLines();
311 DolphinView* view = mainWindow()->activeView();
312 if (!view->hasSelection()) {
313 KFileItem fileItem(S_IFDIR, KFileItem::Unknown, m_shownUrl);
314 fileItem.refresh();
315
316 if (fileItem.isDir()) {
317 addInfoLine(i18n("Type:"), i18n("Directory"));
318 }
319 showAnnotation(m_shownUrl);
320 }
321 else if (view->selectedItems().count() == 1) {
322 KFileItem* fileItem = view->selectedItems()[0];
323 addInfoLine(i18n("Type:"), fileItem->mimeComment());
324
325 QString sizeText(KIO::convertSize(fileItem->size()));
326 addInfoLine(i18n("Size:"), sizeText);
327 addInfoLine(i18n("Modified:"), fileItem->timeString());
328
329 const KFileMetaInfo& metaInfo = fileItem->metaInfo();
330 if (metaInfo.isValid()) {
331 QStringList keys = metaInfo.supportedKeys();
332 for (QStringList::Iterator it = keys.begin(); it != keys.end(); ++it) {
333 if (showMetaInfo(*it)) {
334 KFileMetaInfoItem metaInfoItem = metaInfo.item(*it);
335 addInfoLine(*it, metaInfoItem.string());
336 }
337 }
338 }
339 showAnnotation(fileItem->url());
340 }
341 else {
342 showAnnotations(view->selectedItems().urlList());
343 unsigned long int totSize = 0;
344 foreach(KFileItem* item, view->selectedItems()) {
345 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
346 }
347 addInfoLine(i18n("Total size:"), KIO::convertSize(totSize));
348 }
349 endInfoLines();
350 }
351
352 void InfoSidebarPage::beginInfoLines()
353 {
354 m_infoLines = QString("");
355 }
356
357 void InfoSidebarPage::endInfoLines()
358 {
359 m_infos->setText(m_infoLines);
360 }
361
362 bool InfoSidebarPage::showMetaInfo(const QString& key) const
363 {
364 // sorted list of keys, where it's data should be shown
365 static const char* keys[] = {
366 "Album",
367 "Artist",
368 "Author",
369 "Bitrate",
370 "Date",
371 "Dimensions",
372 "Genre",
373 "Length",
374 "Lines",
375 "Pages",
376 "Title",
377 "Words"
378 };
379
380 // do a binary search for the key...
381 int top = 0;
382 int bottom = sizeof(keys) / sizeof(char*) - 1;
383 while (top < bottom) {
384 const int middle = (top + bottom) / 2;
385 const int result = key.compare(keys[middle]);
386 if (result < 0) {
387 bottom = middle - 1;
388 }
389 else if (result > 0) {
390 top = middle + 1;
391 }
392 else {
393 return true;
394 }
395 }
396
397 return false;
398 }
399
400 void InfoSidebarPage::addInfoLine(const QString& labelText, const QString& infoText)
401 {
402 if (!m_infoLines.isEmpty())
403 m_infoLines += "<br/>";
404 m_infoLines += QString("<b>%1</b> %2").arg(labelText).arg(infoText);
405 }
406
407 void InfoSidebarPage::insertActions()
408 {
409 // delete all existing action widgets
410 // TODO: just use children() from QObject...
411 Q3PtrListIterator<QWidget> deleteIter(m_actionWidgets);
412 QWidget* widget = 0;
413 while ((widget = deleteIter.current()) != 0) {
414 widget->close();
415 widget->deleteLater();
416 ++deleteIter;
417 }
418
419 m_actionWidgets.clear();
420 m_actionsVector.clear();
421
422 int actionsIndex = 0;
423
424 // The algorithm for searching the available actions works on a list
425 // of KFileItems. If no selection is given, a temporary KFileItem
426 // by the given Url 'url' is created and added to the list.
427 KFileItem fileItem(S_IFDIR, KFileItem::Unknown, m_shownUrl);
428 KFileItemList itemList = mainWindow()->activeView()->selectedItems();
429 if (itemList.isEmpty()) {
430 fileItem.refresh();
431 itemList.append(&fileItem);
432 }
433
434 // 'itemList' contains now all KFileItems, where an item information should be shown.
435 // TODO: the following algorithm is quite equal to DolphinContextMenu::insertActionItems().
436 // It's open yet whether they should be merged or whether they have to work slightly different.
437 QStringList dirs = KGlobal::dirs()->findDirs("data", "dolphin/servicemenus/");
438 for (QStringList::ConstIterator dirIt = dirs.begin(); dirIt != dirs.end(); ++dirIt) {
439 QDir dir(*dirIt);
440 QStringList entries = dir.entryList(QStringList("*.desktop"), QDir::Files);
441
442 for (QStringList::ConstIterator entryIt = entries.begin(); entryIt != entries.end(); ++entryIt) {
443 KConfigGroup cfg(KSharedConfig::openConfig( *dirIt + *entryIt, KConfig::OnlyLocal ), "Desktop Entry" );
444 if ((cfg.hasKey("Actions") || cfg.hasKey("X-KDE-GetActionMenu")) && cfg.hasKey("ServiceTypes")) {
445 const QStringList types = cfg.readEntry("ServiceTypes", QStringList(), ',');
446 for (QStringList::ConstIterator it = types.begin(); it != types.end(); ++it) {
447 // check whether the mime type is equal or whether the
448 // mimegroup (e. g. image/*) is supported
449
450 bool insert = false;
451 if ((*it) == "all/allfiles") {
452 // The service type is valid for all files, but not for directories.
453 // Check whether the selected items only consist of files...
454 QListIterator<KFileItem*> mimeIt(itemList);
455 insert = true;
456 while (insert && mimeIt.hasNext()) {
457 KFileItem* item = mimeIt.next();
458 insert = !item->isDir();
459 }
460 }
461
462 if (!insert) {
463 // Check whether the MIME types of all selected files match
464 // to the mimetype of the service action. As soon as one MIME
465 // type does not match, no service menu is shown at all.
466 QListIterator<KFileItem*> mimeIt(itemList);
467 insert = true;
468 while (insert && mimeIt.hasNext()) {
469 KFileItem* item = mimeIt.next();
470 const QString mimeType(item->mimetype());
471 const QString mimeGroup(mimeType.left(mimeType.indexOf('/')));
472
473 insert = (*it == mimeType) ||
474 ((*it).right(1) == "*") &&
475 ((*it).left((*it).indexOf('/')) == mimeGroup);
476 }
477 }
478
479 if (insert) {
480 const QString submenuName = cfg.readEntry( "X-KDE-Submenu" );
481 Q3PopupMenu* popup = 0;
482 if (!submenuName.isEmpty()) {
483 // create a sub menu containing all actions
484 popup = new Q3PopupMenu();
485 connect(popup, SIGNAL(activated(int)),
486 this, SLOT(startService(int)));
487
488 QPushButton* button = new QPushButton(submenuName, m_actionBox);
489 button->setFlat(true);
490 button->setMenu(popup);
491 button->show();
492 m_actionWidgets.append(button);
493 }
494
495 Q3ValueList<KDEDesktopMimeType::Service> userServices =
496 KDEDesktopMimeType::userDefinedServices(*dirIt + *entryIt, true);
497
498 // iterate through all actions and add them to a widget
499 Q3ValueList<KDEDesktopMimeType::Service>::Iterator serviceIt;
500 for (serviceIt = userServices.begin(); serviceIt != userServices.end(); ++serviceIt) {
501 KDEDesktopMimeType::Service service = (*serviceIt);
502 if (popup == 0) {
503 ServiceButton* button = new ServiceButton(KIcon(service.m_strIcon),
504 service.m_strName,
505 m_actionBox,
506 actionsIndex);
507 connect(button, SIGNAL(requestServiceStart(int)),
508 this, SLOT(startService(int)));
509 m_actionWidgets.append(button);
510 button->show();
511 }
512 else {
513 popup->insertItem(KIcon(service.m_strIcon), service.m_strName, actionsIndex);
514 }
515
516 m_actionsVector.append(service);
517 ++actionsIndex;
518 }
519 }
520 }
521 }
522 }
523 }
524 }
525
526 void InfoSidebarPage::showAnnotation(const KUrl& file)
527 {
528 if(m_metadata->storageUp()) {
529 QString text = m_metadata->getAnnotation(file);
530 if (!text.isEmpty()) {
531 m_annotationLabel->show();
532 m_annotationLabel->setText(QString("<b>%1</b> :<br/>%2").arg(i18n("Annotation")).arg(text));
533 m_annotationButton->setText(i18n("Change annotation"));
534 } else {
535 m_annotationLabel->hide();
536 m_annotationButton->setText(i18n("Annotate file"));
537 }
538 }
539 }
540
541 void InfoSidebarPage::showAnnotations(const KUrl::List& files)
542 {
543 static unsigned int maxShownAnnot = 3; //The maximum number of show annotations when selecting multiple files
544 if (m_metadata->storageUp()) {
545 bool hasAnnotation = false;
546 unsigned int annotateNum = 0;
547 QString firsts = QString("<b>%1 :</b><br/>").arg(i18n("Annotations"));
548 foreach (KUrl file, files) {
549 QString annotation = m_metadata->getAnnotation(file);
550 if (!annotation.isEmpty()) {
551 hasAnnotation = true;
552 if(annotateNum < maxShownAnnot) {
553 firsts += m_annotationLabel->fontMetrics().elidedText(QString("<b>%1</b> : %2<br/>").arg(file.fileName()).arg(annotation), Qt::ElideRight, width());//FIXME not really the good method, does not handle resizing ...
554 annotateNum++;
555 }
556 }
557 }
558 if (hasAnnotation) {
559 m_annotationLabel->show();
560 m_annotationLabel->setText(firsts);
561 } else m_annotationLabel->hide();
562 m_annotationButton->setText(hasAnnotation ? i18n("Change annotations") : i18n("Annotate files"));
563 }
564 }
565
566 void InfoSidebarPage::changeAnnotation()
567 {
568 bool ok = false;
569 KUrl::List files(mainWindow()->activeView()->selectedItems().urlList());
570 QString name, old;
571 if (files.isEmpty()) {
572 files << m_shownUrl;
573 }
574 else if (files.count() == 1) {
575 name = files[0].url();
576 old = m_metadata->getAnnotation(files[0]);
577 }
578 else {
579 name = QString("%1 files").arg(files.count());
580 old = QString();
581 }
582 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
583 if(ok) {
584 foreach(KUrl file, files) {
585 m_metadata->setAnnotation(file, text);
586 }
587 showAnnotation(files[0]);
588 }
589 }
590
591 ServiceButton::ServiceButton(const QIcon& icon,
592 const QString& text,
593 QWidget* parent,
594 int index) :
595 QPushButton(icon, text, parent),
596 m_hover(false),
597 m_index(index)
598 {
599 setEraseColor(palette().brush(QPalette::Background).color());
600 setFocusPolicy(Qt::NoFocus);
601 connect(this, SIGNAL(released()),
602 this, SLOT(slotReleased()));
603 }
604
605 ServiceButton::~ServiceButton()
606 {
607 }
608
609 void ServiceButton::paintEvent(QPaintEvent* event)
610 {
611 QPainter painter(this);
612 const int buttonWidth = width();
613 const int buttonHeight = height();
614
615 QColor backgroundColor;
616 QColor foregroundColor;
617 if (m_hover) {
618 backgroundColor = KGlobalSettings::highlightColor();
619 foregroundColor = KGlobalSettings::highlightedTextColor();
620 }
621 else {
622 backgroundColor = palette().brush(QPalette::Background).color();
623 foregroundColor = KGlobalSettings::buttonTextColor();
624 }
625
626 // draw button background
627 painter.setPen(Qt::NoPen);
628 painter.setBrush(backgroundColor);
629 painter.drawRect(0, 0, buttonWidth, buttonHeight);
630
631 const int spacing = KDialog::spacingHint();
632
633 // draw icon
634 int x = spacing;
635 const int y = (buttonHeight - K3Icon::SizeSmall) / 2;
636 const QIcon &set = icon();
637 if (!set.isNull()) {
638 painter.drawPixmap(x, y, set.pixmap(QIcon::Small, QIcon::Normal));
639 }
640 x += K3Icon::SizeSmall + spacing;
641
642 // draw text
643 painter.setPen(foregroundColor);
644
645 const int textWidth = buttonWidth - x;
646 QFontMetrics fontMetrics(font());
647 const bool clipped = fontMetrics.width(text()) >= textWidth;
648 //const int align = clipped ? Qt::AlignVCenter : Qt::AlignCenter;
649 painter.drawText(QRect(x, 0, textWidth, buttonHeight), Qt::AlignVCenter, text());
650
651 if (clipped) {
652 // Blend the right area of the text with the background, as the
653 // text is clipped.
654 // TODO #1: use alpha blending in Qt4 instead of drawing the text that often
655 // TODO #2: same code as in UrlNavigatorButton::drawButton() -> provide helper class?
656 const int blendSteps = 16;
657
658 QColor blendColor(backgroundColor);
659 const int redInc = (foregroundColor.red() - backgroundColor.red()) / blendSteps;
660 const int greenInc = (foregroundColor.green() - backgroundColor.green()) / blendSteps;
661 const int blueInc = (foregroundColor.blue() - backgroundColor.blue()) / blendSteps;
662 for (int i = 0; i < blendSteps; ++i) {
663 painter.setClipRect(QRect(x + textWidth - i, 0, 1, buttonHeight));
664 painter.setPen(blendColor);
665 painter.drawText(QRect(x, 0, textWidth, buttonHeight), Qt::AlignVCenter, text());
666
667 blendColor.setRgb(blendColor.red() + redInc,
668 blendColor.green() + greenInc,
669 blendColor.blue() + blueInc);
670 }
671 }
672 }
673
674 void ServiceButton::enterEvent(QEvent* event)
675 {
676 QPushButton::enterEvent(event);
677 m_hover = true;
678 update();
679 }
680
681 void ServiceButton::leaveEvent(QEvent* event)
682 {
683 QPushButton::leaveEvent(event);
684 m_hover = false;
685 update();
686 }
687
688 void ServiceButton::slotReleased()
689 {
690 emit requestServiceStart(m_index);
691 }
692
693 #include "infosidebarpage.moc"