1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at> *
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. *
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 General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18 ***************************************************************************/
20 #include "infosidebarpage.h"
27 #include <qpushbutton.h>
29 #include <q3vgroupbox.h>
30 #include <q3popupmenu.h>
32 #include <qfontmetrics.h>
34 #include <q3hgroupbox.h>
36 #include <Q3ValueList>
38 #include <Q3VBoxLayout>
40 #include <kbookmarkmanager.h>
42 #include <kstandarddirs.h>
43 #include <kio/previewjob.h>
44 #include <kfileitem.h>
46 #include <kglobalsettings.h>
47 #include <kfilemetainfo.h>
50 #include "dolphinmainwindow.h"
51 #include "pixmapviewer.h"
52 #include "dolphinsettings.h"
54 InfoSidebarPage::InfoSidebarPage(DolphinMainWindow
* mainWindow
, QWidget
* parent
) :
55 SidebarPage(mainWindow
, parent
),
56 m_multipleSelection(false),
57 m_pendingPreview(false),
65 const int spacing
= KDialog::spacingHint();
67 m_timer
= new QTimer(this);
68 connect(m_timer
, SIGNAL(timeout()),
69 this, SLOT(slotTimeout()));
71 Q3VBoxLayout
* layout
= new Q3VBoxLayout(this);
72 layout
->setSpacing(spacing
);
75 m_preview
= new PixmapViewer(this);
76 m_preview
->setMinimumWidth(K3Icon::SizeEnormous
);
77 m_preview
->setFixedHeight(K3Icon::SizeEnormous
);
80 m_name
= new QLabel(this);
81 m_name
->setTextFormat(Qt::RichText
);
82 m_name
->setAlignment(m_name
->alignment() | Qt::AlignHCenter
);
83 QFontMetrics
fontMetrics(m_name
->font());
84 m_name
->setMinimumHeight(fontMetrics
.height() * 3);
85 m_name
->setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::Maximum
);
87 QWidget
* sep1
= new Q3HGroupBox(this); // TODO: check whether default widget exist for this?
88 sep1
->setFixedHeight(1);
90 // general information
91 m_infoGrid
= new Q3Grid(2, this);
92 m_infoGrid
->setSizePolicy(QSizePolicy::Preferred
, QSizePolicy::Fixed
);
94 QWidget
* sep2
= new Q3HGroupBox(this); // TODO: check whether default widget exist for this?
95 sep2
->setFixedHeight(1);
98 m_actionBox
= new KVBox(this);
99 m_actionBox
->setSizePolicy(QSizePolicy::Preferred
, QSizePolicy::Fixed
);
101 // Add a dummy widget with no restriction regarding a vertical resizing.
102 // This assures that information is always top aligned.
103 QWidget
* dummy
= new QWidget(this);
105 layout
->addItem(new QSpacerItem(spacing
, spacing
, QSizePolicy::Preferred
, QSizePolicy::Fixed
));
106 layout
->addWidget(m_preview
);
107 layout
->addWidget(m_name
);
108 layout
->addWidget(sep1
);
109 layout
->addWidget(m_infoGrid
);
110 layout
->addWidget(sep2
);
111 layout
->addWidget(m_actionBox
);
112 layout
->addWidget(dummy
);
114 connect(mainWindow
, SIGNAL(selectionChanged()),
115 this, SLOT(showItemInfo()));
117 connectToActiveView();
120 InfoSidebarPage::~InfoSidebarPage()
124 void InfoSidebarPage::activeViewChanged()
126 connectToActiveView();
129 void InfoSidebarPage::requestDelayedItemInfo(const KUrl
& url
)
133 if (!url
.isEmpty() && !m_multipleSelection
) {
134 m_urlCandidate
= url
;
135 m_timer
->start(300, true);
139 void InfoSidebarPage::requestItemInfo(const KUrl
& url
)
143 if (!url
.isEmpty() && !m_multipleSelection
) {
149 void InfoSidebarPage::showItemInfo()
153 m_multipleSelection
= false;
155 // show the preview...
156 DolphinView
* view
= mainWindow()->activeView();
157 const KFileItemList
* selectedItems
= view
->selectedItems();
158 if ((selectedItems
!= 0) && selectedItems
->count() > 1) {
159 m_multipleSelection
= true;
162 if (m_multipleSelection
) {
163 KIconLoader iconLoader
;
164 QPixmap icon
= iconLoader
.loadIcon("exec",
166 K3Icon::SizeEnormous
);
167 m_preview
->setPixmap(icon
);
168 m_name
->setText(i18n("%1 items selected",selectedItems
->count()));
170 else if (!applyBookmark()) {
171 // try to get a preview pixmap from the item...
173 list
.append(m_shownUrl
);
175 m_pendingPreview
= true;
176 m_preview
->setPixmap(QPixmap());
178 KIO::PreviewJob
* job
= KIO::filePreview(list
,
180 K3Icon::SizeEnormous
);
181 connect(job
, SIGNAL(gotPreview(const KFileItem
*, const QPixmap
&)),
182 this, SLOT(gotPreview(const KFileItem
*, const QPixmap
&)));
183 connect(job
, SIGNAL(failed(const KFileItem
*)),
184 this, SLOT(slotPreviewFailed(const KFileItem
*)));
187 text
.append(m_shownUrl
.fileName());
189 m_name
->setText(text
);
196 void InfoSidebarPage::slotTimeout()
198 m_shownUrl
= m_urlCandidate
;
202 void InfoSidebarPage::slotPreviewFailed(const KFileItem
* item
)
204 m_pendingPreview
= false;
205 if (!applyBookmark()) {
206 m_preview
->setPixmap(item
->pixmap(K3Icon::SizeEnormous
));
210 void InfoSidebarPage::gotPreview(const KFileItem
* /* item */,
211 const QPixmap
& pixmap
)
213 if (m_pendingPreview
) {
214 m_preview
->setPixmap(pixmap
);
215 m_pendingPreview
= false;
219 void InfoSidebarPage::startService(int index
)
221 DolphinView
* view
= mainWindow()->activeView();
222 if (view
->hasSelection()) {
223 KUrl::List selectedUrls
= view
->selectedUrls();
224 KDEDesktopMimeType::executeService(selectedUrls
, m_actionsVector
[index
]);
227 KDEDesktopMimeType::executeService(m_shownUrl
, m_actionsVector
[index
]);
231 void InfoSidebarPage::connectToActiveView()
235 DolphinView
* view
= mainWindow()->activeView();
236 connect(view
, SIGNAL(signalRequestItemInfo(const KUrl
&)),
237 this, SLOT(requestDelayedItemInfo(const KUrl
&)));
238 connect(view
, SIGNAL(signalUrlChanged(const KUrl
&)),
239 this, SLOT(requestItemInfo(const KUrl
&)));
241 m_shownUrl
= view
->url();
245 bool InfoSidebarPage::applyBookmark()
247 KBookmarkGroup root
= DolphinSettings::instance().bookmarkManager()->root();
248 KBookmark bookmark
= root
.first();
249 while (!bookmark
.isNull()) {
250 if (m_shownUrl
.equals(bookmark
.url(), KUrl::CompareWithoutTrailingSlash
)) {
252 text
.append(bookmark
.text());
254 m_name
->setText(text
);
256 KIconLoader iconLoader
;
257 QPixmap icon
= iconLoader
.loadIcon(bookmark
.icon(),
259 K3Icon::SizeEnormous
);
260 m_preview
->setPixmap(icon
);
263 bookmark
= root
.next(bookmark
);
269 void InfoSidebarPage::cancelRequest()
272 m_pendingPreview
= false;
275 void InfoSidebarPage::createMetaInfo()
277 // To prevent a flickering it's important to reuse available
278 // labels instead of deleting them and recreate them afterwards.
279 // The methods beginInfoLines(), addInfoLine() and endInfoLines()
280 // take care of this.
282 DolphinView
* view
= mainWindow()->activeView();
283 if (!view
->hasSelection()) {
284 KFileItem
fileItem(S_IFDIR
, KFileItem::Unknown
, m_shownUrl
);
287 if (fileItem
.isDir()) {
288 addInfoLine(i18n("Type:"), i18n("Directory"));
291 addInfoLine(i18n("Type:"), fileItem
.mimeComment());
293 QString
sizeText(KIO::convertSize(fileItem
.size()));
294 addInfoLine(i18n("Size:"), sizeText
);
295 addInfoLine(i18n("Modified:"), fileItem
.timeString());
297 const KFileMetaInfo
& metaInfo
= fileItem
.metaInfo();
298 if (metaInfo
.isValid()) {
299 QStringList keys
= metaInfo
.supportedKeys();
300 for (QStringList::Iterator it
= keys
.begin(); it
!= keys
.end(); ++it
) {
301 if (showMetaInfo(*it
)) {
302 KFileMetaInfoItem metaInfoItem
= metaInfo
.item(*it
);
303 addInfoLine(*it
, metaInfoItem
.string());
312 void InfoSidebarPage::beginInfoLines()
314 m_currInfoLineIdx
= 0;
317 void InfoSidebarPage::endInfoLines()
319 if (m_currInfoLineIdx
<= 0) {
323 // remove labels which have not been used
324 if (m_currInfoLineIdx
< static_cast<int>(m_infoWidgets
.count())) {
325 Q3PtrListIterator
<QLabel
> deleteIter(m_infoWidgets
);
326 deleteIter
+= m_currInfoLineIdx
;
330 while ((widget
= deleteIter
.current()) != 0) {
332 widget
->deleteLater();
336 for (int i
= 0; i
< removeCount
; ++i
) {
337 m_infoWidgets
.removeLast();
342 bool InfoSidebarPage::showMetaInfo(const QString
& key
) const
344 // sorted list of keys, where it's data should be shown
345 static const char* keys
[] = {
360 // do a binary search for the key...
362 int bottom
= sizeof(keys
) / sizeof(char*) - 1;
363 while (top
< bottom
) {
364 const int middle
= (top
+ bottom
) / 2;
365 const int result
= key
.compare(keys
[middle
]);
369 else if (result
> 0) {
380 void InfoSidebarPage::addInfoLine(const QString
& labelText
, const QString
& infoText
)
382 QString
labelStr("<b>");
383 labelStr
.append(labelText
);
384 labelStr
.append("</b> ");
386 const int count
= m_infoWidgets
.count();
387 if (m_currInfoLineIdx
< count
- 1) {
388 // reuse available labels
389 m_infoWidgets
.at(m_currInfoLineIdx
++)->setText(labelStr
);
390 m_infoWidgets
.at(m_currInfoLineIdx
++)->setText(infoText
);
393 // no labels are available anymore, hence create 2 new ones
394 QLabel
* label
= new QLabel(labelStr
, m_infoGrid
);
395 label
->setTextFormat(Qt::RichText
);
396 label
->setAlignment(Qt::AlignRight
|
399 m_infoWidgets
.append(label
);
401 QLabel
* info
= new QLabel(infoText
, m_infoGrid
);
402 info
->setTextFormat(Qt::RichText
);
403 info
->setAlignment(Qt::AlignTop
| Qt::TextWordWrap
);
405 m_infoWidgets
.append(info
);
407 m_currInfoLineIdx
+= 2;
411 void InfoSidebarPage::insertActions()
413 // delete all existing action widgets
414 // TODO: just use children() from QObject...
415 Q3PtrListIterator
<QWidget
> deleteIter(m_actionWidgets
);
417 while ((widget
= deleteIter
.current()) != 0) {
419 widget
->deleteLater();
423 m_actionWidgets
.clear();
424 m_actionsVector
.clear();
426 int actionsIndex
= 0;
428 // The algorithm for searching the available actions works on a list
429 // of KFileItems. If no selection is given, a temporary KFileItem
430 // by the given Url 'url' is created and added to the list.
431 KFileItem
fileItem(S_IFDIR
, KFileItem::Unknown
, m_shownUrl
);
432 KFileItemList localList
;
433 const KFileItemList
* itemList
= mainWindow()->activeView()->selectedItems();
434 if ((itemList
== 0) || itemList
->isEmpty()) {
436 localList
.append(&fileItem
);
437 itemList
= &localList
;
440 // 'itemList' contains now all KFileItems, where an item information should be shown.
441 // TODO: the following algorithm is quite equal to DolphinContextMenu::insertActionItems().
442 // It's open yet whether they should be merged or whether they have to work slightly different.
443 QStringList dirs
= KGlobal::dirs()->findDirs("data", "dolphin/servicemenus/");
444 for (QStringList::ConstIterator dirIt
= dirs
.begin(); dirIt
!= dirs
.end(); ++dirIt
) {
446 QStringList entries
= dir
.entryList("*.desktop", QDir::Files
);
448 for (QStringList::ConstIterator entryIt
= entries
.begin(); entryIt
!= entries
.end(); ++entryIt
) {
449 KSimpleConfig
cfg(*dirIt
+ *entryIt
, true);
450 cfg
.setDesktopGroup();
451 if ((cfg
.hasKey("Actions") || cfg
.hasKey("X-KDE-GetActionMenu")) && cfg
.hasKey("ServiceTypes")) {
452 const QStringList types
= cfg
.readListEntry("ServiceTypes");
453 for (QStringList::ConstIterator it
= types
.begin(); it
!= types
.end(); ++it
) {
454 // check whether the mime type is equal or whether the
455 // mimegroup (e. g. image/*) is supported
458 if ((*it
) == "all/allfiles") {
459 // The service type is valid for all files, but not for directories.
460 // Check whether the selected items only consist of files...
461 QListIterator
<KFileItem
*> mimeIt(*itemList
);
463 while (insert
&& mimeIt
.hasNext()) {
464 KFileItem
* item
= mimeIt
.next();
465 insert
= !item
->isDir();
470 // Check whether the MIME types of all selected files match
471 // to the mimetype of the service action. As soon as one MIME
472 // type does not match, no service menu is shown at all.
473 QListIterator
<KFileItem
*> mimeIt(*itemList
);
475 while (insert
&& mimeIt
.hasNext()) {
476 KFileItem
* item
= mimeIt
.next();
477 const QString
mimeType(item
->mimetype());
478 const QString
mimeGroup(mimeType
.left(mimeType
.find('/')));
480 insert
= (*it
== mimeType
) ||
481 ((*it
).right(1) == "*") &&
482 ((*it
).left((*it
).find('/')) == mimeGroup
);
487 const QString submenuName
= cfg
.readEntry( "X-KDE-Submenu" );
488 Q3PopupMenu
* popup
= 0;
489 if (!submenuName
.isEmpty()) {
490 // create a sub menu containing all actions
491 popup
= new Q3PopupMenu();
492 connect(popup
, SIGNAL(activated(int)),
493 this, SLOT(startService(int)));
495 QPushButton
* button
= new QPushButton(submenuName
, m_actionBox
);
496 button
->setFlat(true);
497 button
->setPopup(popup
);
499 m_actionWidgets
.append(button
);
502 Q3ValueList
<KDEDesktopMimeType::Service
> userServices
=
503 KDEDesktopMimeType::userDefinedServices(*dirIt
+ *entryIt
, true);
505 // iterate through all actions and add them to a widget
506 Q3ValueList
<KDEDesktopMimeType::Service
>::Iterator serviceIt
;
507 for (serviceIt
= userServices
.begin(); serviceIt
!= userServices
.end(); ++serviceIt
) {
508 KDEDesktopMimeType::Service service
= (*serviceIt
);
510 ServiceButton
* button
= new ServiceButton(SmallIcon(service
.m_strIcon
),
514 connect(button
, SIGNAL(requestServiceStart(int)),
515 this, SLOT(startService(int)));
516 m_actionWidgets
.append(button
);
520 popup
->insertItem(SmallIcon(service
.m_strIcon
), service
.m_strName
, actionsIndex
);
523 m_actionsVector
.append(service
);
533 ServiceButton::ServiceButton(const QIcon
& icon
,
537 QPushButton(icon
, text
, parent
),
541 setEraseColor(colorGroup().background());
542 setFocusPolicy(Qt::NoFocus
);
543 connect(this, SIGNAL(released()),
544 this, SLOT(slotReleased()));
547 ServiceButton::~ServiceButton()
551 void ServiceButton::paintEvent(QPaintEvent
* event
)
553 QPainter
painter(this);
554 const int buttonWidth
= width();
555 const int buttonHeight
= height();
557 QColor backgroundColor
;
558 QColor foregroundColor
;
560 backgroundColor
= KGlobalSettings::highlightColor();
561 foregroundColor
= KGlobalSettings::highlightedTextColor();
564 backgroundColor
= colorGroup().background();
565 foregroundColor
= KGlobalSettings::buttonTextColor();
568 // draw button background
569 painter
.setPen(Qt::NoPen
);
570 painter
.setBrush(backgroundColor
);
571 painter
.drawRect(0, 0, buttonWidth
, buttonHeight
);
573 const int spacing
= KDialog::spacingHint();
577 const int y
= (buttonHeight
- K3Icon::SizeSmall
) / 2;
578 const QIcon
* set
= iconSet();
580 painter
.drawPixmap(x
, y
, set
->pixmap(QIcon::Small
, QIcon::Normal
));
582 x
+= K3Icon::SizeSmall
+ spacing
;
585 painter
.setPen(foregroundColor
);
587 const int textWidth
= buttonWidth
- x
;
588 QFontMetrics
fontMetrics(font());
589 const bool clipped
= fontMetrics
.width(text()) >= textWidth
;
590 //const int align = clipped ? Qt::AlignVCenter : Qt::AlignCenter;
591 painter
.drawText(QRect(x
, 0, textWidth
, buttonHeight
), Qt::AlignVCenter
, text());
594 // Blend the right area of the text with the background, as the
596 // TODO #1: use alpha blending in Qt4 instead of drawing the text that often
597 // TODO #2: same code as in UrlNavigatorButton::drawButton() -> provide helper class?
598 const int blendSteps
= 16;
600 QColor
blendColor(backgroundColor
);
601 const int redInc
= (foregroundColor
.red() - backgroundColor
.red()) / blendSteps
;
602 const int greenInc
= (foregroundColor
.green() - backgroundColor
.green()) / blendSteps
;
603 const int blueInc
= (foregroundColor
.blue() - backgroundColor
.blue()) / blendSteps
;
604 for (int i
= 0; i
< blendSteps
; ++i
) {
605 painter
.setClipRect(QRect(x
+ textWidth
- i
, 0, 1, buttonHeight
));
606 painter
.setPen(blendColor
);
607 painter
.drawText(QRect(x
, 0, textWidth
, buttonHeight
), Qt::AlignVCenter
, text());
609 blendColor
.setRgb(blendColor
.red() + redInc
,
610 blendColor
.green() + greenInc
,
611 blendColor
.blue() + blueInc
);
616 void ServiceButton::enterEvent(QEvent
* event
)
618 QPushButton::enterEvent(event
);
623 void ServiceButton::leaveEvent(QEvent
* event
)
625 QPushButton::leaveEvent(event
);
630 void ServiceButton::slotReleased()
632 emit
requestServiceStart(m_index
);
635 #include "infosidebarpage.moc"