/*************************************************************************** * Copyright (C) 2006 by Peter Penz * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "infosidebarpage.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dolphin.h" #include "pixmapviewer.h" #include "dolphinsettings.h" InfoSidebarPage::InfoSidebarPage(TQWidget* parent) : SidebarPage(parent), m_multipleSelection(false), m_pendingPreview(false), m_timer(0), m_preview(0), m_name(0), m_currInfoLineIdx(0), m_infoGrid(0), m_actionBox(0) { const int spacing = KDialog::spacingHint(); m_timer = new TQTimer(this); connect(m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotTimeout())); TQVBoxLayout* layout = new TQVBoxLayout(this); layout->setSpacing(spacing); // preview m_preview = new PixmapViewer(this); m_preview->setMinimumWidth(TDEIcon::SizeEnormous); m_preview->setFixedHeight(TDEIcon::SizeEnormous); // name m_name = new TQLabel(this); m_name->setTextFormat(TQt::RichText); m_name->setAlignment(m_name->alignment() | TQt::AlignHCenter); TQFontMetrics fontMetrics(m_name->font()); m_name->setMinimumHeight(fontMetrics.height() * 3); m_name->setSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::Maximum); TQWidget* sep1 = new TQHGroupBox(this); // TODO: check whether default widget exist for this? sep1->setFixedHeight(1); // general information m_infoGrid = new TQGrid(2, this); m_infoGrid->setSizePolicy(TQSizePolicy::Preferred, TQSizePolicy::Fixed); TQWidget* sep2 = new TQHGroupBox(this); // TODO: check whether default widget exist for this? sep2->setFixedHeight(1); // actions m_actionBox = new TQVBox(this); m_actionBox->setSizePolicy(TQSizePolicy::Preferred, TQSizePolicy::Fixed); // Add a dummy widget with no restriction regarding a vertical resizing. // This assures that information is always top aligned. TQWidget* dummy = new TQWidget(this); layout->addItem(new TQSpacerItem(spacing, spacing, TQSizePolicy::Preferred, TQSizePolicy::Fixed)); layout->addWidget(m_preview); layout->addWidget(m_name); layout->addWidget(sep1); layout->addWidget(m_infoGrid); layout->addWidget(sep2); layout->addWidget(m_actionBox); layout->addWidget(dummy); connect(&Dolphin::mainWin(), TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(showItemInfo())); connectToActiveView(); } InfoSidebarPage::~InfoSidebarPage() { } void InfoSidebarPage::activeViewChanged() { connectToActiveView(); } void InfoSidebarPage::requestDelayedItemInfo(const KURL& url) { cancelRequest(); if (!url.isEmpty() && !m_multipleSelection) { m_urlCandidate = url; m_timer->start(300, true); } } void InfoSidebarPage::requestItemInfo(const KURL& url) { cancelRequest(); if (!url.isEmpty() && !m_multipleSelection) { m_shownURL = url; showItemInfo(); } } void InfoSidebarPage::showItemInfo() { cancelRequest(); m_multipleSelection = false; // show the preview... DolphinView* view = Dolphin::mainWin().activeView(); const KFileItemList* selectedItems = view->selectedItems(); if ((selectedItems != 0) && selectedItems->count() > 1) { m_multipleSelection = true; } if (m_multipleSelection) { TDEIconLoader iconLoader; TQPixmap icon = iconLoader.loadIcon("application-x-executable", TDEIcon::NoGroup, TDEIcon::SizeEnormous); m_preview->setPixmap(icon); m_name->setText(i18n("%n items selected", "%n items selected", selectedItems->count())); } else if (!applyBookmark()) { // try to get a preview pixmap from the item... KURL::List list; list.append(m_shownURL); m_pendingPreview = true; m_preview->setPixmap(TQPixmap()); TDEIO::PreviewJob* job = TDEIO::filePreview(list, m_preview->width(), TDEIcon::SizeEnormous); connect(job, TQT_SIGNAL(gotPreview(const KFileItem*, const TQPixmap&)), this, TQT_SLOT(gotPreview(const KFileItem*, const TQPixmap&))); connect(job, TQT_SIGNAL(failed(const KFileItem*)), this, TQT_SLOT(slotPreviewFailed(const KFileItem*))); TQString text(""); text.append(m_shownURL.fileName()); text.append(""); m_name->setText(text); } createMetaInfo(); insertActions(); } void InfoSidebarPage::slotTimeout() { m_shownURL = m_urlCandidate; showItemInfo(); } void InfoSidebarPage::slotPreviewFailed(const KFileItem* item) { m_pendingPreview = false; if (!applyBookmark()) { m_preview->setPixmap(item->pixmap(TDEIcon::SizeEnormous)); } } void InfoSidebarPage::gotPreview(const KFileItem* /* item */, const TQPixmap& pixmap) { if (m_pendingPreview) { m_preview->setPixmap(pixmap); m_pendingPreview = false; } } void InfoSidebarPage::startService(int index) { DolphinView* view = Dolphin::mainWin().activeView(); if (view->hasSelection()) { KURL::List selectedURLs = view->selectedURLs(); KDEDesktopMimeType::executeService(selectedURLs, m_actionsVector[index]); } else { KDEDesktopMimeType::executeService(m_shownURL, m_actionsVector[index]); } } void InfoSidebarPage::connectToActiveView() { cancelRequest(); DolphinView* view = Dolphin::mainWin().activeView(); connect(view, TQT_SIGNAL(signalRequestItemInfo(const KURL&)), this, TQT_SLOT(requestDelayedItemInfo(const KURL&))); connect(view, TQT_SIGNAL(signalURLChanged(const KURL&)), this, TQT_SLOT(requestItemInfo(const KURL&))); m_shownURL = view->url(); showItemInfo(); } bool InfoSidebarPage::applyBookmark() { KBookmarkGroup root = DolphinSettings::instance().bookmarkManager()->root(); KBookmark bookmark = root.first(); while (!bookmark.isNull()) { if (m_shownURL.equals(bookmark.url(), true)) { TQString text(""); text.append(bookmark.text()); text.append(""); m_name->setText(text); TDEIconLoader iconLoader; TQPixmap icon = iconLoader.loadIcon(bookmark.icon(), TDEIcon::NoGroup, TDEIcon::SizeEnormous); m_preview->setPixmap(icon); return true; } bookmark = root.next(bookmark); } return false; } void InfoSidebarPage::cancelRequest() { m_timer->stop(); m_pendingPreview = false; } void InfoSidebarPage::createMetaInfo() { // To prevent a flickering it's important to reuse available // labels instead of deleting them and recreate them afterwards. // The methods beginInfoLines(), addInfoLine() and endInfoLines() // take care of this. beginInfoLines(); DolphinView* view = Dolphin::mainWin().activeView(); if (!view->hasSelection()) { KFileItem fileItem(S_IFDIR, KFileItem::Unknown, m_shownURL); fileItem.refresh(); if (fileItem.isDir()) { addInfoLine(i18n("Type:"), i18n("Directory")); } else { addInfoLine(i18n("Type:"), fileItem.mimeComment()); TQString sizeText(TDEIO::convertSize(fileItem.size())); addInfoLine(i18n("Size:"), sizeText); addInfoLine(i18n("Modified:"), fileItem.timeString()); const KFileMetaInfo& metaInfo = fileItem.metaInfo(); if (metaInfo.isValid()) { TQStringList keys = metaInfo.supportedKeys(); for (TQStringList::Iterator it = keys.begin(); it != keys.end(); ++it) { if (showMetaInfo(*it)) { KFileMetaInfoItem metaInfoItem = metaInfo.item(*it); addInfoLine(*it, metaInfoItem.string()); } } } } } endInfoLines(); } void InfoSidebarPage::beginInfoLines() { m_currInfoLineIdx = 0; } void InfoSidebarPage::endInfoLines() { if (m_currInfoLineIdx <= 0) { return; } // remove labels which have not been used if (m_currInfoLineIdx < static_cast(m_infoWidgets.count())) { TQPtrListIterator deleteIter(m_infoWidgets); deleteIter += m_currInfoLineIdx; TQWidget* widget = 0; int removeCount = 0; while ((widget = deleteIter.current()) != 0) { widget->close(); widget->deleteLater(); ++deleteIter; ++removeCount; } for (int i = 0; i < removeCount; ++i) { m_infoWidgets.removeLast(); } } } bool InfoSidebarPage::showMetaInfo(const TQString& key) const { // sorted list of keys, where it's data should be shown static const char* keys[] = { "Album", "Artist", "Author", "Bitrate", "Date", "Dimensions", "Genre", "Length", "Lines", "Pages", "Title", "Words" }; // do a binary search for the key... int top = 0; int bottom = sizeof(keys) / sizeof(char*) - 1; while (top < bottom) { const int middle = (top + bottom) / 2; const int result = key.compare(keys[middle]); if (result < 0) { bottom = middle - 1; } else if (result > 0) { top = middle + 1; } else { return true; } } return false; } void InfoSidebarPage::addInfoLine(const TQString& labelText, const TQString& infoText) { TQString labelStr(""); labelStr.append(labelText); labelStr.append(" "); const int count = m_infoWidgets.count(); if (m_currInfoLineIdx < count - 1) { // reuse available labels m_infoWidgets.at(m_currInfoLineIdx++)->setText(labelStr); m_infoWidgets.at(m_currInfoLineIdx++)->setText(infoText); } else { // no labels are available anymore, hence create 2 new ones TQLabel* label = new TQLabel(labelStr, m_infoGrid); label->setTextFormat(TQt::RichText); label->setAlignment(TQt::AlignRight | TQt::AlignTop); label->show(); m_infoWidgets.append(label); TQLabel* info = new TQLabel(infoText, m_infoGrid); info->setTextFormat(TQt::RichText); info->setAlignment(TQt::AlignTop | TQt::WordBreak); info->show(); m_infoWidgets.append(info); m_currInfoLineIdx += 2; } } void InfoSidebarPage::insertActions() { // delete all existing action widgets // TODO: just use children() from TQObject... TQPtrListIterator deleteIter(m_actionWidgets); TQWidget* widget = 0; while ((widget = deleteIter.current()) != 0) { widget->close(); widget->deleteLater(); ++deleteIter; } m_actionWidgets.clear(); m_actionsVector.clear(); int actionsIndex = 0; // The algorithm for searching the available actions works on a list // of KFileItems. If no selection is given, a temporary KFileItem // by the given URL 'url' is created and added to the list. KFileItem fileItem(S_IFDIR, KFileItem::Unknown, m_shownURL); KFileItemList localList; const KFileItemList* itemList = Dolphin::mainWin().activeView()->selectedItems(); if ((itemList == 0) || itemList->isEmpty()) { fileItem.refresh(); localList.append(&fileItem); itemList = &localList; } // 'itemList' contains now all KFileItems, where an item information should be shown. // TODO: the following algorithm is quite equal to DolphinContextMenu::insertActionItems(). // It's open yet whether they should be merged or whether they have to work slightly different. TQStringList dirs = TDEGlobal::dirs()->findDirs("data", "d3lphin/servicemenus/"); for (TQStringList::ConstIterator dirIt = dirs.begin(); dirIt != dirs.end(); ++dirIt) { TQDir dir(*dirIt); TQStringList entries = dir.entryList("*.desktop", TQDir::Files); for (TQStringList::ConstIterator entryIt = entries.begin(); entryIt != entries.end(); ++entryIt) { KSimpleConfig cfg(*dirIt + *entryIt, true); cfg.setDesktopGroup(); if ((cfg.hasKey("Actions") || cfg.hasKey("X-TDE-GetActionMenu")) && cfg.hasKey("X-TDE-ServiceTypes")) { const TQStringList types = cfg.readListEntry("X-TDE-ServiceTypes"); for (TQStringList::ConstIterator it = types.begin(); it != types.end(); ++it) { // check whether the mime type is equal or whether the // mimegroup (e. g. image/*) is supported bool insert = false; if ((*it) == "all/allfiles") { // The service type is valid for all files, but not for directories. // Check whether the selected items only consist of files... KFileItemListIterator mimeIt(*itemList); KFileItem* item = 0; insert = true; while (insert && ((item = mimeIt.current()) != 0)) { insert = !item->isDir(); ++mimeIt; } } if (!insert) { // Check whether the MIME types of all selected files match // to the mimetype of the service action. As soon as one MIME // type does not match, no service menu is shown at all. KFileItemListIterator mimeIt(*itemList); KFileItem* item = 0; insert = true; while (insert && ((item = mimeIt.current()) != 0)) { const TQString mimeType((*mimeIt)->mimetype()); const TQString mimeGroup(mimeType.left(mimeType.find('/'))); insert = (*it == mimeType) || ((*it).right(1) == "*") && ((*it).left((*it).find('/')) == mimeGroup); ++mimeIt; } } if (insert) { const TQString submenuName = cfg.readEntry( "X-TDE-Submenu" ); TQPopupMenu* popup = 0; if (!submenuName.isEmpty()) { // create a sub menu containing all actions popup = new TQPopupMenu(); connect(popup, TQT_SIGNAL(activated(int)), this, TQT_SLOT(startService(int))); TQPushButton* button = new TQPushButton(submenuName, m_actionBox); button->setFlat(true); button->setPopup(popup); button->show(); m_actionWidgets.append(button); } TQValueList userServices = KDEDesktopMimeType::userDefinedServices(*dirIt + *entryIt, true); // iterate through all actions and add them to a widget TQValueList::Iterator serviceIt; for (serviceIt = userServices.begin(); serviceIt != userServices.end(); ++serviceIt) { KDEDesktopMimeType::Service service = (*serviceIt); if (popup == 0) { ServiceButton* button = new ServiceButton(SmallIcon(service.m_strIcon), service.m_strName, m_actionBox, actionsIndex); connect(button, TQT_SIGNAL(requestServiceStart(int)), this, TQT_SLOT(startService(int))); m_actionWidgets.append(button); button->show(); } else { popup->insertItem(SmallIcon(service.m_strIcon), service.m_strName, actionsIndex); } m_actionsVector.append(service); ++actionsIndex; } } } } } } } ServiceButton::ServiceButton(const TQIconSet& icon, const TQString& text, TQWidget* parent, int index) : TQPushButton(icon, text, parent), m_hover(false), m_index(index) { setEraseColor(colorGroup().background()); setFocusPolicy(TQ_NoFocus); connect(this, TQT_SIGNAL(released()), this, TQT_SLOT(slotReleased())); } ServiceButton::~ServiceButton() { } void ServiceButton::drawButton(TQPainter* painter) { const int buttonWidth = width(); const int buttonHeight = height(); TQColor backgroundColor; TQColor foregroundColor; if (m_hover) { backgroundColor = TDEGlobalSettings::highlightColor(); foregroundColor = TDEGlobalSettings::highlightedTextColor(); } else { backgroundColor = colorGroup().background(); foregroundColor = TDEGlobalSettings::buttonTextColor(); } // draw button background painter->setPen(NoPen); painter->setBrush(backgroundColor); painter->drawRect(0, 0, buttonWidth, buttonHeight); const int spacing = KDialog::spacingHint(); // draw icon int x = spacing; const int y = (buttonHeight - TDEIcon::SizeSmall) / 2; const TQIconSet* set = iconSet(); if (set != 0) { painter->drawPixmap(x, y, set->pixmap(TQIconSet::Small, TQIconSet::Normal)); } x += TDEIcon::SizeSmall + spacing; // draw text painter->setPen(foregroundColor); const int textWidth = buttonWidth - x; TQFontMetrics fontMetrics(font()); const bool clipped = fontMetrics.width(text()) >= textWidth; //const int align = clipped ? TQt::AlignVCenter : TQt::AlignCenter; painter->drawText(TQRect(x, 0, textWidth, buttonHeight), TQt::AlignVCenter, text()); if (clipped) { // Blend the right area of the text with the background, as the // text is clipped. // TODO #1: use alpha blending in TQt4 instead of drawing the text that often // TODO #2: same code as in URLNavigatorButton::drawButton() -> provide helper class? const int blendSteps = 16; TQColor blendColor(backgroundColor); const int redInc = (foregroundColor.red() - backgroundColor.red()) / blendSteps; const int greenInc = (foregroundColor.green() - backgroundColor.green()) / blendSteps; const int blueInc = (foregroundColor.blue() - backgroundColor.blue()) / blendSteps; for (int i = 0; i < blendSteps; ++i) { painter->setClipRect(TQRect(x + textWidth - i, 0, 1, buttonHeight)); painter->setPen(blendColor); painter->drawText(TQRect(x, 0, textWidth, buttonHeight), TQt::AlignVCenter, text()); blendColor.setRgb(blendColor.red() + redInc, blendColor.green() + greenInc, blendColor.blue() + blueInc); } } } void ServiceButton::enterEvent(TQEvent* event) { TQPushButton::enterEvent(event); m_hover = true; update(); } void ServiceButton::leaveEvent(TQEvent* event) { TQPushButton::leaveEvent(event); m_hover = false; update(); } void ServiceButton::slotReleased() { emit requestServiceStart(m_index); } #include "infosidebarpage.moc"