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 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
->setSingleShot(true);
140 void InfoSidebarPage::requestItemInfo(const KUrl
& url
)
144 if (!url
.isEmpty() && !m_multipleSelection
) {
150 void InfoSidebarPage::showItemInfo()
154 m_multipleSelection
= false;
156 // show the preview...
157 DolphinView
* view
= mainWindow()->activeView();
158 const KFileItemList selectedItems
= view
->selectedItems();
159 if (selectedItems
.count() > 1) {
160 m_multipleSelection
= true;
163 if (m_multipleSelection
) {
164 KIconLoader iconLoader
;
165 QPixmap icon
= iconLoader
.loadIcon("exec",
167 K3Icon::SizeEnormous
);
168 m_preview
->setPixmap(icon
);
169 m_name
->setText(i18n("%1 items selected",selectedItems
.count()));
171 else if (!applyBookmark()) {
172 // try to get a preview pixmap from the item...
174 list
.append(m_shownUrl
);
176 m_pendingPreview
= true;
177 m_preview
->setPixmap(QPixmap());
179 KIO::PreviewJob
* job
= KIO::filePreview(list
,
181 K3Icon::SizeEnormous
,
186 job
->setIgnoreMaximumSize(true);
188 connect(job
, SIGNAL(gotPreview(const KFileItem
*, const QPixmap
&)),
189 this, SLOT(gotPreview(const KFileItem
*, const QPixmap
&)));
190 connect(job
, SIGNAL(failed(const KFileItem
*)),
191 this, SLOT(slotPreviewFailed(const KFileItem
*)));
194 text
.append(m_shownUrl
.fileName());
196 m_name
->setText(text
);
203 void InfoSidebarPage::slotTimeout()
205 m_shownUrl
= m_urlCandidate
;
209 void InfoSidebarPage::slotPreviewFailed(const KFileItem
* item
)
211 m_pendingPreview
= false;
212 if (!applyBookmark()) {
213 m_preview
->setPixmap(item
->pixmap(K3Icon::SizeEnormous
));
217 void InfoSidebarPage::gotPreview(const KFileItem
* /* item */,
218 const QPixmap
& pixmap
)
220 if (m_pendingPreview
) {
221 m_preview
->setPixmap(pixmap
);
222 m_pendingPreview
= false;
226 void InfoSidebarPage::startService(int index
)
228 DolphinView
* view
= mainWindow()->activeView();
229 if (view
->hasSelection()) {
230 KUrl::List selectedUrls
= view
->selectedUrls();
231 KDEDesktopMimeType::executeService(selectedUrls
, m_actionsVector
[index
]);
234 KDEDesktopMimeType::executeService(m_shownUrl
, m_actionsVector
[index
]);
238 void InfoSidebarPage::connectToActiveView()
242 DolphinView
* view
= mainWindow()->activeView();
243 connect(view
, SIGNAL(requestItemInfo(const KUrl
&)),
244 this, SLOT(requestDelayedItemInfo(const KUrl
&)));
245 connect(view
, SIGNAL(urlChanged(const KUrl
&)),
246 this, SLOT(requestItemInfo(const KUrl
&)));
248 m_shownUrl
= view
->url();
252 bool InfoSidebarPage::applyBookmark()
254 KBookmarkGroup root
= DolphinSettings::instance().bookmarkManager()->root();
255 KBookmark bookmark
= root
.first();
256 while (!bookmark
.isNull()) {
257 if (m_shownUrl
.equals(bookmark
.url(), KUrl::CompareWithoutTrailingSlash
)) {
259 text
.append(bookmark
.text());
261 m_name
->setText(text
);
263 KIconLoader iconLoader
;
264 QPixmap icon
= iconLoader
.loadIcon(bookmark
.icon(),
266 K3Icon::SizeEnormous
);
267 m_preview
->setPixmap(icon
);
270 bookmark
= root
.next(bookmark
);
276 void InfoSidebarPage::cancelRequest()
279 m_pendingPreview
= false;
282 void InfoSidebarPage::createMetaInfo()
284 // To prevent a flickering it's important to reuse available
285 // labels instead of deleting them and recreate them afterwards.
286 // The methods beginInfoLines(), addInfoLine() and endInfoLines()
287 // take care of this.
289 DolphinView
* view
= mainWindow()->activeView();
290 if (!view
->hasSelection()) {
291 KFileItem
fileItem(S_IFDIR
, KFileItem::Unknown
, m_shownUrl
);
294 if (fileItem
.isDir()) {
295 addInfoLine(i18n("Type:"), i18n("Directory"));
298 addInfoLine(i18n("Type:"), fileItem
.mimeComment());
300 QString
sizeText(KIO::convertSize(fileItem
.size()));
301 addInfoLine(i18n("Size:"), sizeText
);
302 addInfoLine(i18n("Modified:"), fileItem
.timeString());
304 const KFileMetaInfo
& metaInfo
= fileItem
.metaInfo();
305 if (metaInfo
.isValid()) {
306 QStringList keys
= metaInfo
.supportedKeys();
307 for (QStringList::Iterator it
= keys
.begin(); it
!= keys
.end(); ++it
) {
308 if (showMetaInfo(*it
)) {
309 KFileMetaInfoItem metaInfoItem
= metaInfo
.item(*it
);
310 addInfoLine(*it
, metaInfoItem
.string());
319 void InfoSidebarPage::beginInfoLines()
321 m_currInfoLineIdx
= 0;
324 void InfoSidebarPage::endInfoLines()
326 if (m_currInfoLineIdx
<= 0) {
330 // remove labels which have not been used
331 if (m_currInfoLineIdx
< static_cast<int>(m_infoWidgets
.count())) {
332 Q3PtrListIterator
<QLabel
> deleteIter(m_infoWidgets
);
333 deleteIter
+= m_currInfoLineIdx
;
337 while ((widget
= deleteIter
.current()) != 0) {
339 widget
->deleteLater();
343 for (int i
= 0; i
< removeCount
; ++i
) {
344 m_infoWidgets
.removeLast();
349 bool InfoSidebarPage::showMetaInfo(const QString
& key
) const
351 // sorted list of keys, where it's data should be shown
352 static const char* keys
[] = {
367 // do a binary search for the key...
369 int bottom
= sizeof(keys
) / sizeof(char*) - 1;
370 while (top
< bottom
) {
371 const int middle
= (top
+ bottom
) / 2;
372 const int result
= key
.compare(keys
[middle
]);
376 else if (result
> 0) {
387 void InfoSidebarPage::addInfoLine(const QString
& labelText
, const QString
& infoText
)
389 QString
labelStr("<b>");
390 labelStr
.append(labelText
);
391 labelStr
.append("</b> ");
393 const int count
= m_infoWidgets
.count();
394 if (m_currInfoLineIdx
< count
- 1) {
395 // reuse available labels
396 m_infoWidgets
.at(m_currInfoLineIdx
++)->setText(labelStr
);
397 m_infoWidgets
.at(m_currInfoLineIdx
++)->setText(infoText
);
400 // no labels are available anymore, hence create 2 new ones
401 QLabel
* label
= new QLabel(labelStr
, m_infoGrid
);
402 label
->setTextFormat(Qt::RichText
);
403 label
->setAlignment(Qt::AlignRight
|
406 m_infoWidgets
.append(label
);
408 QLabel
* info
= new QLabel(infoText
, m_infoGrid
);
409 info
->setTextFormat(Qt::RichText
);
410 info
->setAlignment(Qt::AlignTop
);
411 info
->setWordWrap(true);
413 m_infoWidgets
.append(info
);
415 m_currInfoLineIdx
+= 2;
419 void InfoSidebarPage::insertActions()
421 // delete all existing action widgets
422 // TODO: just use children() from QObject...
423 Q3PtrListIterator
<QWidget
> deleteIter(m_actionWidgets
);
425 while ((widget
= deleteIter
.current()) != 0) {
427 widget
->deleteLater();
431 m_actionWidgets
.clear();
432 m_actionsVector
.clear();
434 int actionsIndex
= 0;
436 // The algorithm for searching the available actions works on a list
437 // of KFileItems. If no selection is given, a temporary KFileItem
438 // by the given Url 'url' is created and added to the list.
439 KFileItem
fileItem(S_IFDIR
, KFileItem::Unknown
, m_shownUrl
);
440 KFileItemList itemList
= mainWindow()->activeView()->selectedItems();
441 if (itemList
.isEmpty()) {
443 itemList
.append(&fileItem
);
446 // 'itemList' contains now all KFileItems, where an item information should be shown.
447 // TODO: the following algorithm is quite equal to DolphinContextMenu::insertActionItems().
448 // It's open yet whether they should be merged or whether they have to work slightly different.
449 QStringList dirs
= KGlobal::dirs()->findDirs("data", "dolphin/servicemenus/");
450 for (QStringList::ConstIterator dirIt
= dirs
.begin(); dirIt
!= dirs
.end(); ++dirIt
) {
452 QStringList entries
= dir
.entryList(QStringList("*.desktop"), QDir::Files
);
454 for (QStringList::ConstIterator entryIt
= entries
.begin(); entryIt
!= entries
.end(); ++entryIt
) {
455 KConfigGroup
cfg(KSharedConfig::openConfig( *dirIt
+ *entryIt
, KConfig::OnlyLocal
), "Desktop Entry" );
456 if ((cfg
.hasKey("Actions") || cfg
.hasKey("X-KDE-GetActionMenu")) && cfg
.hasKey("ServiceTypes")) {
457 const QStringList types
= cfg
.readEntry("ServiceTypes", QStringList(), ',');
458 for (QStringList::ConstIterator it
= types
.begin(); it
!= types
.end(); ++it
) {
459 // check whether the mime type is equal or whether the
460 // mimegroup (e. g. image/*) is supported
463 if ((*it
) == "all/allfiles") {
464 // The service type is valid for all files, but not for directories.
465 // Check whether the selected items only consist of files...
466 QListIterator
<KFileItem
*> mimeIt(itemList
);
468 while (insert
&& mimeIt
.hasNext()) {
469 KFileItem
* item
= mimeIt
.next();
470 insert
= !item
->isDir();
475 // Check whether the MIME types of all selected files match
476 // to the mimetype of the service action. As soon as one MIME
477 // type does not match, no service menu is shown at all.
478 QListIterator
<KFileItem
*> mimeIt(itemList
);
480 while (insert
&& mimeIt
.hasNext()) {
481 KFileItem
* item
= mimeIt
.next();
482 const QString
mimeType(item
->mimetype());
483 const QString
mimeGroup(mimeType
.left(mimeType
.indexOf('/')));
485 insert
= (*it
== mimeType
) ||
486 ((*it
).right(1) == "*") &&
487 ((*it
).left((*it
).indexOf('/')) == mimeGroup
);
492 const QString submenuName
= cfg
.readEntry( "X-KDE-Submenu" );
493 Q3PopupMenu
* popup
= 0;
494 if (!submenuName
.isEmpty()) {
495 // create a sub menu containing all actions
496 popup
= new Q3PopupMenu();
497 connect(popup
, SIGNAL(activated(int)),
498 this, SLOT(startService(int)));
500 QPushButton
* button
= new QPushButton(submenuName
, m_actionBox
);
501 button
->setFlat(true);
502 button
->setMenu(popup
);
504 m_actionWidgets
.append(button
);
507 Q3ValueList
<KDEDesktopMimeType::Service
> userServices
=
508 KDEDesktopMimeType::userDefinedServices(*dirIt
+ *entryIt
, true);
510 // iterate through all actions and add them to a widget
511 Q3ValueList
<KDEDesktopMimeType::Service
>::Iterator serviceIt
;
512 for (serviceIt
= userServices
.begin(); serviceIt
!= userServices
.end(); ++serviceIt
) {
513 KDEDesktopMimeType::Service service
= (*serviceIt
);
515 ServiceButton
* button
= new ServiceButton(KIcon(service
.m_strIcon
),
519 connect(button
, SIGNAL(requestServiceStart(int)),
520 this, SLOT(startService(int)));
521 m_actionWidgets
.append(button
);
525 popup
->insertItem(KIcon(service
.m_strIcon
), service
.m_strName
, actionsIndex
);
528 m_actionsVector
.append(service
);
538 ServiceButton::ServiceButton(const QIcon
& icon
,
542 QPushButton(icon
, text
, parent
),
546 setEraseColor(palette().brush(QPalette::Background
).color());
547 setFocusPolicy(Qt::NoFocus
);
548 connect(this, SIGNAL(released()),
549 this, SLOT(slotReleased()));
552 ServiceButton::~ServiceButton()
556 void ServiceButton::paintEvent(QPaintEvent
* event
)
558 QPainter
painter(this);
559 const int buttonWidth
= width();
560 const int buttonHeight
= height();
562 QColor backgroundColor
;
563 QColor foregroundColor
;
565 backgroundColor
= KGlobalSettings::highlightColor();
566 foregroundColor
= KGlobalSettings::highlightedTextColor();
569 backgroundColor
= palette().brush(QPalette::Background
).color();
570 foregroundColor
= KGlobalSettings::buttonTextColor();
573 // draw button background
574 painter
.setPen(Qt::NoPen
);
575 painter
.setBrush(backgroundColor
);
576 painter
.drawRect(0, 0, buttonWidth
, buttonHeight
);
578 const int spacing
= KDialog::spacingHint();
582 const int y
= (buttonHeight
- K3Icon::SizeSmall
) / 2;
583 const QIcon
&set
= icon();
585 painter
.drawPixmap(x
, y
, set
.pixmap(QIcon::Small
, QIcon::Normal
));
587 x
+= K3Icon::SizeSmall
+ spacing
;
590 painter
.setPen(foregroundColor
);
592 const int textWidth
= buttonWidth
- x
;
593 QFontMetrics
fontMetrics(font());
594 const bool clipped
= fontMetrics
.width(text()) >= textWidth
;
595 //const int align = clipped ? Qt::AlignVCenter : Qt::AlignCenter;
596 painter
.drawText(QRect(x
, 0, textWidth
, buttonHeight
), Qt::AlignVCenter
, text());
599 // Blend the right area of the text with the background, as the
601 // TODO #1: use alpha blending in Qt4 instead of drawing the text that often
602 // TODO #2: same code as in UrlNavigatorButton::drawButton() -> provide helper class?
603 const int blendSteps
= 16;
605 QColor
blendColor(backgroundColor
);
606 const int redInc
= (foregroundColor
.red() - backgroundColor
.red()) / blendSteps
;
607 const int greenInc
= (foregroundColor
.green() - backgroundColor
.green()) / blendSteps
;
608 const int blueInc
= (foregroundColor
.blue() - backgroundColor
.blue()) / blendSteps
;
609 for (int i
= 0; i
< blendSteps
; ++i
) {
610 painter
.setClipRect(QRect(x
+ textWidth
- i
, 0, 1, buttonHeight
));
611 painter
.setPen(blendColor
);
612 painter
.drawText(QRect(x
, 0, textWidth
, buttonHeight
), Qt::AlignVCenter
, text());
614 blendColor
.setRgb(blendColor
.red() + redInc
,
615 blendColor
.green() + greenInc
,
616 blendColor
.blue() + blueInc
);
621 void ServiceButton::enterEvent(QEvent
* event
)
623 QPushButton::enterEvent(event
);
628 void ServiceButton::leaveEvent(QEvent
* event
)
630 QPushButton::leaveEvent(event
);
635 void ServiceButton::slotReleased()
637 emit
requestServiceStart(m_index
);
640 #include "infosidebarpage.moc"