diff options
Diffstat (limited to 'kicker/libkicker/panelbutton.cpp')
-rw-r--r-- | kicker/libkicker/panelbutton.cpp | 985 |
1 files changed, 985 insertions, 0 deletions
diff --git a/kicker/libkicker/panelbutton.cpp b/kicker/libkicker/panelbutton.cpp new file mode 100644 index 000000000..f53d4b38f --- /dev/null +++ b/kicker/libkicker/panelbutton.cpp @@ -0,0 +1,985 @@ +/***************************************************************** + +Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <qcursor.h> +#include <qfile.h> +#include <qfontmetrics.h> +#include <qpainter.h> +#include <qpopupmenu.h> +#include <qstyle.h> +#include <qstylesheet.h> +#include <qtooltip.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kcursor.h> +#include <kdialog.h> +#include <kdirwatch.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <kicontheme.h> +#include <kipc.h> +#include <kstandarddirs.h> +#include <klocale.h> + +#include "global.h" + +#include "kshadowengine.h" +#include "kshadowsettings.h" + +#include "kickerSettings.h" +#include "panelbutton.h" +#include "panelbutton.moc" + +// init static variable +KShadowEngine* PanelButton::s_textShadowEngine = 0L; + +PanelButton::PanelButton( QWidget* parent, const char* name ) + : QButton(parent, name), + m_valid(true), + m_isLeftMouseButtonDown(false), + m_drawArrow(false), + m_highlight(false), + m_changeCursorOverItem(true), + m_hasAcceptedDrag(false), + m_arrowDirection(KPanelExtension::Bottom), + m_popupDirection(KPanelApplet::Up), + m_orientation(Horizontal), + m_size((KIcon::StdSizes)-1), + m_fontPercent(0.40) +{ + setBackgroundOrigin(AncestorOrigin); + setWFlags(WNoAutoErase); + KGlobal::locale()->insertCatalogue("libkicker"); + calculateIconSize(); + setAcceptDrops(true); + + m_textColor = KGlobalSettings::textColor(); + + updateSettings(KApplication::SETTINGS_MOUSE); + + kapp->addKipcEventMask(KIPC::SettingsChanged | KIPC::IconChanged); + + installEventFilter(KickerTip::the()); + + connect(kapp, SIGNAL(settingsChanged(int)), SLOT(updateSettings(int))); + connect(kapp, SIGNAL(iconChanged(int)), SLOT(updateIcon(int))); +} + +void PanelButton::configure() +{ + QString name = tileName(); + if( name.isEmpty() ) + return; + + if (!KickerSettings::enableTileBackground()) + { + setTile(QString::null); + return; + } + + KConfigGroup tilesGroup( KGlobal::config(), "button_tiles" ); + if( !tilesGroup.readBoolEntry( "Enable" + name + "Tiles", true ) ) { + setTile( QString::null ); + return; + } + + QString tile = tilesGroup.readEntry( name + "Tile" ); + QColor color = QColor(); + + if (tile == "Colorize") + { + color = tilesGroup.readColorEntry( name + "TileColor" ); + tile = QString::null; + } + + setTile( tile, color ); +} + +void PanelButton::setTile(const QString& tile, const QColor& color) +{ + if (tile == m_tile && m_tileColor == color) + { + return; + } + + m_tile = tile; + m_tileColor = color; + loadTiles(); + update(); +} + +void PanelButton::setDrawArrow(bool drawArrow) +{ + if (m_drawArrow == drawArrow) + { + return; + } + + m_drawArrow = drawArrow; + update(); +} + +QImage PanelButton::loadTile(const QString& tile, + const QSize& size, + const QString& state) +{ + QString name = tile; + + if (size.height() < 42) + { + name += "_tiny_"; + } + else if (size.height() < 54) + { + name += "_normal_"; + } + else + { + name += "_large_"; + } + + name += state + ".png"; + + QImage tileImg(KGlobal::dirs()->findResource("tiles", name)); + + // scale if size does not match exactly + if (!tileImg.isNull() && tileImg.size() != size) + { + tileImg = tileImg.smoothScale(size); + } + + return tileImg; +} + +void PanelButton::setEnabled(bool enable) +{ + QButton::setEnabled(enable); + loadIcons(); + update(); +} + +void PanelButton::setPopupDirection(KPanelApplet::Direction d) +{ + m_popupDirection = d; + setArrowDirection(KickerLib::directionToPopupPosition(d)); +} + +void PanelButton::setOrientation(Orientation o) +{ + m_orientation = o; +} + +void PanelButton::updateIcon(int group) +{ + if (group != KIcon::Panel) + { + return; + } + + loadIcons(); + update(); +} + +void PanelButton::updateSettings(int category) +{ + if (category != KApplication::SETTINGS_MOUSE) + { + return; + } + + m_changeCursorOverItem = KGlobalSettings::changeCursorOverIcon(); + + if (m_changeCursorOverItem) + { + setCursor(KCursor::handCursor()); + } + else + { + unsetCursor(); + } +} + +void PanelButton::checkForDeletion(const QString& path) +{ + if (path == m_backingFile) + { + setEnabled(false); + QTimer::singleShot(1000, this, SLOT(scheduleForRemoval())); + } +} + +bool PanelButton::checkForBackingFile() +{ + return QFile::exists(m_backingFile); +} + +void PanelButton::scheduleForRemoval() +{ + static int timelapse = 1000; + if (checkForBackingFile()) + { + setEnabled(true); + timelapse = 1000; + emit hideme(false); + return; + } + else if (KickerSettings::removeButtonsWhenBroken()) + { + if (timelapse > 255*1000) // we'v given it ~8.5 minutes by this point + { + emit removeme(); + return; + } + + if (timelapse > 3000 && isVisible()) + { + emit hideme(true); + } + + timelapse *= 2; + QTimer::singleShot(timelapse, this, SLOT(scheduleForRemoval())); + } +} + +// return the dimension that the button wants to be for a given panel dimension (panelDim) +int PanelButton::preferredDimension(int panelDim) const +{ + // determine the upper limit on the size. Normally, this is panelDim, + // but if conserveSpace() is true, we restrict size to comfortably fit the icon + if (KickerSettings::conserveSpace()) + { + int newSize = preferredIconSize(panelDim); + if (newSize > 0) + { + return QMIN(panelDim, newSize + (KDialog::spacingHint() * 2)); + } + } + + return panelDim; +} + +int PanelButton::widthForHeight(int height) const +{ + int rc = preferredDimension(height); + + // we only paint the text when horizontal, so make sure we're horizontal + // before adding the text in here + if (orientation() == Horizontal && !m_buttonText.isEmpty()) + { + QFont f(font()); + f.setPixelSize(KMIN(height, KMAX(int(float(height) * m_fontPercent), 16))); + QFontMetrics fm(f); + + rc += fm.width(m_buttonText) + KMIN(25, KMAX(5, fm.width('m') / 2)); + } + + return rc; +} + +int PanelButton::heightForWidth(int width) const +{ + return preferredDimension(width); +} + +const QPixmap& PanelButton::labelIcon() const +{ + return m_highlight ? m_iconh : m_icon; +} + +const QPixmap& PanelButton::zoomIcon() const +{ + return m_iconz; +} + +bool PanelButton::isValid() const +{ + return m_valid; +} + +void PanelButton::setTitle(const QString& t) +{ + m_title = t; +} + +void PanelButton::setIcon(const QString& icon) +{ + if (icon == m_iconName) + { + return; + } + + m_iconName = icon; + loadIcons(); + update(); + emit iconChanged(); +} + +QString PanelButton::icon() const +{ + return m_iconName; +} + +bool PanelButton::hasText() const +{ + return !m_buttonText.isEmpty(); +} + +void PanelButton::setButtonText(const QString& text) +{ + m_buttonText = text; + update(); +} + +QString PanelButton::buttonText() const +{ + return m_buttonText; +} + +void PanelButton::setTextColor(const QColor& c) +{ + m_textColor = c; +} + +QColor PanelButton::textColor() const +{ + return m_textColor; +} + +void PanelButton::setFontPercent(double p) +{ + m_fontPercent = p; +} + +double PanelButton::fontPercent() const +{ + return m_fontPercent; +} + +KPanelExtension::Orientation PanelButton::orientation() const +{ + return m_orientation; +} + +KPanelApplet::Direction PanelButton::popupDirection() const +{ + return m_popupDirection; +} + +QPoint PanelButton::center() const +{ + return mapToGlobal(rect().center()); +} + +QString PanelButton::title() const +{ + return m_title; +} + +void PanelButton::triggerDrag() +{ + setDown(false); + + startDrag(); +} + +void PanelButton::startDrag() +{ + emit dragme(m_icon); +} + +void PanelButton::enterEvent(QEvent* e) +{ + if (!m_highlight) + { + m_highlight = true; + repaint(false); + } + + QButton::enterEvent(e); +} + +void PanelButton::leaveEvent(QEvent* e) +{ + if (m_highlight) + { + m_highlight = false; + repaint(false); + } + + QButton::leaveEvent(e); +} + +void PanelButton::dragEnterEvent(QDragEnterEvent* e) +{ + if (e->isAccepted()) + { + m_hasAcceptedDrag = true; + } + + update(); + QButton::dragEnterEvent( e ); +} + +void PanelButton::dragLeaveEvent(QDragLeaveEvent* e) +{ + m_hasAcceptedDrag = false; + update(); + QButton::dragLeaveEvent( e ); +} + +void PanelButton::dropEvent(QDropEvent* e) +{ + m_hasAcceptedDrag = false; + update(); + QButton::dropEvent( e ); +} + +void PanelButton::mouseMoveEvent(QMouseEvent *e) +{ + if (!m_isLeftMouseButtonDown || (e->state() & LeftButton) == 0) + { + return; + } + + QPoint p(e->pos() - m_lastLeftMouseButtonPress); + if (p.manhattanLength() <= 16) + { + // KGlobalSettings::dndEventDelay() is not enough! + return; + } + + m_isLeftMouseButtonDown = false; + triggerDrag(); +} + +void PanelButton::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == LeftButton) + { + m_lastLeftMouseButtonPress = e->pos(); + m_isLeftMouseButtonDown = true; + } + QButton::mousePressEvent(e); +} + +void PanelButton::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() == LeftButton) + { + m_isLeftMouseButtonDown = false; + } + QButton::mouseReleaseEvent(e); +} + +void PanelButton::resizeEvent(QResizeEvent*) +{ + loadTiles(); + + if (calculateIconSize()) + { + loadIcons(); + } +} + +void PanelButton::drawButton(QPainter *p) +{ + const QPixmap& tile = (isDown() || isOn()) ? m_down : m_up; + + if (m_tileColor.isValid()) + { + p->fillRect(rect(), m_tileColor); + style().drawPrimitive(QStyle::PE_Panel, p, rect(), colorGroup()); + } + else if (paletteBackgroundPixmap()) + { + // Draw the background. This is always needed, even when using tiles, + // because they don't have to cover the entire button. + QPoint offset = backgroundOffset(); + int ox = offset.x(); + int oy = offset.y(); + p->drawTiledPixmap( 0, 0, width(), height(),*paletteBackgroundPixmap(), ox, oy); + } + + if (!tile.isNull()) + { + // Draw the tile. + p->drawPixmap(0, 0, tile); + } + else if (isDown() || isOn()) + { + // Draw shapes to indicate the down state. + style().drawPrimitive(QStyle::PE_Panel, p, rect(), colorGroup(), QStyle::Style_Sunken); + } + + drawButtonLabel(p); + + if (hasFocus() || m_hasAcceptedDrag) + { + int x1, y1, x2, y2; + rect().coords(&x1, &y1, &x2, &y2); + QRect r(x1+2, y1+2, x2-x1-3, y2-y1-3); + style().drawPrimitive(QStyle::PE_FocusRect, p, r, colorGroup(), + QStyle::Style_Default, colorGroup().button()); + } +} + +void PanelButton::drawButtonLabel(QPainter *p) +{ + QPixmap icon = labelIcon(); + bool active = isDown() || isOn(); + + if (active) + { + icon = icon.convertToImage().smoothScale(icon.width() - 2, + icon.height() - 2); + } + + if (!m_buttonText.isEmpty() && orientation() == Horizontal) + { + int h = height(); + int w = width(); + int y = (h - icon.height())/2; + p->save(); + QFont f = font(); + + double fontPercent = m_fontPercent; + if (active) + { + fontPercent *= .8; + } + f.setPixelSize(KMIN(h, KMAX(int(float(h) * m_fontPercent), 16))); + QFontMetrics fm(f); + p->setFont(f); + + /* Draw shadowed text */ + bool reverse = QApplication::reverseLayout(); + QPainter::TextDirection rtl = reverse ? QPainter::RTL : QPainter::LTR; + + if (!reverse && !icon.isNull()) + { + /* Draw icon */ + p->drawPixmap(3, y, icon); + } + + int tX = reverse ? 3 : icon.width() + KMIN(25, KMAX(5, fm.width('m') / 2)); + int tY = fm.ascent() + ((h - fm.height()) / 2); + + QColor shadCol = KickerLib::shadowColor(m_textColor); + + // get a transparent pixmap + QPainter pixPainter; + QPixmap textPixmap(w, h); + + textPixmap.fill(QColor(0,0,0)); + textPixmap.setMask(textPixmap.createHeuristicMask(true)); + + // draw text + pixPainter.begin(&textPixmap); + pixPainter.setPen(m_textColor); + pixPainter.setFont(p->font()); // get the font from the root painter + pixPainter.drawText(tX, tY, m_buttonText, -1, rtl); + pixPainter.end(); + + if (!s_textShadowEngine) + { + KShadowSettings* shadset = new KShadowSettings(); + shadset->setOffsetX(0); + shadset->setOffsetY(0); + shadset->setThickness(1); + shadset->setMaxOpacity(96); + s_textShadowEngine = new KShadowEngine(shadset); + } + + // draw shadow + QImage img = s_textShadowEngine->makeShadow(textPixmap, shadCol); + p->drawImage(0, 0, img); + p->save(); + p->setPen(m_textColor); + p->drawText(tX, tY, m_buttonText, -1, rtl); + p->restore(); + + if (reverse && !icon.isNull()) + { + p->drawPixmap(w - icon.width() - 3, y, icon); + } + + p->restore(); + } + else if (!icon.isNull()) + { + int y = (height() - icon.height()) / 2; + int x = (width() - icon.width()) / 2; + p->drawPixmap(x, y, icon); + } + + if (m_drawArrow && (m_highlight || active)) + { + QStyle::PrimitiveElement e = QStyle::PE_ArrowUp; + int arrowSize = style().pixelMetric(QStyle::PM_MenuButtonIndicator); + QRect r((width() - arrowSize)/2, 0, arrowSize, arrowSize); + + switch (m_arrowDirection) + { + case KPanelExtension::Top: + e = QStyle::PE_ArrowUp; + break; + case KPanelExtension::Bottom: + e = QStyle::PE_ArrowDown; + r.moveBy(0, height() - arrowSize); + break; + case KPanelExtension::Right: + e = QStyle::PE_ArrowRight; + r = QRect(width() - arrowSize, (height() - arrowSize)/2, arrowSize, arrowSize); + break; + case KPanelExtension::Left: + e = QStyle::PE_ArrowLeft; + r = QRect(0, (height() - arrowSize)/2, arrowSize, arrowSize); + break; + case KPanelExtension::Floating: + if (orientation() == Horizontal) + { + e = QStyle::PE_ArrowDown; + r.moveBy(0, height() - arrowSize); + } + else if (QApplication::reverseLayout()) + { + e = QStyle::PE_ArrowLeft; + r = QRect(0, (height() - arrowSize)/2, arrowSize, arrowSize); + } + else + { + e = QStyle::PE_ArrowRight; + r = QRect(width() - arrowSize, (height() - arrowSize)/2, arrowSize, arrowSize); + } + break; + } + + int flags = QStyle::Style_Enabled; + if (active) + { + flags |= QStyle::Style_Down; + } + style().drawPrimitive(e, p, r, colorGroup(), flags); + } +} + +// return the icon size that would be used if the panel were proposed_size +// if proposed_size==-1, use the current panel size instead +int PanelButton::preferredIconSize(int proposed_size) const +{ + // (re)calculates the icon sizes and report true if they have changed. + // Get sizes from icontheme. We assume they are sorted. + KIconTheme *ith = KGlobal::iconLoader()->theme(); + + if (!ith) + { + return -1; // unknown icon size + } + + QValueList<int> sizes = ith->querySizes(KIcon::Panel); + + int sz = ith->defaultSize(KIcon::Panel); + + if (proposed_size < 0) + { + proposed_size = (orientation() == Horizontal) ? height() : width(); + } + + // determine the upper limit on the size. Normally, this is panelSize, + // but if conserve space is requested, the max button size is used instead. + int upperLimit = proposed_size; + if (proposed_size > KickerLib::maxButtonDim() && + KickerSettings::conserveSpace()) + { + upperLimit = KickerLib::maxButtonDim(); + } + + //kdDebug()<<endl<<endl<<flush; + QValueListConstIterator<int> i = sizes.constBegin(); + while (i != sizes.constEnd()) + { + if ((*i) + (2 * KickerSettings::iconMargin()) > upperLimit) + { + break; + } + sz = *i; // get the largest size under the limit + ++i; + } + + //kdDebug()<<"Using icon sizes: "<<sz<<" "<<zoom_sz<<endl<<flush; + return sz; +} + +void PanelButton::backedByFile(const QString& localFilePath) +{ + m_backingFile = localFilePath; + + if (m_backingFile.isEmpty()) + { + return; + } + + // avoid multiple connections + disconnect(KDirWatch::self(), SIGNAL(deleted(const QString&)), + this, SLOT(checkForDeletion(const QString&))); + + if (!KDirWatch::self()->contains(m_backingFile)) + { + KDirWatch::self()->addFile(m_backingFile); + } + + connect(KDirWatch::self(), SIGNAL(deleted(const QString&)), + this, SLOT(checkForDeletion(const QString&))); + +} + +void PanelButton::setArrowDirection(KPanelExtension::Position dir) +{ + if (m_arrowDirection != dir) + { + m_arrowDirection = dir; + update(); + } +} + +void PanelButton::loadTiles() +{ + if (m_tileColor.isValid()) + { + setBackgroundOrigin(WidgetOrigin); + m_up = m_down = QPixmap(); + } + else if (m_tile.isNull()) + { + setBackgroundOrigin(AncestorOrigin); + m_up = m_down = QPixmap(); + } + else + { + setBackgroundOrigin(WidgetOrigin); + // If only the tiles were named a bit smarter we wouldn't have + // to pass the up or down argument. + m_up = QPixmap(loadTile(m_tile, size(), "up")); + m_down = QPixmap(loadTile(m_tile, size(), "down")); + } +} + +void PanelButton::loadIcons() +{ + KIconLoader * ldr = KGlobal::iconLoader(); + QString nm = m_iconName; + KIcon::States defaultState = isEnabled() ? KIcon::DefaultState : + KIcon::DisabledState; + m_icon = ldr->loadIcon(nm, KIcon::Panel, m_size, defaultState, 0L, true); + + if (m_icon.isNull()) + { + nm = defaultIcon(); + m_icon = ldr->loadIcon(nm, KIcon::Panel, m_size, defaultState); + } + + if (!isEnabled()) + { + m_iconh = m_icon; + } + else + { + m_iconh = ldr->loadIcon(nm, KIcon::Panel, m_size, + KIcon::ActiveState, 0L, true); + } + + m_iconz = ldr->loadIcon(nm, KIcon::Panel, KIcon::SizeHuge, + defaultState, 0L, true ); +} + +// (re)calculates the icon sizes and report true if they have changed. +// (false if we don't know, because theme couldn't be loaded?) +bool PanelButton::calculateIconSize() +{ + int size = preferredIconSize(); + + if (size < 0) + { + // size unknown + return false; + } + + if (m_size != size) + { + // Size has changed, update + m_size = size; + return true; + } + + return false; +} + +void PanelButton::updateKickerTip(KickerTip::Data& data) +{ + data.message = QStyleSheet::escape(title()); + data.subtext = QStyleSheet::escape(QToolTip::textFor(this)); + data.icon = zoomIcon(); + data.direction = popupDirection(); +} + +// +// PanelPopupButton class +// + +PanelPopupButton::PanelPopupButton(QWidget *parent, const char *name) + : PanelButton(parent, name), + m_popup(0), + m_pressedDuringPopup(false), + m_initialized(false) +{ + connect(this, SIGNAL(pressed()), SLOT(slotExecMenu())); +} + +void PanelPopupButton::setPopup(QPopupMenu *popup) +{ + if (m_popup) + { + m_popup->removeEventFilter(this); + disconnect(m_popup, SIGNAL(aboutToHide()), this, SLOT(menuAboutToHide())); + } + + m_popup = popup; + setDrawArrow(m_popup != 0); + + if (m_popup) + { + m_popup->installEventFilter(this); + connect(m_popup, SIGNAL(aboutToHide()), this, SLOT(menuAboutToHide())); + } +} + +QPopupMenu *PanelPopupButton::popup() const +{ + return m_popup; +} + +bool PanelPopupButton::eventFilter(QObject *, QEvent *e) +{ + if (e->type() == QEvent::MouseMove) + { + QMouseEvent *me = static_cast<QMouseEvent *>(e); + if (rect().contains(mapFromGlobal(me->globalPos())) && + ((me->state() & ControlButton) != 0 || + (me->state() & ShiftButton) != 0)) + { + PanelButton::mouseMoveEvent(me); + return true; + } + } + else if (e->type() == QEvent::MouseButtonPress || + e->type() == QEvent::MouseButtonDblClick) + { + QMouseEvent *me = static_cast<QMouseEvent *>(e); + if (rect().contains(mapFromGlobal(me->globalPos()))) + { + m_pressedDuringPopup = true; + return true; + } + } + else if (e->type() == QEvent::MouseButtonRelease) + { + QMouseEvent *me = static_cast<QMouseEvent *>(e); + if (rect().contains(mapFromGlobal(me->globalPos()))) + { + if (m_pressedDuringPopup && m_popup) + { + m_popup->hide(); + } + return true; + } + } + return false; +} + +void PanelPopupButton::showMenu() +{ + if (isDown()) + { + if (m_popup) + { + m_popup->hide(); + } + + setDown(false); + return; + } + + setDown(true); + update(); + slotExecMenu(); +} + +void PanelPopupButton::slotExecMenu() +{ + if (!m_popup) + { + return; + } + + m_pressedDuringPopup = false; + KickerTip::enableTipping(false); + kapp->syncX(); + kapp->processEvents(); + + if (!m_initialized) + { + initPopup(); + } + + m_popup->adjustSize(); + m_popup->exec(KickerLib::popupPosition(popupDirection(), m_popup, this)); +} + +void PanelPopupButton::menuAboutToHide() +{ + if (!m_popup) + { + return; + } + + setDown(false); + KickerTip::enableTipping(true); +} + +void PanelPopupButton::triggerDrag() +{ + if (m_popup) + { + m_popup->hide(); + } + + PanelButton::triggerDrag(); +} + +void PanelPopupButton::setInitialized(bool initialized) +{ + m_initialized = initialized; +} + |