File indexing completed on 2024-05-19 12:54:53
0001 /* This file is part of the KDE project 0002 Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 0003 Copyright (C) 2011-2014 Jarosław Staniek <staniek@kde.org> 0004 0005 Based on qmenu.cpp from Qt 4.7 0006 0007 Based on oxygenhelper.cpp 0008 0009 Copyright 2009-2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org> 0010 Copyright 2008 Long Huynh Huu <long.upcase@googlemail.com> 0011 Copyright 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> 0012 Copyright 2007 C. Boemann <cbo@boemann.dk> 0013 Copyright 2007 Fredrik Höglund <fredrik@kde.org> 0014 0015 This library is free software; you can redistribute it and/or 0016 modify it under the terms of the GNU Library General Public 0017 License as published by the Free Software Foundation; either 0018 version 2 of the License, or (at your option) any later version. 0019 0020 This library is distributed in the hope that it will be useful, 0021 but WITHOUT ANY WARRANTY; without even the implied warranty of 0022 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0023 Library General Public License for more details. 0024 0025 You should have received a copy of the GNU Library General Public License 0026 along with this library; see the file COPYING.LIB. If not, write to 0027 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0028 * Boston, MA 02110-1301, USA. 0029 */ 0030 0031 #include "KexiMenuWidget.h" 0032 #include "KexiMenuWidget_p.h" 0033 #include <KColorScheme> 0034 #include <KColorUtils> 0035 #include <KIconLoader> 0036 #include <kexiutils/utils.h> 0037 #include <KexiVersion.h> 0038 #include <KexiIcon.h> 0039 #include <KLocalizedString> 0040 0041 #include <QApplication> 0042 #include <QBoxLayout> 0043 #include <QCache> 0044 #include <QDebug> 0045 #include <QDesktopServices> 0046 #include <QDesktopWidget> 0047 #include <QEvent> 0048 #include <QFontDatabase> 0049 #include <QLabel> 0050 #include <QMenu> 0051 #include <QPainter> 0052 #include <QPushButton> 0053 #include <QScopedPointer> 0054 #include <QStyle> 0055 #include <QTimer> 0056 #include <QToolButton> 0057 #include <QUrl> 0058 #include <QWidgetAction> 0059 #ifndef QT_NO_ACCESSIBILITY 0060 # include <qaccessible.h> 0061 #endif 0062 #ifndef QT_NO_WHATSTHIS 0063 # include <QWhatsThis> 0064 #endif 0065 0066 const int calligraLogoPixmapInternalWidth = 100; 0067 const int calligraLogoPixmapInternalHeight = 71; 0068 const char calligraUrl[] = "https://www.calligra.org"; 0069 const char facebookUrl[] = "https://www.facebook.com/kexi.project"; 0070 const char twitterUrl[] = "https://twitter.com/kexi_project"; 0071 0072 //! @todo KEXI3 port OxygenHelper 0073 #if 0 0074 // from oxygenhelper.cpp: 0075 OxygenHelper::OxygenHelper() 0076 : _componentData("oxygen", 0, KComponentData::SkipMainComponentRegistration) 0077 { 0078 _config = _componentData.config(); 0079 _contrast = KColorScheme::contrastF(_config); 0080 // background contrast is calculated so that it is 0.9 0081 // when KGlobalSettings contrast value of 0.7 0082 _bgcontrast = qMin(1.0, 0.9*_contrast/0.7); 0083 } 0084 0085 OxygenHelper::~OxygenHelper() 0086 { 0087 } 0088 0089 bool OxygenHelper::lowThreshold(const QColor &color) 0090 { 0091 const quint32 key( color.rgba() ); 0092 ColorMap::iterator iter( m_lowThreshold.find( key ) ); 0093 if( iter != m_lowThreshold.end() ) return iter.value(); 0094 else { 0095 0096 const QColor darker( KColorScheme::shade(color, KColorScheme::MidShade, 0.5 ) ); 0097 const bool result( KColorUtils::luma(darker) > KColorUtils::luma(color) ); 0098 m_lowThreshold.insert( key, result ); 0099 return result; 0100 0101 } 0102 } 0103 0104 bool OxygenHelper::highThreshold(const QColor &color) 0105 { 0106 const quint32 key( color.rgba() ); 0107 ColorMap::iterator iter( m_highThreshold.find( key ) ); 0108 if( iter != m_highThreshold.end() ) return iter.value(); 0109 else { 0110 0111 const QColor lighter( KColorScheme::shade(color, KColorScheme::LightShade, 0.5 ) ); 0112 const bool result( KColorUtils::luma(lighter) < KColorUtils::luma(color) ); 0113 m_highThreshold.insert( key, result ); 0114 return result; 0115 0116 } 0117 } 0118 0119 const QColor& OxygenHelper::backgroundTopColor(const QColor &color) 0120 { 0121 const quint64 key( color.rgba() ); 0122 QColor* out( m_backgroundTopColorCache.object( key ) ); 0123 if( !out ) 0124 { 0125 if( lowThreshold(color) ) out = new QColor( KColorScheme::shade(color, KColorScheme::MidlightShade, 0.0) ); 0126 else { 0127 const qreal my( KColorUtils::luma( KColorScheme::shade(color, KColorScheme::LightShade, 0.0) ) ); 0128 const qreal by( KColorUtils::luma(color) ); 0129 out = new QColor( KColorUtils::shade(color, (my - by) * _bgcontrast) ); 0130 } 0131 0132 m_backgroundTopColorCache.insert( key, out ); 0133 } 0134 0135 return *out; 0136 0137 } 0138 0139 const QColor& OxygenHelper::backgroundBottomColor(const QColor &color) 0140 { 0141 const quint64 key( color.rgba() ); 0142 QColor* out( m_backgroundBottomColorCache.object( key ) ); 0143 if( !out ) 0144 { 0145 const QColor midColor( KColorScheme::shade(color, KColorScheme::MidShade, 0.0) ); 0146 if( lowThreshold(color) ) out = new QColor( midColor ); 0147 else { 0148 0149 const qreal by( KColorUtils::luma(color) ); 0150 const qreal my( KColorUtils::luma(midColor) ); 0151 out = new QColor( KColorUtils::shade(color, (my - by) * _bgcontrast) ); 0152 0153 } 0154 0155 m_backgroundBottomColorCache.insert( key, out ); 0156 } 0157 0158 return *out; 0159 0160 } 0161 0162 const QColor& OxygenHelper::backgroundRadialColor(const QColor &color) 0163 { 0164 const quint64 key( color.rgba() ); 0165 QColor* out( m_backgroundRadialColorCache.object( key ) ); 0166 if( !out ) 0167 { 0168 if( lowThreshold(color) ) out = new QColor( KColorScheme::shade(color, KColorScheme::LightShade, 0.0) ); 0169 else if( highThreshold( color ) ) out = new QColor( color ); 0170 else out = new QColor( KColorScheme::shade(color, KColorScheme::LightShade, _bgcontrast) ); 0171 m_backgroundRadialColorCache.insert( key, out ); 0172 } 0173 0174 return *out; 0175 } 0176 0177 QPixmap OxygenHelper::verticalGradient(const QColor &color, int height, int offset) 0178 { 0179 const quint64 key( (quint64(color.rgba()) << 32) | height | 0x8000 ); 0180 QPixmap *pixmap( m_backgroundCache.object( key ) ); 0181 0182 if (!pixmap) 0183 { 0184 pixmap = new QPixmap(1, height); 0185 pixmap->fill( Qt::transparent ); 0186 0187 QLinearGradient gradient(0, offset, 0, height+offset); 0188 gradient.setColorAt(0.0, backgroundTopColor(color)); 0189 gradient.setColorAt(0.5, color); 0190 gradient.setColorAt(1.0, backgroundBottomColor(color)); 0191 0192 QPainter p(pixmap); 0193 p.setCompositionMode(QPainter::CompositionMode_Source); 0194 p.fillRect(pixmap->rect(), gradient); 0195 0196 p.end(); 0197 0198 m_backgroundCache.insert(key, pixmap); 0199 } 0200 0201 return *pixmap; 0202 } 0203 0204 QPixmap OxygenHelper::radialGradient(const QColor &color, int width, int height) 0205 { 0206 const quint64 key( ( quint64(color.rgba()) << 32) | width | 0xb000 ); 0207 QPixmap *pixmap( m_backgroundCache.object( key ) ); 0208 0209 if (!pixmap) 0210 { 0211 pixmap = new QPixmap(width, height); 0212 pixmap->fill(Qt::transparent); 0213 0214 QColor radialColor = backgroundRadialColor(color); 0215 radialColor.setAlpha(255); 0216 QRadialGradient gradient(64, height-64, 64); 0217 gradient.setColorAt(0, radialColor); 0218 radialColor.setAlpha(101); 0219 gradient.setColorAt(0.5, radialColor); 0220 radialColor.setAlpha(37); 0221 gradient.setColorAt(0.75, radialColor); 0222 radialColor.setAlpha(0); 0223 gradient.setColorAt(1, radialColor); 0224 0225 QPainter p(pixmap); 0226 p.scale(width/128.0,1); 0227 p.fillRect(QRect(0,0,128,height), gradient); 0228 0229 p.end(); 0230 0231 m_backgroundCache.insert(key, pixmap); 0232 } 0233 0234 return *pixmap; 0235 } 0236 0237 void OxygenHelper::renderOxygenWindowBackground(QPainter *p, const QRect &clipRect, 0238 const QWidget *widget, const QWidget* window, 0239 const QColor& color, int y_shift, 0240 int gradientHeight) 0241 { 0242 // get coordinates relative to the client area 0243 // this is stupid. One could use mapTo if this was taking const QWidget* and not 0244 // QWidget* as argument. 0245 const QWidget* w( widget ); 0246 int x(0); 0247 int y(-y_shift); 0248 0249 while ( w != window && !w->isWindow() && w != w->parentWidget() ) 0250 { 0251 x += w->geometry().x(); 0252 y += w->geometry().y(); 0253 w = w->parentWidget(); 0254 } 0255 0256 if (clipRect.isValid()) 0257 { 0258 p->save(); 0259 p->setClipRegion(clipRect,Qt::IntersectClip); 0260 } 0261 0262 // calculate upper part height 0263 // special tricks are needed 0264 // to handle both window contents and window decoration 0265 const QRect r = window->rect(); 0266 int height( window->frameGeometry().height() ); 0267 int width( window->frameGeometry().width() ); 0268 if( y_shift > 0 ) 0269 { 0270 height -= 2*y_shift; 0271 width -= 2*y_shift; 0272 } 0273 0274 const int splitY( qMin(300, (3*height)/4) ); 0275 0276 // draw upper linear gradient 0277 const QRect upperRect(-x, -y, r.width(), splitY); 0278 QPixmap tile( verticalGradient(color, splitY, gradientHeight-64) ); 0279 p->drawTiledPixmap(upperRect, tile); 0280 0281 // draw lower flat part 0282 const QRect lowerRect(-x, splitY-y, r.width(), r.height() - splitY-y_shift); 0283 p->fillRect(lowerRect, backgroundBottomColor(color)); 0284 0285 // draw upper radial gradient 0286 const int radialW( qMin(600, width) ); 0287 const QRect radialRect( (r.width() - radialW) / 2-x, -y, radialW, gradientHeight); 0288 if (clipRect.intersects(radialRect)) 0289 { 0290 tile = radialGradient(color, radialW, gradientHeight); 0291 p->drawPixmap(radialRect, tile); 0292 } 0293 0294 if (clipRect.isValid()) 0295 { p->restore(); } 0296 } 0297 0298 //!@name window background gradients 0299 //@{ 0300 /*! 0301 \par y_shift: shift the background gradient upwards, to fit with the windec 0302 \par gradientHeight: the height of the generated gradient. 0303 for different heights, the gradient is translated so that it is always at the same position from the bottom 0304 */ 0305 void OxygenHelper::renderWindowBackground(QPainter *p, const QRect &clipRect, 0306 const QWidget *widget, const QPalette & pal, 0307 int y_shift, int gradientHeight) 0308 { 0309 renderOxygenWindowBackground( 0310 p, clipRect, widget, widget->window(), 0311 pal.color( widget->window()->backgroundRole() ), 0312 y_shift, gradientHeight ); 0313 } 0314 0315 void OxygenHelper::renderMenuBackground( QPainter* p, const QRect& clipRect, 0316 const QWidget* widget, const QPalette& pal) 0317 { 0318 renderMenuBackground( p, clipRect, widget, pal.color( widget->window()->backgroundRole() ) ); 0319 } 0320 0321 void OxygenHelper::renderMenuBackground( QPainter* p, const QRect& clipRect, 0322 const QWidget* widget, const QColor& color ) 0323 { 0324 0325 // get coordinates relative to the client area 0326 // this is stupid. One could use mapTo if this was taking const QWidget* and not 0327 // QWidget* as argument. 0328 const QWidget* w( widget ); 0329 int x(0); 0330 int y(0); 0331 0332 while( !w->isWindow() && w != w->parentWidget() ) 0333 { 0334 x += w->geometry().x(); 0335 y += w->geometry().y(); 0336 w = w->parentWidget(); 0337 } 0338 0339 if (clipRect.isValid()) { 0340 p->save(); 0341 p->setClipRegion(clipRect,Qt::IntersectClip); 0342 } 0343 0344 // calculate upper part height 0345 // special tricks are needed 0346 // to handle both window contents and window decoration 0347 QRect r = w->rect(); 0348 const int height( w->frameGeometry().height() ); 0349 const int splitY( qMin(200, (3*height)/4) ); 0350 0351 const QRect upperRect( QRect(0, 0, r.width(), splitY) ); 0352 const QPixmap tile( verticalGradient(color, splitY) ); 0353 p->drawTiledPixmap(upperRect, tile); 0354 0355 const QRect lowerRect( 0,splitY, r.width(), r.height() - splitY ); 0356 p->fillRect(lowerRect, backgroundBottomColor(color)); 0357 0358 if (clipRect.isValid()) 0359 { p->restore(); } 0360 0361 } 0362 0363 // </oxygen> 0364 #endif // 0 0365 0366 class KexiMenuWidgetActionPrivate 0367 { 0368 public: 0369 KexiMenuWidgetActionPrivate() 0370 : persistentlySelected(false) 0371 { 0372 } 0373 bool persistentlySelected; 0374 }; 0375 0376 KexiMenuWidgetAction::KexiMenuWidgetAction(QObject *parent) 0377 : QAction(parent) 0378 , d(new KexiMenuWidgetActionPrivate) 0379 { 0380 } 0381 0382 KexiMenuWidgetAction::KexiMenuWidgetAction(const QString &text, QObject *parent) 0383 : QAction(text, parent) 0384 , d(new KexiMenuWidgetActionPrivate) 0385 { 0386 } 0387 0388 KexiMenuWidgetAction::KexiMenuWidgetAction(const QIcon &icon, const QString &text, 0389 QObject *parent) 0390 : QAction(icon, text, parent) 0391 , d(new KexiMenuWidgetActionPrivate) 0392 { 0393 } 0394 0395 KexiMenuWidgetAction::KexiMenuWidgetAction(KStandardAction::StandardAction id, QObject *parent) 0396 : QAction(parent) 0397 , d(new KexiMenuWidgetActionPrivate) 0398 { 0399 QScopedPointer<QAction> tmp(KStandardAction::create(id, 0, 0, 0)); 0400 setIcon(tmp->icon()); 0401 setText(tmp->text()); 0402 setShortcut(tmp->shortcut()); 0403 setToolTip(tmp->toolTip()); 0404 } 0405 0406 KexiMenuWidgetAction::~KexiMenuWidgetAction() 0407 { 0408 } 0409 0410 void KexiMenuWidgetAction::setPersistentlySelected(bool set) 0411 { 0412 if (set == d->persistentlySelected) 0413 return; 0414 //qDebug() << "^^^^" << objectName() << set; 0415 d->persistentlySelected = set; 0416 } 0417 0418 bool KexiMenuWidgetAction::persistentlySelected() const 0419 { 0420 return d->persistentlySelected; 0421 } 0422 0423 KexiMenuWidget *KexiMenuWidgetPrivate::mouseDown = 0; 0424 int KexiMenuWidgetPrivate::sloppyDelayTimer = 0; 0425 0426 void KexiMenuWidgetPrivate::init() 0427 { 0428 //! @todo KEXI3 port OxygenHelper 0429 #if 0 0430 oxygenHelper = q->style()->objectName() == "oxygen" ? new OxygenHelper : 0; 0431 bespin = oxygenHelper ? false : q->style()->objectName() == "bespin"; 0432 qtcurve = oxygenHelper ? false : q->style()->objectName() == "qtcurve"; 0433 #else 0434 bespin = q->style()->objectName() == "bespin"; 0435 qtcurve = q->style()->objectName() == "qtcurve"; 0436 #endif 0437 0438 #ifndef QT_NO_WHATSTHIS 0439 //q->setAttribute(Qt::WA_CustomWhatsThis); 0440 #endif 0441 //q->setAttribute(Qt::WA_X11NetWmWindowTypePopupMenu); 0442 defaultMenuAction = menuAction = new QAction(q); 0443 //menuAction->d_func()->menu = q; 0444 q->setMouseTracking(q->style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, q)); 0445 if (q->style()->styleHint(QStyle::SH_Menu_Scrollable, 0, q)) { 0446 scroll = new KexiMenuWidgetPrivate::QMenuScroller; 0447 scroll->scrollFlags = KexiMenuWidgetPrivate::QMenuScroller::ScrollNone; 0448 } 0449 0450 #ifdef QT_SOFTKEYS_ENABLED 0451 selectAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::SelectSoftKey, Qt::Key_Select, q); 0452 cancelAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::CancelSoftKey, Qt::Key_Back, q); 0453 selectAction->setPriority(QAction::HighPriority); 0454 cancelAction->setPriority(QAction::HighPriority); 0455 q->addAction(selectAction); 0456 q->addAction(cancelAction); 0457 #endif 0458 q->setFocusPolicy(Qt::StrongFocus); 0459 0460 smallTextFont = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont); 0461 0462 QVBoxLayout *vlyr = new QVBoxLayout(q); 0463 vlyr->setSpacing(0); 0464 vlyr->setMargin(0); 0465 vlyr->addStretch(1); 0466 0467 // social media section 0468 socialWidget = new QWidget; 0469 QHBoxLayout *socialLayout = new QHBoxLayout(socialWidget); 0470 socialLayout->setMargin(3); 0471 socialLayout->setSpacing(6); 0472 socialLayout->addStretch(1); 0473 QLabel *followUs = new QLabel(xi18n("Join us on:")); 0474 followUs->setFont(smallTextFont); 0475 followUs->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); 0476 socialLayout->addWidget(followUs, 1); 0477 0478 QToolButton *fbButton = new QToolButton; 0479 fbButton->setIcon(KexiIcon("im-facebook")); 0480 fbButton->setAutoRaise(true); 0481 fbButton->setCursor(Qt::PointingHandCursor); 0482 fbButton->setFocusPolicy(Qt::NoFocus); 0483 fbButton->setToolTip(xi18n("Visit KEXI Facebook page at %1", QLatin1String(facebookUrl))); 0484 QObject::connect(fbButton, &QPushButton::clicked, 0485 []() { QDesktopServices::openUrl(QUrl(facebookUrl)); }); 0486 0487 QToolButton *twButton = new QToolButton; 0488 twButton->setIcon(KexiIcon("im-twitter")); 0489 twButton->setAutoRaise(true); 0490 twButton->setCursor(Qt::PointingHandCursor); 0491 twButton->setFocusPolicy(Qt::NoFocus); 0492 twButton->setToolTip(xi18n("Visit KEXI Twitter page at %1", QLatin1String(twitterUrl))); 0493 QObject::connect(twButton, &QPushButton::clicked, 0494 []() { QDesktopServices::openUrl(QUrl(twitterUrl)); }); 0495 0496 socialLayout->addWidget(fbButton); 0497 socialLayout->addWidget(twButton); 0498 socialLayout->addStretch(1); 0499 vlyr->addWidget(socialWidget, 0 , Qt::AlignCenter); 0500 } 0501 0502 int KexiMenuWidgetPrivate::scrollerHeight() const 0503 { 0504 return qMax(QApplication::globalStrut().height(), q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q)); 0505 } 0506 0507 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't 0508 QRect KexiMenuWidgetPrivate::popupGeometry(const QWidget *widget) const 0509 { 0510 return QApplication::desktop()->screenGeometry(widget); 0511 } 0512 0513 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't 0514 QRect KexiMenuWidgetPrivate::popupGeometry(int screen) const 0515 { 0516 return QApplication::desktop()->screenGeometry(screen); 0517 } 0518 0519 QList<QPointer<QWidget> > KexiMenuWidgetPrivate::calcCausedStack() const 0520 { 0521 QList<QPointer<QWidget> > ret; 0522 for(QWidget *widget = causedPopup.widget; widget; ) { 0523 ret.append(widget); 0524 if (KexiMenuWidget *qmenu = qobject_cast<KexiMenuWidget*>(widget)) 0525 widget = qmenu->causedPopup().widget; 0526 else 0527 break; 0528 } 0529 return ret; 0530 } 0531 0532 void KexiMenuWidgetPrivate::updateActionRects() const 0533 { 0534 if (!itemsDirty) 0535 return; 0536 0537 q->ensurePolished(); 0538 0539 //let's reinitialize the buffer 0540 QList<QAction*> actionsList = q->actions(); 0541 actionRects.resize(actionsList.count()); 0542 actionRects.fill(QRect()); 0543 0544 //let's try to get the last visible action 0545 int lastVisibleAction = actionsList.count() - 1; 0546 for(;lastVisibleAction >= 0; --lastVisibleAction) { 0547 const QAction *action = actionsList.at(lastVisibleAction); 0548 if (action->isVisible()) { 0549 //removing trailing separators 0550 if (action->isSeparator() && collapsibleSeparators) 0551 continue; 0552 break; 0553 } 0554 } 0555 0556 int max_column_width = 0, 0557 dh = popupGeometry(q).height(), 0558 y = 0; 0559 QStyle *style = q->style(); 0560 QStyleOption opt; 0561 opt.initFrom(q); 0562 const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q), 0563 vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q), 0564 icone = KIconLoader::SizeMedium; 0565 //! @todo KEXI3 adjust this size for smaller displays 0566 //style->pixelMetric(QStyle::PM_SmallIconSize, &opt, q); 0567 const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q); 0568 const int deskFw = frameWidth(&opt); 0569 //const int tearoffHeight = tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0; 0570 0571 //for compatibility now - will have to refactor this away 0572 tabWidth = 0; 0573 maxIconWidth = 0; 0574 hasCheckableItems = false; 0575 ncols = 1; 0576 sloppyAction = 0; 0577 0578 for (int i = 0; i < actionsList.count(); ++i) { 0579 QAction *action = actionsList.at(i); 0580 if (action->isSeparator() || !action->isVisible() || widgetItems.contains(action)) 0581 continue; 0582 //..and some members 0583 hasCheckableItems |= action->isCheckable(); 0584 QIcon is = action->icon(); 0585 if (!is.isNull()) { 0586 maxIconWidth = qMax<int>(maxIconWidth, icone + 4); 0587 } 0588 } 0589 0590 //calculate size 0591 QFontMetrics qfm = q->fontMetrics(); 0592 bool previousWasSeparator = true; // this is true to allow removing the leading separators 0593 for(int i = 0; i <= lastVisibleAction; i++) { 0594 QAction *action = actionsList.at(i); 0595 0596 if (!action->isVisible() || 0597 (collapsibleSeparators && previousWasSeparator && action->isSeparator())) 0598 continue; // we continue, this action will get an empty QRect 0599 0600 previousWasSeparator = action->isSeparator(); 0601 0602 //let the style modify the above size.. 0603 QStyleOptionMenuItem opt; 0604 q->initStyleOption(&opt, action); 0605 const QFontMetrics &fm = opt.fontMetrics; 0606 0607 QSize sz; 0608 if (QWidget *w = widgetItems.value(action)) { 0609 sz = w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize()); 0610 } else { 0611 //calc what I think the size is.. 0612 if (action->isSeparator()) { 0613 sz = QSize(2, 2); 0614 } else { 0615 QString s = action->text(); 0616 int t = s.indexOf(QLatin1Char('\t')); 0617 if (t != -1) { 0618 tabWidth = qMax(int(tabWidth), qfm.width(s.mid(t+1))); 0619 s = s.left(t); 0620 #ifndef QT_NO_SHORTCUT 0621 } else { 0622 QKeySequence seq = action->shortcut(); 0623 if (!seq.isEmpty()) 0624 tabWidth = qMax(int(tabWidth), qfm.width(seq.toString())); 0625 #endif 0626 } 0627 sz.setWidth(fm.boundingRect(QRect(), Qt::TextSingleLine | Qt::TextShowMnemonic, s).width()); 0628 sz.setHeight(qMax(fm.height(), qfm.height())); 0629 0630 //(not need because iconless items have to be of the same size as icon ones): if (!is.isNull()) { 0631 QSize is_sz = QSize(icone, icone); 0632 if (is_sz.height() > sz.height()) 0633 sz.setHeight(is_sz.height()); 0634 //} 0635 } 0636 sz = style->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q); 0637 } 0638 0639 0640 if (!sz.isEmpty()) { 0641 max_column_width = qMax(max_column_width, sz.width()); 0642 //wrapping 0643 if (!scroll && 0644 y+sz.height()+vmargin > dh - (deskFw * 2)) { 0645 ncols++; 0646 y = vmargin; 0647 } 0648 y += sz.height(); 0649 //update the item 0650 actionRects[i] = QRect(0, 0, sz.width(), sz.height()); 0651 } 0652 } 0653 0654 max_column_width += tabWidth; //finally add in the tab width 0655 const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, &opt, QApplication::globalStrut(), q).width() - QApplication::globalStrut().width(); 0656 int leftmargin, topmargin, rightmargin, bottommargin; 0657 q->getContentsMargins(&leftmargin, &topmargin, &rightmargin, &bottommargin); 0658 const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin)); 0659 max_column_width = qMax(min_column_width, max_column_width); 0660 0661 0662 //calculate position 0663 const int base_y = vmargin + fw + topmargin + 0664 (scroll ? scroll->scrollOffset : 0) 0665 /*+ tearoffHeight*/; 0666 int x = hmargin + fw + leftmargin; 0667 y = base_y; 0668 0669 for(int i = 0; i < actionsList.count(); i++) { 0670 QRect &rect = actionRects[i]; 0671 if (rect.isNull()) 0672 continue; 0673 if (!scroll && 0674 y+rect.height() > dh - deskFw * 2) { 0675 x += max_column_width + hmargin; 0676 y = base_y; 0677 } 0678 rect.translate(x, y); //move 0679 rect.setWidth(max_column_width); //uniform width 0680 0681 //we need to update the widgets geometry 0682 if (QWidget *widget = widgetItems.value(actionsList.at(i))) { 0683 widget->setGeometry(rect); 0684 widget->setVisible(actionsList.at(i)->isVisible()); 0685 } 0686 0687 y += rect.height(); 0688 } 0689 itemsDirty = 0; 0690 } 0691 0692 QRect KexiMenuWidgetPrivate::actionRect(QAction *act) const 0693 { 0694 QList<QAction*> actionsList = q->actions(); 0695 int index = actionsList.indexOf(act); 0696 if (index == -1) 0697 return QRect(); 0698 0699 updateActionRects(); 0700 0701 //we found the action 0702 return actionRects.at(index); 0703 } 0704 0705 void KexiMenuWidgetPrivate::hideUpToMenuBar() 0706 { 0707 bool fadeMenus = q->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide); 0708 QWidget *caused = causedPopup.widget; 0709 hideMenu(q); //hide after getting causedPopup 0710 while(caused) { 0711 if (KexiMenuWidget *m = qobject_cast<KexiMenuWidget*>(caused)) { 0712 caused = m->causedPopup().widget; 0713 hideMenu(m, fadeMenus); 0714 if (!fadeMenus) // Mac doesn't clear the action until after hidden. 0715 m->setCurrentAction(0); 0716 } else { 0717 caused = 0; 0718 } 0719 } 0720 setCurrentAction(0); 0721 } 0722 0723 void KexiMenuWidgetPrivate::hideMenu(KexiMenuWidget *menu, bool justRegister) 0724 { 0725 Q_UNUSED(menu); 0726 Q_UNUSED(justRegister); 0727 } 0728 0729 #if 0 0730 void KexiMenuWidgetPrivate::popupAction(QAction *action, int delay, bool activateFirst) 0731 { 0732 Q_UNUSED(activateFirst); 0733 if (action && action->isEnabled()) { 0734 if (!delay) 0735 q->internalDelayedPopup(); 0736 else if (!menuDelayTimer.isActive() && (!action->menu() || !action->menu()->isVisible())) 0737 menuDelayTimer.start(delay, q); 0738 /* if (activateFirst && action->menu()) 0739 action->menu()->d_func()->setFirstActionActive();*/ 0740 } else if (KexiMenuWidget *menu = activeMenu) { //hide the current item 0741 activeMenu = 0; 0742 hideMenu(menu); 0743 } 0744 } 0745 #endif 0746 0747 void KexiMenuWidgetPrivate::setSyncAction() 0748 { 0749 QAction *current = currentAction; 0750 if(current && (!current->isEnabled() || current->menu() || current->isSeparator())) 0751 current = 0; 0752 for(QWidget *caused = q; caused;) { 0753 if (KexiMenuWidget *m = qobject_cast<KexiMenuWidget*>(caused)) { 0754 caused = m->causedPopup().widget; 0755 if (m->eventLoop()) 0756 m->setSyncAction(current); // synchronous operation 0757 } else { 0758 break; 0759 } 0760 } 0761 } 0762 0763 0764 void KexiMenuWidgetPrivate::setFirstActionActive() 0765 { 0766 updateActionRects(); 0767 QList<QAction*> actionsList = q->actions(); 0768 for(int i = 0, saccum = 0; i < actionsList.count(); i++) { 0769 const QRect &rect = actionRects.at(i); 0770 if (rect.isNull()) 0771 continue; 0772 if (scroll && scroll->scrollFlags & QMenuScroller::ScrollUp) { 0773 saccum -= rect.height(); 0774 if (saccum > scroll->scrollOffset - scrollerHeight()) 0775 continue; 0776 } 0777 QAction *act = actionsList.at(i); 0778 if (!act->isSeparator() && 0779 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q) 0780 || act->isEnabled())) { 0781 setCurrentAction(act); 0782 break; 0783 } 0784 } 0785 } 0786 0787 // popup == -1 means do not popup, 0 means immediately, others mean use a timer 0788 void KexiMenuWidgetPrivate::setCurrentAction(QAction *action, int popup, KexiMenuWidget::SelectionReason reason, bool activateFirst) 0789 { 0790 Q_UNUSED(activateFirst); 0791 //tearoffHighlighted = 0; 0792 if (currentAction) 0793 q->update(actionRect(currentAction)); 0794 0795 sloppyAction = 0; 0796 if (!sloppyRegion.isEmpty()) 0797 sloppyRegion = QRegion(); 0798 KexiMenuWidget *hideActiveMenu = activeMenu; 0799 #ifndef QT_NO_STATUSTIP 0800 QAction *previousAction = currentAction; 0801 #endif 0802 0803 currentAction = action; 0804 if (action) { 0805 if (!action->isSeparator()) { 0806 activateAction(action, QAction::Hover); 0807 /*if (popup != -1) { 0808 hideActiveMenu = 0; //will be done "later" 0809 // if the menu is visible then activate the required action, 0810 // otherwise we just mark the action as currentAction 0811 // and activate it when the menu will be popuped. 0812 if (q->isVisible()) 0813 popupAction(currentAction, popup, activateFirst); 0814 }*/ 0815 q->update(actionRect(action)); 0816 0817 if (reason == KexiMenuWidget::SelectedFromKeyboard) { 0818 QWidget *widget = widgetItems.value(action); 0819 if (widget) { 0820 if (widget->focusPolicy() != Qt::NoFocus) 0821 widget->setFocus(Qt::TabFocusReason); 0822 } else { 0823 //when the action has no QWidget, the KexiMenuWidget itself should 0824 // get the focus 0825 // Since the menu is a pop-up, it uses the popup reason. 0826 if (!q->hasFocus()) { 0827 q->setFocus(Qt::PopupFocusReason); 0828 } 0829 } 0830 } 0831 } else { //action is a separator 0832 if (popup != -1) 0833 hideActiveMenu = 0; //will be done "later" 0834 } 0835 #ifndef QT_NO_STATUSTIP 0836 } else if (previousAction) { 0837 //previousAction->d_func()->showStatusText(topCausedWidget(), QString()); 0838 #endif 0839 } 0840 if (hideActiveMenu) { 0841 activeMenu = 0; 0842 hideMenu(hideActiveMenu); 0843 } 0844 } 0845 0846 //return the top causedPopup.widget that is not a KexiMenuWidget 0847 QWidget *KexiMenuWidgetPrivate::topCausedWidget() const 0848 { 0849 QWidget* top = causedPopup.widget; 0850 while (KexiMenuWidget* m = qobject_cast<KexiMenuWidget *>(top)) 0851 top = m->causedPopup().widget; 0852 return top; 0853 } 0854 0855 QAction *KexiMenuWidgetPrivate::actionAt(QPoint p) const 0856 { 0857 if (!q->rect().contains(p)) //sanity check 0858 return 0; 0859 0860 QList<QAction*> actionsList = q->actions(); 0861 for(int i = 0; i < actionRects.count(); i++) { 0862 if (actionRects.at(i).contains(p)) 0863 return actionsList.at(i); 0864 } 0865 return 0; 0866 } 0867 0868 void KexiMenuWidgetPrivate::setOverrideMenuAction(QAction *a) 0869 { 0870 QObject::disconnect(menuAction, SIGNAL(destroyed()), q, SLOT(overrideMenuActionDestroyed())); 0871 if (a) { 0872 menuAction = a; 0873 QObject::connect(a, SIGNAL(destroyed()), q, SLOT(overrideMenuActionDestroyed())); 0874 } else { //we revert back to the default action created by the KexiMenuWidget itself 0875 menuAction = defaultMenuAction; 0876 } 0877 } 0878 0879 void KexiMenuWidget::overrideMenuActionDestroyed() 0880 { 0881 d->menuAction = d->defaultMenuAction; 0882 } 0883 0884 0885 void KexiMenuWidgetPrivate::updateLayoutDirection() 0886 { 0887 //we need to mimic the cause of the popup's layout direction 0888 //to allow setting it on a mainwindow for example 0889 //we call setLayoutDirection_helper to not overwrite a user-defined value 0890 if (!q->testAttribute(Qt::WA_SetLayoutDirection)) { 0891 if (QWidget *w = causedPopup.widget) 0892 setLayoutDirection_helper(w, w->layoutDirection()); 0893 else if (QWidget *w = q->parentWidget()) 0894 setLayoutDirection_helper(w, w->layoutDirection()); 0895 else 0896 setLayoutDirection_helper(w, QApplication::layoutDirection()); 0897 } 0898 } 0899 0900 bool KexiMenuWidgetPrivate::actionPersistentlySelected(const QAction* action) const 0901 { 0902 const KexiMenuWidgetAction* kaction = qobject_cast<const KexiMenuWidgetAction*>(action); 0903 return kaction ? kaction->persistentlySelected() : false; 0904 } 0905 0906 void KexiMenuWidgetPrivate::setActionPersistentlySelected(QAction* action, bool set) 0907 { 0908 KexiMenuWidgetAction* kaction = qobject_cast<KexiMenuWidgetAction*>(action); 0909 if (previousPersistentlySelectedAction) { 0910 previousPersistentlySelectedAction->setPersistentlySelected(false); 0911 q->update(actionRect(previousPersistentlySelectedAction)); 0912 } 0913 if (kaction) 0914 kaction->setPersistentlySelected(set); 0915 previousPersistentlySelectedAction = kaction; 0916 } 0917 0918 void KexiMenuWidgetPrivate::toggleActionPersistentlySelected(QAction* action) 0919 { 0920 KexiMenuWidgetAction* kaction = qobject_cast<KexiMenuWidgetAction*>(action); 0921 if (!kaction) 0922 return; 0923 setActionPersistentlySelected(kaction, !kaction->persistentlySelected()); 0924 } 0925 0926 /*! 0927 Returns the action associated with this menu. 0928 */ 0929 QAction *KexiMenuWidget::menuAction() const 0930 { 0931 return d->menuAction; 0932 } 0933 0934 /*! 0935 \property KexiMenuWidget::title 0936 \brief The title of the menu 0937 0938 This is equivalent to the QAction::text property of the menuAction(). 0939 0940 By default, this property contains an empty string. 0941 */ 0942 QString KexiMenuWidget::title() const 0943 { 0944 return d->menuAction->text(); 0945 } 0946 0947 void KexiMenuWidget::setTitle(const QString &text) 0948 { 0949 d->menuAction->setText(text); 0950 } 0951 0952 /*! 0953 \property KexiMenuWidget::icon 0954 0955 \brief The icon of the menu 0956 0957 This is equivalent to the QAction::icon property of the menuAction(). 0958 0959 By default, if no icon is explicitly set, this property contains a null icon. 0960 */ 0961 QIcon KexiMenuWidget::icon() const 0962 { 0963 return d->menuAction->icon(); 0964 } 0965 0966 void KexiMenuWidget::setIcon(const QIcon &icon) 0967 { 0968 d->menuAction->setIcon(icon); 0969 } 0970 0971 0972 //actually performs the scrolling 0973 void KexiMenuWidgetPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation location, bool active) 0974 { 0975 Q_UNUSED(action) 0976 Q_UNUSED(location) 0977 Q_UNUSED(active) 0978 #if 0 0979 if (!scroll || !scroll->scrollFlags) 0980 return; 0981 updateActionRects(); 0982 int newOffset = 0; 0983 const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollerHeight() : 0; 0984 const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0; 0985 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q); 0986 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); 0987 0988 QList<QAction*> actionsList = q->actions(); 0989 if (location == QMenuScroller::ScrollTop) { 0990 for(int i = 0, saccum = 0; i < actionsList.count(); i++) { 0991 if (actionsList.at(i) == action) { 0992 newOffset = topScroll - saccum; 0993 break; 0994 } 0995 saccum += actionRects.at(i).height(); 0996 } 0997 } else { 0998 for(int i = 0, saccum = 0; i < actionsList.count(); i++) { 0999 saccum += actionRects.at(i).height(); 1000 if (actionsList.at(i) == action) { 1001 if (location == QMenuScroller::ScrollCenter) 1002 newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll); 1003 else 1004 newOffset = (q->height() - botScroll) - saccum; 1005 break; 1006 } 1007 } 1008 if(newOffset) 1009 newOffset -= fw * 2; 1010 } 1011 1012 //figure out which scroll flags 1013 int newScrollFlags = QMenuScroller::ScrollNone; 1014 if (newOffset < 0) //easy and cheap one 1015 newScrollFlags |= QMenuScroller::ScrollUp; 1016 int saccum = newOffset; 1017 for(int i = 0; i < actionRects.count(); i++) { 1018 saccum += actionRects.at(i).height(); 1019 if (saccum > q->height()) { 1020 newScrollFlags |= QMenuScroller::ScrollDown; 1021 break; 1022 } 1023 } 1024 1025 if (!(newScrollFlags & QMenuScroller::ScrollDown) && (scroll->scrollFlags & QMenuScroller::ScrollDown)) { 1026 newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin; //last item at bottom 1027 } 1028 1029 if (!(newScrollFlags & QMenuScroller::ScrollUp) && (scroll->scrollFlags & QMenuScroller::ScrollUp)) { 1030 newOffset = 0; //first item at top 1031 } 1032 1033 if (newScrollFlags & QMenuScroller::ScrollUp) 1034 newOffset -= vmargin; 1035 1036 QRect screen = popupGeometry(q); 1037 const int desktopFrame = frameWidth(); 1038 if (q->height() < screen.height()-(desktopFrame*2)-1) { 1039 QRect geom = q->geometry(); 1040 if (newOffset > scroll->scrollOffset && (scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollUp)) { //scroll up 1041 const int newHeight = geom.height()-(newOffset-scroll->scrollOffset); 1042 if(newHeight > geom.height()) 1043 geom.setHeight(newHeight); 1044 } else if(scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollDown) { 1045 int newTop = geom.top() + (newOffset-scroll->scrollOffset); 1046 if (newTop < desktopFrame+screen.top()) 1047 newTop = desktopFrame+screen.top(); 1048 if (newTop < geom.top()) { 1049 geom.setTop(newTop); 1050 newOffset = 0; 1051 newScrollFlags &= ~QMenuScroller::ScrollUp; 1052 } 1053 } 1054 if (geom.bottom() > screen.bottom() - desktopFrame) 1055 geom.setBottom(screen.bottom() - desktopFrame); 1056 if (geom.top() < desktopFrame+screen.top()) 1057 geom.setTop(desktopFrame+screen.top()); 1058 if (geom != q->geometry()) { 1059 #if 0 1060 if (newScrollFlags & QMenuScroller::ScrollDown && 1061 q->geometry().top() - geom.top() >= -newOffset) 1062 newScrollFlags &= ~QMenuScroller::ScrollDown; 1063 #endif 1064 q->setGeometry(geom); 1065 } 1066 } 1067 1068 //actually update flags 1069 const int delta = qMin(0, newOffset) - scroll->scrollOffset; //make sure the new offset is always negative 1070 if (!itemsDirty && delta) { 1071 //we've scrolled so we need to update the action rects 1072 for (int i = 0; i < actionRects.count(); ++i) { 1073 QRect ¤t = actionRects[i]; 1074 current.moveTop(current.top() + delta); 1075 1076 //we need to update the widgets geometry 1077 if (QWidget *w = widgetItems.value(actionsList.at(i))) 1078 w->setGeometry(current); 1079 } 1080 } 1081 scroll->scrollOffset += delta; 1082 scroll->scrollFlags = newScrollFlags; 1083 if (active) 1084 setCurrentAction(action); 1085 1086 q->update(); //issue an update so we see all the new state.. 1087 #endif 1088 } 1089 1090 void KexiMenuWidgetPrivate::scrollMenu(QMenuScroller::ScrollLocation location, bool active) 1091 { 1092 Q_UNUSED(location) 1093 Q_UNUSED(active) 1094 #if 0 1095 updateActionRects(); 1096 QList<QAction*> actionsList = q->actions(); 1097 if(location == QMenuScroller::ScrollBottom) { 1098 for(int i = actionsList.size()-1; i >= 0; --i) { 1099 QAction *act = actionsList.at(i); 1100 if (actionRects.at(i).isNull()) 1101 continue; 1102 if (!act->isSeparator() && 1103 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q) 1104 || act->isEnabled())) { 1105 if(scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollDown) 1106 scrollMenu(act, KexiMenuWidgetPrivate::QMenuScroller::ScrollBottom, active); 1107 else if(active) 1108 setCurrentAction(act, /*popup*/-1, KexiMenuWidget::SelectedFromKeyboard); 1109 break; 1110 } 1111 } 1112 } else if(location == QMenuScroller::ScrollTop) { 1113 for(int i = 0; i < actionsList.size(); ++i) { 1114 QAction *act = actionsList.at(i); 1115 if (actionRects.at(i).isNull()) 1116 continue; 1117 if (!act->isSeparator() && 1118 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q) 1119 || act->isEnabled())) { 1120 if(scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollUp) 1121 scrollMenu(act, KexiMenuWidgetPrivate::QMenuScroller::ScrollTop, active); 1122 else if(active) 1123 setCurrentAction(act, /*popup*/-1, KexiMenuWidget::SelectedFromKeyboard); 1124 break; 1125 } 1126 } 1127 } 1128 #endif 1129 } 1130 1131 //only directional 1132 void KexiMenuWidgetPrivate::scrollMenu(QMenuScroller::ScrollDirection direction, bool page, bool active) 1133 { 1134 Q_UNUSED(direction) 1135 Q_UNUSED(page) 1136 Q_UNUSED(active) 1137 #if 0 1138 if (!scroll || !(scroll->scrollFlags & direction)) //not really possible... 1139 return; 1140 updateActionRects(); 1141 const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollerHeight() : 0; 1142 const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0; 1143 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q); 1144 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); 1145 const int offset = topScroll ? topScroll-vmargin : 0; 1146 QList<QAction*> actionsList = q->actions(); 1147 if (direction == QMenuScroller::ScrollUp) { 1148 for(int i = 0, saccum = 0; i < actionsList.count(); i++) { 1149 saccum -= actionRects.at(i).height(); 1150 if (saccum <= scroll->scrollOffset-offset) { 1151 scrollMenu(actionsList.at(i), page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active); 1152 break; 1153 } 1154 } 1155 } else if (direction == QMenuScroller::ScrollDown) { 1156 bool scrolled = false; 1157 for(int i = 0, saccum = 0; i < actionsList.count(); i++) { 1158 const int iHeight = actionRects.at(i).height(); 1159 saccum -= iHeight; 1160 if (saccum <= scroll->scrollOffset-offset) { 1161 const int scrollerArea = q->height() - botScroll - fw*2; 1162 int visible = (scroll->scrollOffset-offset) - saccum; 1163 for(i++ ; i < actionsList.count(); i++) { 1164 visible += actionRects.at(i).height(); 1165 if (visible > scrollerArea - topScroll) { 1166 scrolled = true; 1167 scrollMenu(actionsList.at(i), page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active); 1168 break; 1169 } 1170 } 1171 break; 1172 } 1173 } 1174 if(!scrolled) { 1175 scroll->scrollFlags &= ~QMenuScroller::ScrollDown; 1176 q->update(); 1177 } 1178 } 1179 #endif 1180 } 1181 1182 /* This is poor-mans eventfilters. This avoids the use of 1183 eventFilter (which can be nasty for users of QMenuBar's). */ 1184 bool KexiMenuWidgetPrivate::mouseEventTaken(QMouseEvent *e) 1185 { 1186 QPoint pos = q->mapFromGlobal(e->globalPos()); 1187 if (scroll && !activeMenu) { //let the scroller "steal" the event 1188 bool isScroll = false; 1189 if (pos.x() >= 0 && pos.x() < q->width()) { 1190 for(int dir = QMenuScroller::ScrollUp; dir <= QMenuScroller::ScrollDown; dir = dir << 1) { 1191 if (scroll->scrollFlags & dir) { 1192 if (dir == QMenuScroller::ScrollUp) 1193 isScroll = (pos.y() <= scrollerHeight()); 1194 else if (dir == QMenuScroller::ScrollDown) 1195 isScroll = (pos.y() >= q->height() - scrollerHeight()); 1196 if (isScroll) { 1197 scroll->scrollDirection = dir; 1198 break; 1199 } 1200 } 1201 } 1202 } 1203 if (isScroll) { 1204 scroll->scrollTimer.start(50, q); 1205 return true; 1206 } else { 1207 scroll->scrollTimer.stop(); 1208 } 1209 } 1210 1211 // if (tearoff) { //let the tear off thingie "steal" the event.. 1212 // QRect tearRect(0, 0, q->width(), q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q)); 1213 // if (scroll && scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollUp) 1214 // tearRect.translate(0, scrollerHeight()); 1215 // q->update(tearRect); 1216 // if (tearRect.contains(pos) && hasMouseMoved(e->globalPos())) { 1217 // setCurrentAction(0); 1218 // tearoffHighlighted = 1; 1219 // if (e->type() == QEvent::MouseButtonRelease) { 1220 // hideUpToMenuBar(); 1221 // } 1222 // return true; 1223 // } 1224 // tearoffHighlighted = 0; 1225 // } 1226 1227 if (q->frameGeometry().contains(e->globalPos())) //otherwise if the event is in our rect we want it.. 1228 return false; 1229 1230 for(QWidget *caused = causedPopup.widget; caused;) { 1231 bool passOnEvent = false; 1232 QWidget *next_widget = 0; 1233 QPoint cpos = caused->mapFromGlobal(e->globalPos()); 1234 if (KexiMenuWidget *m = qobject_cast<KexiMenuWidget*>(caused)) { 1235 passOnEvent = m->rect().contains(cpos); 1236 next_widget = m->causedPopup().widget; 1237 } 1238 if (passOnEvent) { 1239 if(e->type() != QEvent::MouseButtonRelease || mouseDown == caused) { 1240 QMouseEvent new_e(e->type(), cpos, e->button(), e->buttons(), e->modifiers()); 1241 QApplication::sendEvent(caused, &new_e); 1242 return true; 1243 } 1244 } 1245 if (!next_widget) 1246 break; 1247 caused = next_widget; 1248 } 1249 return false; 1250 } 1251 1252 void KexiMenuWidgetPrivate::activateCausedStack(const QList<QPointer<QWidget> > &causedStack, QAction *action, QAction::ActionEvent action_e, bool self) 1253 { 1254 KexiUtils::BoolBlocker guard(&activationRecursionGuard, true); 1255 if(self) 1256 action->activate(action_e); 1257 1258 for(int i = 0; i < causedStack.size(); ++i) { 1259 QPointer<QWidget> widget = causedStack.at(i); 1260 if (!widget) 1261 continue; 1262 //fire 1263 if (KexiMenuWidget *qmenu = qobject_cast<KexiMenuWidget*>(widget)) { 1264 widget = qmenu->causedPopup().widget; 1265 if (action_e == QAction::Trigger) { 1266 emit qmenu->triggered(action); 1267 } else if (action_e == QAction::Hover) { 1268 emit qmenu->hovered(action); 1269 } 1270 } 1271 } 1272 } 1273 1274 void KexiMenuWidgetPrivate::activateAction(QAction *action, QAction::ActionEvent action_e, bool self) 1275 { 1276 #ifndef QT_NO_WHATSTHIS 1277 bool inWhatsThisMode = QWhatsThis::inWhatsThisMode(); 1278 #endif 1279 if (!action || !q->isEnabled() 1280 || (action_e == QAction::Trigger 1281 #ifndef QT_NO_WHATSTHIS 1282 && !inWhatsThisMode 1283 #endif 1284 && (action->isSeparator() ||!action->isEnabled()))) 1285 return; 1286 1287 /* I have to save the caused stack here because it will be undone after popup execution (ie in the hide). 1288 Then I iterate over the list to actually send the events. --Sam 1289 */ 1290 const QList<QPointer<QWidget> > causedStack = calcCausedStack(); 1291 if (action_e == QAction::Trigger) { 1292 #ifndef QT_NO_WHATSTHIS 1293 if (!inWhatsThisMode) 1294 actionAboutToTrigger = action; 1295 #endif 1296 1297 if (q->testAttribute(Qt::WA_DontShowOnScreen)) { 1298 hideUpToMenuBar(); 1299 } else { 1300 for(QWidget *widget = QApplication::activePopupWidget(); widget; ) { 1301 if (KexiMenuWidget *qmenu = qobject_cast<KexiMenuWidget*>(widget)) { 1302 if(qmenu == q) 1303 hideUpToMenuBar(); 1304 widget = qmenu->causedPopup().widget; 1305 } else { 1306 break; 1307 } 1308 } 1309 } 1310 1311 #ifndef QT_NO_WHATSTHIS 1312 if (inWhatsThisMode) { 1313 QString s = action->whatsThis(); 1314 if (s.isEmpty()) 1315 s = q->whatsThis(); 1316 QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q); 1317 return; 1318 } 1319 #endif 1320 } 1321 1322 1323 activateCausedStack(causedStack, action, action_e, self); 1324 1325 1326 if (action_e == QAction::Hover) { 1327 #ifndef QT_NO_ACCESSIBILITY 1328 if (QAccessible::isActive()) { 1329 int actionIndex = indexOf(action) + 1; 1330 QAccessibleEvent focusEvent(q, QAccessible::Focus); 1331 focusEvent.setChild(actionIndex); 1332 QAccessible::updateAccessibility(&focusEvent); 1333 QAccessibleEvent selectionEvent(q, QAccessible::Selection); 1334 selectionEvent.setChild(actionIndex); 1335 QAccessible::updateAccessibility(&selectionEvent); 1336 } 1337 #endif 1338 action->showStatusText(topCausedWidget()); 1339 } else { 1340 actionAboutToTrigger = 0; 1341 } 1342 } 1343 1344 void KexiMenuWidget::actionTriggered() 1345 { 1346 if (QAction *action = qobject_cast<QAction *>(sender())) { 1347 QPointer<QAction> actionGuard = action; 1348 emit triggered(action); 1349 1350 if (!d->activationRecursionGuard && actionGuard) { 1351 //in case the action has not been activated by the mouse 1352 //we check the parent hierarchy 1353 QList< QPointer<QWidget> > list; 1354 for(QWidget *widget = parentWidget(); widget; ) { 1355 if (qobject_cast<KexiMenuWidget*>(widget)) 1356 { 1357 list.append(widget); 1358 widget = widget->parentWidget(); 1359 } else { 1360 break; 1361 } 1362 } 1363 d->activateCausedStack(list, action, QAction::Trigger, false); 1364 } 1365 } 1366 } 1367 1368 void KexiMenuWidget::actionHovered() 1369 { 1370 if (QAction * action = qobject_cast<QAction *>(sender())) { 1371 emit hovered(action); 1372 } 1373 } 1374 1375 bool KexiMenuWidgetPrivate::hasMouseMoved(const QPoint &globalPos) 1376 { 1377 //determines if the mouse has moved (ie its initial position has 1378 //changed by more than QApplication::startDragDistance() 1379 //or if there were at least 6 mouse motions) 1380 return motions > 6 || 1381 QApplication::startDragDistance() < (mousePopupPos - globalPos).manhattanLength(); 1382 } 1383 1384 // from QWidgetPrivate 1385 void KexiMenuWidgetPrivate::setLayoutDirection_helper(QWidget* w, Qt::LayoutDirection direction) 1386 { 1387 if (!w || (direction == Qt::RightToLeft) == w->testAttribute(Qt::WA_RightToLeft)) 1388 return; 1389 w->setAttribute(Qt::WA_RightToLeft, (direction == Qt::RightToLeft)); 1390 foreach (QObject *obj, w->children()) { 1391 QWidget *widget = qobject_cast<QWidget*>(obj); 1392 if (widget && !widget->isWindow() && !widget->testAttribute(Qt::WA_SetLayoutDirection)) 1393 setLayoutDirection_helper(widget, direction); 1394 } 1395 QEvent e(QEvent::LayoutDirectionChange); 1396 QApplication::sendEvent(w, &e); 1397 } 1398 1399 int KexiMenuWidgetPrivate::frameWidth(const QStyleOption* opt) const 1400 { 1401 if (!hasFrame) 1402 return 0; 1403 return q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, opt, q); 1404 } 1405 1406 bool KexiMenuWidget::hasFrame() const 1407 { 1408 return d->hasFrame; 1409 } 1410 1411 void KexiMenuWidget::setFrame(bool set) 1412 { 1413 d->hasFrame = set; 1414 } 1415 1416 // void QWidgetPrivate::resolveLayoutDirection() 1417 // { 1418 // Q_Q(const QWidget); 1419 // if (!q->testAttribute(Qt::WA_SetLayoutDirection)) 1420 // setLayoutDirection_helper(q->isWindow() ? QApplication::layoutDirection() : q->parentWidget()->layoutDirection()); 1421 // } 1422 1423 /*! 1424 Initialize \a option with the values from this menu and information from \a action. This method 1425 is useful for subclasses when they need a QStyleOptionMenuItem, but don't want 1426 to fill in all the information themselves. 1427 1428 \sa QStyleOption::initFrom() QMenuBar::initStyleOption() 1429 */ 1430 void KexiMenuWidget::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const 1431 { 1432 if (!option || !action) 1433 return; 1434 1435 option->initFrom(this); 1436 option->palette = palette(); 1437 option->state = QStyle::State_None; 1438 1439 if (window()->isActiveWindow()) 1440 option->state |= QStyle::State_Active; 1441 if (isEnabled() && action->isEnabled() 1442 && (!action->menu() || action->menu()->isEnabled())) 1443 option->state |= QStyle::State_Enabled; 1444 else 1445 option->palette.setCurrentColorGroup(QPalette::Disabled); 1446 1447 option->font = action->font().resolve(font()); 1448 option->fontMetrics = QFontMetrics(option->font); 1449 1450 if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) { 1451 option->state |= QStyle::State_Selected 1452 | (d->mouseDown ? QStyle::State_Sunken : QStyle::State_None); 1453 } 1454 1455 option->menuHasCheckableItems = d->hasCheckableItems; 1456 if (!action->isCheckable()) { 1457 option->checkType = QStyleOptionMenuItem::NotCheckable; 1458 } else { 1459 option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive()) 1460 ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive; 1461 option->checked = action->isChecked(); 1462 } 1463 if (action->menu()) 1464 option->menuItemType = QStyleOptionMenuItem::SubMenu; 1465 else if (action->isSeparator()) 1466 option->menuItemType = QStyleOptionMenuItem::Separator; 1467 else if (d->defaultAction == action) 1468 option->menuItemType = QStyleOptionMenuItem::DefaultItem; 1469 else 1470 option->menuItemType = QStyleOptionMenuItem::Normal; 1471 if (action->isIconVisibleInMenu()) 1472 option->icon = action->icon(); 1473 QString textAndAccel = action->text(); 1474 #ifndef QT_NO_SHORTCUT 1475 if (textAndAccel.indexOf(QLatin1Char('\t')) == -1) { 1476 QKeySequence seq = action->shortcut(); 1477 if (!seq.isEmpty()) 1478 textAndAccel += QLatin1Char('\t') + seq.toString(); 1479 } 1480 #endif 1481 option->text = textAndAccel; 1482 option->tabWidth = d->tabWidth; 1483 option->maxIconWidth = d->maxIconWidth; 1484 option->menuRect = rect(); 1485 } 1486 1487 /*! 1488 Constructs a menu with parent \a parent. 1489 1490 Although a popup menu is always a top-level widget, if a parent is 1491 passed the popup menu will be deleted when that parent is 1492 destroyed (as with any other QObject). 1493 */ 1494 KexiMenuWidget::KexiMenuWidget(QWidget *parent) 1495 : QWidget(parent), d(new KexiMenuWidgetPrivate(this)) 1496 { 1497 d->init(); 1498 } 1499 1500 /*! 1501 Constructs a menu with a \a title and a \a parent. 1502 1503 Although a popup menu is always a top-level widget, if a parent is 1504 passed the popup menu will be deleted when that parent is 1505 destroyed (as with any other QObject). 1506 1507 \sa title 1508 */ 1509 KexiMenuWidget::KexiMenuWidget(const QString &title, QWidget *parent) 1510 : QWidget(parent), d(new KexiMenuWidgetPrivate(this)) 1511 { 1512 d->init(); 1513 d->menuAction->setText(title); 1514 } 1515 1516 /*! \internal 1517 */ 1518 // KexiMenuWidget::KexiMenuWidget(KexiMenuWidgetPrivate &dd, QWidget *parent) 1519 // : QWidget(dd, parent, Qt::Popup) 1520 // { 1521 // d->init(); 1522 // } 1523 1524 /*! 1525 Destroys the menu. 1526 */ 1527 KexiMenuWidget::~KexiMenuWidget() 1528 { 1529 if (!d->widgetItems.isEmpty()) { // avoid detach on shared null hash 1530 QHash<QAction *, QWidget *>::iterator it = d->widgetItems.begin(); 1531 for (; it != d->widgetItems.end(); ++it) { 1532 if (QWidget *widget = it.value()) { 1533 QWidgetAction *action = static_cast<QWidgetAction *>(it.key()); 1534 action->releaseWidget(widget); 1535 *it = 0; 1536 } 1537 } 1538 } 1539 1540 if (d->eventLoop) 1541 d->eventLoop->exit(); 1542 //hideTearOffMenu(); 1543 delete d; 1544 } 1545 1546 KexiMenuWidgetAction* KexiMenuWidget::persistentlySelectedAction() const 1547 { 1548 if (d->previousPersistentlySelectedAction 1549 && d->previousPersistentlySelectedAction->persistentlySelected()) 1550 { 1551 return d->previousPersistentlySelectedAction; 1552 } 1553 return 0; 1554 } 1555 1556 void KexiMenuWidget::setPersistentlySelectedAction(KexiMenuWidgetAction* action, bool set) 1557 { 1558 d->setActionPersistentlySelected(action, set); 1559 } 1560 1561 const KexiMenuWidgetCaused& KexiMenuWidget::causedPopup() const 1562 { 1563 return d->causedPopup; 1564 } 1565 1566 void KexiMenuWidget::setCurrentAction(QAction *a, int popup, KexiMenuWidget::SelectionReason reason, bool activateFirst) 1567 { 1568 d->setCurrentAction(a, popup, reason, activateFirst); 1569 } 1570 1571 QEventLoop *KexiMenuWidget::eventLoop() const 1572 { 1573 return d->eventLoop; 1574 } 1575 1576 void KexiMenuWidget::setSyncAction(QAction *a) 1577 { 1578 d->syncAction = a; 1579 } 1580 1581 /*! 1582 \overload 1583 1584 This convenience function creates a new action with \a text. 1585 The function adds the newly created action to the menu's 1586 list of actions, and returns it. 1587 1588 \sa QWidget::addAction() 1589 */ 1590 QAction *KexiMenuWidget::addAction(const QString &text) 1591 { 1592 QAction *ret = new QAction(text, this); 1593 addAction(ret); 1594 return ret; 1595 } 1596 1597 /*! 1598 \overload 1599 1600 This convenience function creates a new action with an \a icon 1601 and some \a text. The function adds the newly created action to 1602 the menu's list of actions, and returns it. 1603 1604 \sa QWidget::addAction() 1605 */ 1606 QAction *KexiMenuWidget::addAction(const QIcon &icon, const QString &text) 1607 { 1608 QAction *ret = new QAction(icon, text, this); 1609 addAction(ret); 1610 return ret; 1611 } 1612 1613 /*! 1614 \overload 1615 1616 This convenience function creates a new action with the text \a 1617 text and an optional shortcut \a shortcut. The action's 1618 QAction::triggered() signal is connected to the 1619 \a receiver's \a member slot. The function adds the newly created 1620 action to the menu's list of actions and returns it. 1621 1622 \sa QWidget::addAction() 1623 */ 1624 QAction *KexiMenuWidget::addAction(const QString &text, const QObject *receiver, const char* member, const QKeySequence &shortcut) 1625 { 1626 QAction *action = new QAction(text, this); 1627 #ifdef QT_NO_SHORTCUT 1628 Q_UNUSED(shortcut); 1629 #else 1630 action->setShortcut(shortcut); 1631 #endif 1632 QObject::connect(action, SIGNAL(triggered(bool)), receiver, member); 1633 addAction(action); 1634 return action; 1635 } 1636 1637 /*! 1638 \overload 1639 1640 This convenience function creates a new action with an \a icon and 1641 some \a text and an optional shortcut \a shortcut. The action's 1642 QAction::triggered() signal is connected to the 1643 \a member slot of the \a receiver object. The function adds the 1644 newly created action to the menu's list of actions, and returns it. 1645 1646 \sa QWidget::addAction() 1647 */ 1648 QAction *KexiMenuWidget::addAction(const QIcon &icon, const QString &text, const QObject *receiver, 1649 const char* member, const QKeySequence &shortcut) 1650 { 1651 QAction *action = new QAction(icon, text, this); 1652 #ifdef QT_NO_SHORTCUT 1653 Q_UNUSED(shortcut); 1654 #else 1655 action->setShortcut(shortcut); 1656 #endif 1657 QObject::connect(action, SIGNAL(triggered(bool)), receiver, member); 1658 addAction(action); 1659 return action; 1660 } 1661 1662 /*! 1663 This convenience function creates a new separator action, i.e. an 1664 action with QAction::isSeparator() returning true, and adds the new 1665 action to this menu's list of actions. It returns the newly 1666 created action. 1667 1668 \sa QWidget::addAction() 1669 */ 1670 QAction *KexiMenuWidget::addSeparator() 1671 { 1672 QAction *action = new QAction(this); 1673 action->setSeparator(true); 1674 addAction(action); 1675 return action; 1676 } 1677 1678 /*! 1679 This convenience function creates a new separator action, i.e. an 1680 action with QAction::isSeparator() returning true. The function inserts 1681 the newly created action into this menu's list of actions before 1682 action \a before and returns it. 1683 1684 \sa QWidget::insertAction(), addSeparator() 1685 */ 1686 QAction *KexiMenuWidget::insertSeparator(QAction *before) 1687 { 1688 QAction *action = new QAction(this); 1689 action->setSeparator(true); 1690 insertAction(before, action); 1691 return action; 1692 } 1693 1694 /*! 1695 This sets the default action to \a act. The default action may have 1696 a visual cue, depending on the current QStyle. A default action 1697 usually indicates what will happen by default when a drop occurs. 1698 1699 \sa defaultAction() 1700 */ 1701 void KexiMenuWidget::setDefaultAction(QAction *act) 1702 { 1703 d->defaultAction = act; 1704 } 1705 1706 /*! 1707 Returns the current default action. 1708 1709 \sa setDefaultAction() 1710 */ 1711 QAction *KexiMenuWidget::defaultAction() const 1712 { 1713 return d->defaultAction; 1714 } 1715 1716 /*! 1717 Sets the currently highlighted action to \a act. 1718 */ 1719 void KexiMenuWidget::setActiveAction(QAction *act) 1720 { 1721 d->setCurrentAction(act, 0); 1722 if (d->scroll) 1723 d->scrollMenu(act, KexiMenuWidgetPrivate::QMenuScroller::ScrollCenter); 1724 } 1725 1726 1727 /*! 1728 Returns the currently highlighted action, or 0 if no 1729 action is currently highlighted. 1730 */ 1731 QAction *KexiMenuWidget::activeAction() const 1732 { 1733 return d->currentAction; 1734 } 1735 1736 /*! 1737 Returns true if there are no visible actions inserted into the menu, false 1738 otherwise. 1739 1740 \sa QWidget::actions() 1741 */ 1742 bool KexiMenuWidget::isEmpty() const 1743 { 1744 bool ret = true; 1745 QList<QAction*> actionsList = this->actions(); 1746 for(int i = 0; ret && i < actionsList.count(); ++i) { 1747 const QAction *action = actionsList.at(i); 1748 if (!action->isSeparator() && action->isVisible()) { 1749 ret = false; 1750 } 1751 } 1752 return ret; 1753 } 1754 1755 /*! 1756 Removes all the menu's actions. Actions owned by the menu and not 1757 shown in any other widget are deleted. 1758 1759 \sa removeAction() 1760 */ 1761 void KexiMenuWidget::clear() 1762 { 1763 QList<QAction*> acts = actions(); 1764 1765 for(int i = 0; i < acts.size(); i++) { 1766 #ifdef QT_SOFTKEYS_ENABLED 1767 // Lets not touch to our internal softkey actions 1768 if(acts[i] == d->selectAction || acts[i] == d->cancelAction) 1769 continue; 1770 #endif 1771 removeAction(acts[i]); 1772 if (acts[i]->parent() == this && acts[i]->associatedWidgets().isEmpty()) 1773 delete acts[i]; 1774 } 1775 } 1776 1777 /*! 1778 If a menu does not fit on the screen it lays itself out so that it 1779 does fit. It is style dependent what layout means (for example, on 1780 Windows it will use multiple columns). 1781 1782 This functions returns the number of columns necessary. 1783 */ 1784 int KexiMenuWidget::columnCount() const 1785 { 1786 return d->ncols; 1787 } 1788 1789 /*! 1790 Returns the item at \a pt; returns 0 if there is no item there. 1791 */ 1792 QAction *KexiMenuWidget::actionAt(const QPoint &pt) const 1793 { 1794 if (QAction *ret = d->actionAt(pt)) 1795 return ret; 1796 return 0; 1797 } 1798 1799 /*! 1800 Returns the geometry of action \a act. 1801 */ 1802 QRect KexiMenuWidget::actionGeometry(QAction *act) const 1803 { 1804 return d->actionRect(act); 1805 } 1806 1807 /*! 1808 \reimp 1809 */ 1810 QSize KexiMenuWidget::sizeHint() const 1811 { 1812 d->updateActionRects(); 1813 1814 QSize s; 1815 for (int i = 0; i < d->actionRects.count(); ++i) { 1816 const QRect &rect = d->actionRects.at(i); 1817 if (rect.isNull()) 1818 continue; 1819 if (rect.bottom() >= s.height()) 1820 s.setHeight(rect.y() + rect.height()); 1821 if (rect.right() >= s.width()) 1822 s.setWidth(rect.x() + rect.width()); 1823 } 1824 // Note that the action rects calculated above already include 1825 // the top and left margins, so we only need to add margins for 1826 // the bottom and right. 1827 QStyleOption opt(0); 1828 opt.initFrom(this); 1829 const int fw = d->frameWidth(&opt); 1830 int leftmargin, topmargin, rightmargin, bottommargin; 1831 getContentsMargins(&leftmargin, &topmargin, &rightmargin, &bottommargin); 1832 s.rwidth() += style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this) 1833 + fw + rightmargin + 2 /*frame*/; 1834 s.rheight() += style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) + fw + bottommargin; 1835 1836 return style()->sizeFromContents(QStyle::CT_Menu, &opt, 1837 s.expandedTo(QApplication::globalStrut()), this); 1838 } 1839 1840 /*! 1841 Displays the menu so that the action \a atAction will be at the 1842 specified \e global position \a p. To translate a widget's local 1843 coordinates into global coordinates, use QWidget::mapToGlobal(). 1844 1845 When positioning a menu with exec() or popup(), bear in mind that 1846 you cannot rely on the menu's current size(). For performance 1847 reasons, the menu adapts its size only when necessary, so in many 1848 cases, the size before and after the show is different. Instead, 1849 use sizeHint() which calculates the proper size depending on the 1850 menu's current contents. 1851 1852 \sa QWidget::mapToGlobal(), exec() 1853 */ 1854 void KexiMenuWidget::popup(const QPoint &p, QAction *atAction) 1855 { 1856 #ifndef Q_OS_SYMBIAN 1857 if (d->scroll) { // reset scroll state from last popup 1858 d->scroll->scrollOffset = 0; 1859 d->scroll->scrollFlags = KexiMenuWidgetPrivate::QMenuScroller::ScrollNone; 1860 } 1861 #endif 1862 //d->tearoffHighlighted = 0; 1863 d->motions = 0; 1864 //d->doChildEffects = true; 1865 d->updateLayoutDirection(); 1866 1867 ensurePolished(); // Get the right font 1868 emit aboutToShow(); 1869 //const bool actionListChanged = d->itemsDirty; 1870 d->updateActionRects(); 1871 QPoint pos; 1872 /*QPushButton *causedButton = qobject_cast<QPushButton*>(d->causedPopup().widget); 1873 if (actionListChanged && causedButton) 1874 pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition(); 1875 else*/ 1876 pos = p; 1877 1878 QSize size = sizeHint(); 1879 QRect screen; 1880 #ifndef QT_NO_GRAPHICSVIEW 1881 /*bool isEmbedded = !bypassGraphicsProxyWidget(this) && d->nearestGraphicsProxyWidget(this); 1882 if (isEmbedded) 1883 screen = d->popupGeometry(this); 1884 else*/ 1885 #endif 1886 screen = d->popupGeometry(QApplication::desktop()->screenNumber(p)); 1887 1888 const int desktopFrame = d->frameWidth(); 1889 bool adjustToDesktop = !window()->testAttribute(Qt::WA_DontShowOnScreen); 1890 QList<QAction*> actionsList = this->actions(); 1891 #ifdef QT_KEYPAD_NAVIGATION 1892 if (!atAction && QApplication::keypadNavigationEnabled()) { 1893 // Try to have one item activated 1894 if (d->defaultAction && d->defaultAction->isEnabled()) { 1895 atAction = d->defaultAction; 1896 // TODO: This works for first level menus, not yet sub menus 1897 } else { 1898 foreach (QAction *action, actionsList) 1899 if (action->isEnabled()) { 1900 atAction = action; 1901 break; 1902 } 1903 } 1904 d->currentAction = atAction; 1905 } 1906 #endif 1907 if (d->ncols > 1) { 1908 pos.setY(screen.top() + desktopFrame); 1909 } else if (atAction) { 1910 for (int i = 0, above_height = 0; i < actionsList.count(); i++) { 1911 QAction *action = actionsList.at(i); 1912 if (action == atAction) { 1913 int newY = pos.y() - above_height; 1914 if (d->scroll && newY < desktopFrame) { 1915 d->scroll->scrollFlags = d->scroll->scrollFlags 1916 | KexiMenuWidgetPrivate::QMenuScroller::ScrollUp; 1917 d->scroll->scrollOffset = newY; 1918 newY = desktopFrame; 1919 } 1920 pos.setY(newY); 1921 1922 if (d->scroll && d->scroll->scrollFlags != KexiMenuWidgetPrivate::QMenuScroller::ScrollNone 1923 && !style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll, 0, this)) { 1924 int below_height = above_height + d->scroll->scrollOffset; 1925 for (int i2 = i; i2 < d->actionRects.count(); i2++) 1926 below_height += d->actionRects.at(i2).height(); 1927 size.setHeight(below_height); 1928 } 1929 break; 1930 } else { 1931 above_height += d->actionRects.at(i).height(); 1932 } 1933 } 1934 } 1935 1936 QPoint mouse = QCursor::pos(); 1937 d->mousePopupPos = mouse; 1938 const bool snapToMouse = (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse)); 1939 1940 if (adjustToDesktop) { 1941 // handle popup falling "off screen" 1942 if (isRightToLeft()) { 1943 if (snapToMouse) // position flowing left from the mouse 1944 pos.setX(mouse.x() - size.width()); 1945 1946 if (pos.x() < screen.left() + desktopFrame) 1947 pos.setX(qMax(p.x(), screen.left() + desktopFrame)); 1948 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame) 1949 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1)); 1950 } else { 1951 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame) 1952 pos.setX(screen.right() - desktopFrame - size.width() + 1); 1953 if (pos.x() < screen.left() + desktopFrame) 1954 pos.setX(screen.left() + desktopFrame); 1955 } 1956 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) { 1957 if(snapToMouse) 1958 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1)); 1959 else 1960 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1)); 1961 } else if (pos.y() < screen.top() + desktopFrame) { 1962 pos.setY(screen.top() + desktopFrame); 1963 } 1964 1965 if (pos.y() < screen.top() + desktopFrame) 1966 pos.setY(screen.top() + desktopFrame); 1967 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) { 1968 if (d->scroll) { 1969 d->scroll->scrollFlags |= int(KexiMenuWidgetPrivate::QMenuScroller::ScrollDown); 1970 int y = qMax(screen.y(),pos.y()); 1971 size.setHeight(screen.bottom() - (desktopFrame * 2) - y); 1972 } else { 1973 // Too big for screen, bias to see bottom of menu (for some reason) 1974 pos.setY(screen.bottom() - size.height() + 1); 1975 } 1976 } 1977 } 1978 //setGeometry(QRect(pos, size)); 1979 //resize(size); 1980 show(); 1981 1982 #ifndef QT_NO_ACCESSIBILITY 1983 //QAccessible::updateAccessibility(this, 0, QAccessible::PopupMenuStart); 1984 #endif 1985 } 1986 1987 #if 0 1988 /*! 1989 Executes this menu synchronously. 1990 1991 This is equivalent to \c{exec(pos())}. 1992 1993 This returns the triggered QAction in either the popup menu or one 1994 of its submenus, or 0 if no item was triggered (normally because 1995 the user pressed Esc). 1996 1997 In most situations you'll want to specify the position yourself, 1998 for example, the current mouse position: 1999 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 0 2000 or aligned to a widget: 2001 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 1 2002 or in reaction to a QMouseEvent *e: 2003 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 2 2004 */ 2005 QAction *KexiMenuWidget::exec() 2006 { 2007 return exec(pos()); 2008 } 2009 2010 2011 /*! 2012 \overload 2013 2014 Executes this menu synchronously. 2015 2016 Pops up the menu so that the action \a action will be at the 2017 specified \e global position \a p. To translate a widget's local 2018 coordinates into global coordinates, use QWidget::mapToGlobal(). 2019 2020 This returns the triggered QAction in either the popup menu or one 2021 of its submenus, or 0 if no item was triggered (normally because 2022 the user pressed Esc). 2023 2024 Note that all signals are emitted as usual. If you connect a 2025 QAction to a slot and call the menu's exec(), you get the result 2026 both via the signal-slot connection and in the return value of 2027 exec(). 2028 2029 Common usage is to position the menu at the current mouse 2030 position: 2031 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 3 2032 or aligned to a widget: 2033 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 4 2034 or in reaction to a QMouseEvent *e: 2035 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 5 2036 2037 When positioning a menu with exec() or popup(), bear in mind that 2038 you cannot rely on the menu's current size(). For performance 2039 reasons, the menu adapts its size only when necessary. So in many 2040 cases, the size before and after the show is different. Instead, 2041 use sizeHint() which calculates the proper size depending on the 2042 menu's current contents. 2043 2044 \sa popup(), QWidget::mapToGlobal() 2045 */ 2046 QAction *KexiMenuWidget::exec(const QPoint &p, QAction *action) 2047 { 2048 createWinId(); 2049 QEventLoop eventLoop; 2050 d->eventLoop = &eventLoop; 2051 popup(p, action); 2052 2053 QPointer<QObject> guard = this; 2054 (void) eventLoop.exec(); 2055 if (guard.isNull()) 2056 return 0; 2057 2058 action = d->syncAction; 2059 d->syncAction = 0; 2060 d->eventLoop = 0; 2061 return action; 2062 } 2063 2064 /*! 2065 \overload 2066 2067 Executes a menu synchronously. 2068 2069 The menu's actions are specified by the list of \a actions. The menu will 2070 pop up so that the specified action, \a at, appears at global position \a 2071 pos. If \a at is not specified then the menu appears at position \a 2072 pos. \a parent is the menu's parent widget; specifying the parent will 2073 provide context when \a pos alone is not enough to decide where the menu 2074 should go (e.g., with multiple desktops or when the parent is embedded in 2075 QGraphicsView). 2076 2077 The function returns the triggered QAction in either the popup 2078 menu or one of its submenus, or 0 if no item was triggered 2079 (normally because the user pressed Esc). 2080 2081 This is equivalent to: 2082 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 6 2083 2084 \sa popup(), QWidget::mapToGlobal() 2085 */ 2086 QAction *KexiMenuWidget::exec(QList<QAction*> actions, const QPoint &pos, QAction *at, QWidget *parent) 2087 { 2088 KexiMenuWidget menu(parent); 2089 menu.addActions(actions); 2090 return menu.exec(pos, at); 2091 } 2092 2093 /*! 2094 \overload 2095 2096 Executes a menu synchronously. 2097 2098 The menu's actions are specified by the list of \a actions. The menu 2099 will pop up so that the specified action, \a at, appears at global 2100 position \a pos. If \a at is not specified then the menu appears 2101 at position \a pos. 2102 2103 The function returns the triggered QAction in either the popup 2104 menu or one of its submenus, or 0 if no item was triggered 2105 (normally because the user pressed Esc). 2106 2107 This is equivalent to: 2108 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 6 2109 2110 \sa popup(), QWidget::mapToGlobal() 2111 */ 2112 QAction *KexiMenuWidget::exec(QList<QAction*> actions, const QPoint &pos, QAction *at) 2113 { 2114 // ### Qt 5: merge 2115 return exec(actions, pos, at, 0); 2116 } 2117 #endif 2118 2119 ClickableLogoArea::ClickableLogoArea(QWidget *parent) 2120 : QAbstractButton(parent) 2121 { 2122 connect(this, SIGNAL(clicked()), this, SLOT(slotClicked())); 2123 } 2124 2125 void ClickableLogoArea::slotClicked() 2126 { 2127 QDesktopServices::openUrl(QUrl(calligraUrl)); 2128 } 2129 2130 void ClickableLogoArea::paintEvent(QPaintEvent*) 2131 { 2132 } 2133 2134 void KexiMenuWidgetPrivate::updateLogoPixmap() 2135 { 2136 bool isLight; 2137 if (bespin) { 2138 isLight = q->palette().color(QPalette::Shadow).lightness() >= 128; 2139 } 2140 else { 2141 isLight = KexiUtils::isLightColorScheme(); 2142 } 2143 calligraLogoPixmap = QPixmap(isLight ? ":/calligra-logo-white-glow" : ":/calligra-logo-black-glow"); 2144 } 2145 2146 int KexiMenuWidgetPrivate::bottomOfLastItem() const 2147 { 2148 if (actionRects.isEmpty()) { 2149 return 0; 2150 } 2151 return actionRects.last().bottom(); 2152 } 2153 2154 /* Logo's pixmap is consisted of logo and some glow (cut-off vertically). 2155 (without the glow the math would be simpler) 2156 +--------+ + 2157 | glow | |--- glowHeight - cutOffGlow 2158 | +----+ | + 2159 | |logo| | |--- calligraLogoPixmapInternalHeight 2160 | +----+ | + 2161 | glow | |--- glowHeight - cutOffGlow 2162 +--------+ + 2163 */ 2164 const int glowHeight = 64; 2165 const int cutOffGlow = 12; 2166 const int spacingAfterLastItem = 10; 2167 2168 //! @return distance between bottom border of the logo and bottom border of the entire menu widget 2169 int KexiMenuWidgetPrivate::logoBottomMargin() const 2170 { 2171 /* 2172 +----------------+ 2173 | Menu item 1 | | 2174 | ... | | 2175 | Last menu item | | q->height() 2176 +----------------+ <--- bottomOfLastItem() | 2177 | | |--- spacingAfterLastItem | 2178 +----------------+ 2179 | glow | 2180 | +----+ | 2181 | |logo| | 2182 | +----+ | + 2183 | glow | |--- bottomMargin 2184 +----------------+ + 2185 */ 2186 int bottomMargin = glowHeight - cutOffGlow; 2187 bool showSocial = true; 2188 if ((q->height() - bottomMargin - calligraLogoPixmapInternalHeight - cutOffGlow) 2189 <= (bottomOfLastItem() + spacingAfterLastItem + socialWidget->height())) 2190 { 2191 /* Special case when the last menu item would cover the logo: keep the logo below 2192 +----------------+ 2193 | +----+ | 2194 | |logo| | 2195 +----------------+ |--- bottomMargin (can be 0 or negative) 2196 | */ 2197 bottomMargin = q->height() - bottomOfLastItem() - spacingAfterLastItem 2198 - cutOffGlow - calligraLogoPixmapInternalHeight - socialWidget->height(); 2199 showSocial = bottomMargin > QFontMetrics(smallTextFont).height(); 2200 } 2201 socialWidget->setVisible(showSocial); 2202 return bottomMargin; 2203 } 2204 2205 void KexiMenuWidgetPrivate::updateLogo() 2206 { 2207 const QRect logoRect((q->width() - 2 - calligraLogoPixmapInternalWidth) / 2, 2208 q->height() - logoBottomMargin() 2209 - calligraLogoPixmapInternalHeight - cutOffGlow, 2210 calligraLogoPixmapInternalWidth, 2211 calligraLogoPixmapInternalHeight); 2212 if (!clickableLogoArea) { 2213 updateLogoPixmap(); 2214 clickableLogoArea = new ClickableLogoArea(q); 2215 clickableLogoArea->setCursor(Qt::PointingHandCursor); 2216 clickableLogoArea->setToolTip(xi18n("Visit Calligra home page at %1", QLatin1String(calligraUrl))); 2217 } 2218 clickableLogoArea->setGeometry(logoRect.translated(0, -socialWidget->height())); 2219 } 2220 2221 /*! 2222 \reimp 2223 */ 2224 void KexiMenuWidget::showEvent(QShowEvent* event) 2225 { 2226 QWidget::showEvent(event); 2227 d->updateLogo(); 2228 d->clickableLogoArea->show(); 2229 } 2230 2231 /*! 2232 \reimp 2233 */ 2234 void KexiMenuWidget::hideEvent(QHideEvent *) 2235 { 2236 emit aboutToHide(); 2237 if (d->eventLoop) 2238 d->eventLoop->exit(); 2239 d->setCurrentAction(0); 2240 #ifndef QT_NO_ACCESSIBILITY 2241 QAccessibleEvent event(this, QAccessible::PopupMenuEnd); 2242 event.setChild(0); 2243 QAccessible::updateAccessibility(&event); 2244 #endif 2245 d->mouseDown = 0; 2246 d->hasHadMouse = false; 2247 d->causedPopup.widget = 0; 2248 d->causedPopup.action = 0; 2249 if (d->scroll) 2250 d->scroll->scrollTimer.stop(); //make sure the timer stops 2251 // this unmarks the previous persistent action 2252 d->setActionPersistentlySelected(0, false); 2253 } 2254 2255 /*! 2256 \reimp 2257 */ 2258 void KexiMenuWidget::paintEvent(QPaintEvent *e) 2259 { 2260 d->updateActionRects(); 2261 QPainter p(this); 2262 QRegion emptyArea = QRegion(rect()); 2263 2264 //! @todo KEXI3 port OxygenHelper 2265 #if 0 2266 if (d->oxygenHelper) { 2267 //d->oxygenHelper->renderWindowBackground(&p, rect(), this, palette()); 2268 d->oxygenHelper->renderMenuBackground(&p, rect(), this, palette()); 2269 // d->oxygenHelper->drawFloatFrame(&p, rect(), palette().window().color(), true); 2270 } 2271 #endif 2272 2273 QStyleOptionMenuItem menuOpt; 2274 menuOpt.initFrom(this); 2275 menuOpt.state = QStyle::State_None; 2276 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable; 2277 menuOpt.maxIconWidth = 0; 2278 menuOpt.tabWidth = 0; 2279 if (d->bespin) { 2280 p.fillRect(rect(), palette().shadow()); 2281 } 2282 else { 2283 style()->drawPrimitive(QStyle::PE_PanelMenu, &menuOpt, &p, this); 2284 } 2285 2286 { 2287 QStyleOptionFrame opt; 2288 opt.initFrom(this); 2289 opt.frameShape = QFrame::VLine; 2290 opt.state |= QStyle::State_Raised; 2291 opt.lineWidth = 1; 2292 opt.midLineWidth = 0; 2293 //opt.rect = QRect(opt.rect.topRight() - QPoint(0, 0), opt.rect.bottomRight() + QPoint(10, 0)); 2294 //const QRect cr = style()->subElementRect(QStyle::SE_ShapedFrameContents, &opt); 2295 //opt.lineWidth = opt.rect.right() - cr.right(); 2296 opt.rect.setX(opt.rect.width() - opt.lineWidth); 2297 opt.rect.setWidth(opt.lineWidth); 2298 style()->drawControl(QStyle::CE_ShapedFrame, &opt, &p); 2299 2300 /* 2301 QStyleOption opt; 2302 opt.initFrom(this); 2303 opt.state |= QStyle::State_Horizontal; 2304 opt.rect = QRect(opt.rect.topRight() - QPoint(10, 0), opt.rect.bottomRight() + QPoint(10, 0)); 2305 style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, &p, this); 2306 */ 2307 } 2308 2309 //draw the items that need updating.. 2310 QList<QAction*> actionsList = this->actions(); 2311 for (int i = 0; i < actionsList.count(); ++i) { 2312 QAction *action = actionsList.at(i); 2313 QRect adjustedActionRect = d->actionRects.at(i); 2314 if (!e->rect().intersects(adjustedActionRect) 2315 || d->widgetItems.value(action)) 2316 continue; 2317 //set the clip region to be extra safe (and adjust for the scrollers) 2318 QRegion adjustedActionReg(adjustedActionRect); 2319 emptyArea -= adjustedActionReg; 2320 p.setClipRegion(adjustedActionReg); 2321 2322 QStyleOptionMenuItem opt; 2323 initStyleOption(&opt, action); 2324 opt.rect = adjustedActionRect; 2325 if (d->actionPersistentlySelected(action)) { 2326 opt.state |= QStyle::State_Selected; 2327 if (!d->bespin && !d->qtcurve) { 2328 opt.palette.setBrush(QPalette::Window, opt.palette.brush(QPalette::Highlight)); 2329 opt.palette.setBrush(QPalette::WindowText, opt.palette.brush(QPalette::HighlightedText)); 2330 } 2331 } 2332 else if (!action->isSeparator()) { 2333 if (!d->bespin && opt.state & QStyle::State_Selected) { 2334 // lighten the highlight to make it different from 2335 // the persistently selected item 2336 opt.palette.setColor(QPalette::Highlight, 2337 KColorUtils::mix( 2338 opt.palette.color(QPalette::Highlight), 2339 opt.palette.color(QPalette::Window))); 2340 opt.palette.setColor(QPalette::HighlightedText, opt.palette.color(QPalette::Text)); 2341 } 2342 } 2343 2344 // Depending on style Button or Background brush may be used 2345 // to fill background of deselected items. Make it transparent. 2346 bool transparentBackground = !(opt.state & QStyle::State_Selected); 2347 //! @todo KEXI3 port OxygenHelper 2348 #if 0 2349 if (d->oxygenHelper && action->isSeparator()) { 2350 transparentBackground = false; 2351 } 2352 #endif 2353 if (transparentBackground) { 2354 opt.palette.setBrush(QPalette::Button, QBrush(Qt::transparent)); 2355 if (!d->bespin) { 2356 opt.palette.setBrush(QPalette::Background, QBrush(Qt::transparent)); 2357 } 2358 } 2359 2360 style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this); 2361 } 2362 2363 const int fw = d->frameWidth(); 2364 //draw the scroller regions.. 2365 if (d->scroll) { 2366 menuOpt.menuItemType = QStyleOptionMenuItem::Scroller; 2367 menuOpt.state |= QStyle::State_Enabled; 2368 if (d->scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollUp) { 2369 menuOpt.rect.setRect(fw, fw, width() - (fw * 2), d->scrollerHeight()); 2370 emptyArea -= QRegion(menuOpt.rect); 2371 p.setClipRect(menuOpt.rect); 2372 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this); 2373 } 2374 if (d->scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollDown) { 2375 menuOpt.rect.setRect(fw, height() - d->scrollerHeight() - fw, width() - (fw * 2), 2376 d->scrollerHeight()); 2377 emptyArea -= QRegion(menuOpt.rect); 2378 menuOpt.state |= QStyle::State_DownArrow; 2379 p.setClipRect(menuOpt.rect); 2380 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this); 2381 } 2382 } 2383 //paint the tear off.. 2384 /* if (d->tearoff) { 2385 menuOpt.menuItemType = QStyleOptionMenuItem::TearOff; 2386 menuOpt.rect.setRect(fw, fw, width() - (fw * 2), 2387 style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this)); 2388 if (d->scroll && d->scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollUp) 2389 menuOpt.rect.translate(0, d->scrollerHeight()); 2390 emptyArea -= QRegion(menuOpt.rect); 2391 p.setClipRect(menuOpt.rect); 2392 menuOpt.state = QStyle::State_None; 2393 if (d->tearoffHighlighted) 2394 menuOpt.state |= QStyle::State_Selected; 2395 style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, &p, this); 2396 }*/ 2397 //draw border 2398 if (fw > 0) { 2399 QRegion borderReg; 2400 borderReg += QRect(0, 0, fw, height()); //left 2401 borderReg += QRect(width()-fw, 0, fw, height()); //right 2402 borderReg += QRect(0, 0, width(), fw); //top 2403 borderReg += QRect(0, height()-fw, width(), fw); //bottom 2404 p.setClipRegion(borderReg); 2405 emptyArea -= borderReg; 2406 QStyleOptionFrame frame; 2407 frame.rect = rect(); 2408 frame.palette = palette(); 2409 frame.state = QStyle::State_None; 2410 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth); 2411 frame.midLineWidth = 0; 2412 style()->drawPrimitive(QStyle::PE_FrameMenu, &frame, &p, this); 2413 // full frame: 2414 style()->drawPrimitive(QStyle::PE_FrameWindow, &frame, &p, this); 2415 } 2416 2417 //finally the rest of the space 2418 p.setClipRegion(emptyArea); 2419 menuOpt.state = QStyle::State_None; 2420 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea; 2421 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable; 2422 menuOpt.rect = rect(); 2423 menuOpt.menuRect = rect(); 2424 style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this); 2425 2426 p.translate(0, -d->socialWidget->height()); 2427 2428 // version 2429 p.setFont(d->smallTextFont); 2430 QColor textColor; 2431 textColor = palette().color(QPalette::Base); 2432 p.setPen(QPen(textColor)); 2433 const int logoBottomMargin = d->logoBottomMargin(); 2434 QRect textRect(0, height() - logoBottomMargin + 1, width(), 2435 logoBottomMargin - 1 + d->socialWidget->height()); 2436 p.drawText(textRect, Qt::AlignHCenter | Qt::AlignTop, QLatin1String(Kexi::versionString())); 2437 textColor = palette().color(QPalette::WindowText); 2438 textColor.setAlpha(180); 2439 p.setPen(QPen(textColor)); 2440 textRect.translate(0, -1); 2441 p.drawText(textRect, Qt::AlignHCenter | Qt::AlignTop, QLatin1String(Kexi::versionString())); 2442 2443 // logo 2444 p.drawPixmap((width() - d->calligraLogoPixmap.width()) / 2, 2445 textRect.top() - d->calligraLogoPixmap.height() 2446 + calligraLogoPixmapInternalHeight - 20, 2447 d->calligraLogoPixmap); 2448 } 2449 2450 #ifndef QT_NO_WHEELEVENT 2451 /*! 2452 \reimp 2453 */ 2454 void KexiMenuWidget::wheelEvent(QWheelEvent *e) 2455 { 2456 if (d->scroll && rect().contains(e->pos())) 2457 d->scrollMenu(e->delta() > 0 ? 2458 KexiMenuWidgetPrivate::QMenuScroller::ScrollUp : KexiMenuWidgetPrivate::QMenuScroller::ScrollDown); 2459 } 2460 #endif 2461 2462 /*! 2463 \reimp 2464 */ 2465 void KexiMenuWidget::mousePressEvent(QMouseEvent *e) 2466 { 2467 if (d->aboutToHide || d->mouseEventTaken(e)) 2468 return; 2469 if (!rect().contains(e->pos())) { 2470 if (d->noReplayFor 2471 && QRect(d->noReplayFor->mapToGlobal(QPoint()), d->noReplayFor->size()).contains(e->globalPos())) 2472 setAttribute(Qt::WA_NoMouseReplay); 2473 if (d->eventLoop) // synchronous operation 2474 d->syncAction = 0; 2475 d->hideUpToMenuBar(); 2476 return; 2477 } 2478 d->mouseDown = this; 2479 2480 QAction *action = d->actionAt(e->pos()); 2481 d->setCurrentAction(action, 20); 2482 update(); 2483 } 2484 2485 /*! 2486 \reimp 2487 */ 2488 void KexiMenuWidget::mouseReleaseEvent(QMouseEvent *e) 2489 { 2490 if (d->aboutToHide || d->mouseEventTaken(e)) 2491 return; 2492 if(d->mouseDown != this) { 2493 d->mouseDown = 0; 2494 return; 2495 } 2496 2497 d->mouseDown = 0; 2498 d->setSyncAction(); 2499 QAction *action = d->actionAt(e->pos()); 2500 //qDebug() << "action:" << action << "d->currentAction:" << d->currentAction; 2501 if (action && action == d->currentAction) { 2502 if (!action->menu()){ 2503 #if defined(Q_OS_WIN) 2504 //On Windows only context menus can be activated with the right button 2505 if (e->button() == Qt::LeftButton || d->topCausedWidget() == 0) 2506 #endif 2507 { 2508 if (!d->actionPersistentlySelected(action)) { 2509 d->toggleActionPersistentlySelected(action); 2510 update(); 2511 d->activateAction(action, QAction::Trigger); 2512 } 2513 } 2514 } 2515 } else if (d->hasMouseMoved(e->globalPos())) { 2516 d->hideUpToMenuBar(); 2517 } 2518 } 2519 2520 /*! 2521 \reimp 2522 */ 2523 void KexiMenuWidget::changeEvent(QEvent *e) 2524 { 2525 if (e->type() == QEvent::StyleChange || e->type() == QEvent::FontChange || 2526 e->type() == QEvent::LayoutDirectionChange) { 2527 d->itemsDirty = 1; 2528 setMouseTracking(style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, this)); 2529 if (isVisible()) 2530 resize(sizeHint()); 2531 if (!style()->styleHint(QStyle::SH_Menu_Scrollable, 0, this)) { 2532 delete d->scroll; 2533 d->scroll = 0; 2534 } else if (!d->scroll) { 2535 d->scroll = new KexiMenuWidgetPrivate::QMenuScroller; 2536 d->scroll->scrollFlags = KexiMenuWidgetPrivate::QMenuScroller::ScrollNone; 2537 } 2538 } else if (e->type() == QEvent::EnabledChange) { 2539 d->menuAction->setEnabled(isEnabled()); 2540 } 2541 else if (e->type() == QEvent::PaletteChange) { 2542 d->updateLogoPixmap(); 2543 d->updateLogo(); 2544 } 2545 QWidget::changeEvent(e); 2546 } 2547 2548 2549 /*! 2550 \reimp 2551 */ 2552 bool 2553 KexiMenuWidget::event(QEvent *e) 2554 { 2555 switch (e->type()) { 2556 case QEvent::Polish: 2557 d->updateLayoutDirection(); 2558 break; 2559 case QEvent::ShortcutOverride: { 2560 QKeyEvent *kev = static_cast<QKeyEvent*>(e); 2561 if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down 2562 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right 2563 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return 2564 || kev->key() == Qt::Key_Escape) { 2565 e->accept(); 2566 return true; 2567 } 2568 } 2569 break; 2570 case QEvent::KeyPress: { 2571 QKeyEvent *ke = (QKeyEvent*)e; 2572 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) { 2573 keyPressEvent(ke); 2574 return true; 2575 } 2576 } break; 2577 /*case QEvent::ContextMenu: 2578 if(d->menuDelayTimer.isActive()) { 2579 d->menuDelayTimer.stop(); 2580 internalDelayedPopup(); 2581 } 2582 break;*/ 2583 case QEvent::Resize: { 2584 QStyleHintReturnMask menuMask; 2585 QStyleOption option; 2586 option.initFrom(this); 2587 if (style()->styleHint(QStyle::SH_Menu_Mask, &option, this, &menuMask)) { 2588 setMask(menuMask.region); 2589 } 2590 d->itemsDirty = 1; 2591 d->updateActionRects(); 2592 d->updateLogo(); 2593 break; 2594 } 2595 case QEvent::Show: 2596 d->mouseDown = 0; 2597 d->updateActionRects(); 2598 show(); 2599 // if (d->currentAction) 2600 // d->popupAction(d->currentAction, 0, false); 2601 break; 2602 #ifndef QT_NO_WHATSTHIS 2603 case QEvent::QueryWhatsThis: 2604 e->setAccepted(whatsThis().size()); 2605 if (QAction *action = d->actionAt(static_cast<QHelpEvent*>(e)->pos())) { 2606 if (action->whatsThis().size() || action->menu()) 2607 e->accept(); 2608 } 2609 return true; 2610 #endif 2611 #ifdef QT_SOFTKEYS_ENABLED 2612 case QEvent::LanguageChange: { 2613 d->selectAction->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::SelectSoftKey)); 2614 d->cancelAction->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::CancelSoftKey)); 2615 } 2616 break; 2617 #endif 2618 default: 2619 break; 2620 } 2621 return QWidget::event(e); 2622 } 2623 2624 /*! 2625 \reimp 2626 */ 2627 bool KexiMenuWidget::focusNextPrevChild(bool next) 2628 { 2629 setFocus(); 2630 QKeyEvent ev(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier); 2631 keyPressEvent(&ev); 2632 return true; 2633 } 2634 2635 /*! 2636 \reimp 2637 */ 2638 void KexiMenuWidget::keyPressEvent(QKeyEvent *e) 2639 { 2640 d->updateActionRects(); 2641 int key = e->key(); 2642 if (isRightToLeft()) { // in reverse mode open/close key for submenues are reversed 2643 if (key == Qt::Key_Left) 2644 key = Qt::Key_Right; 2645 else if (key == Qt::Key_Right) 2646 key = Qt::Key_Left; 2647 } 2648 #ifndef Q_OS_MACOS 2649 if (key == Qt::Key_Tab) //means down 2650 key = Qt::Key_Down; 2651 if (key == Qt::Key_Backtab) //means up 2652 key = Qt::Key_Up; 2653 #endif 2654 2655 bool key_consumed = false; 2656 QList<QAction*> actionsList = this->actions(); 2657 switch(key) { 2658 case Qt::Key_Home: 2659 key_consumed = true; 2660 if (d->scroll) 2661 d->scrollMenu(KexiMenuWidgetPrivate::QMenuScroller::ScrollTop, true); 2662 break; 2663 case Qt::Key_End: 2664 key_consumed = true; 2665 if (d->scroll) 2666 d->scrollMenu(KexiMenuWidgetPrivate::QMenuScroller::ScrollBottom, true); 2667 break; 2668 case Qt::Key_PageUp: 2669 key_consumed = true; 2670 if (d->currentAction && d->scroll) { 2671 if(d->scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollUp) 2672 d->scrollMenu(KexiMenuWidgetPrivate::QMenuScroller::ScrollUp, true, true); 2673 else 2674 d->scrollMenu(KexiMenuWidgetPrivate::QMenuScroller::ScrollTop, true); 2675 } 2676 break; 2677 case Qt::Key_PageDown: 2678 key_consumed = true; 2679 if (d->currentAction && d->scroll) { 2680 if(d->scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollDown) 2681 d->scrollMenu(KexiMenuWidgetPrivate::QMenuScroller::ScrollDown, true, true); 2682 else 2683 d->scrollMenu(KexiMenuWidgetPrivate::QMenuScroller::ScrollBottom, true); 2684 } 2685 break; 2686 case Qt::Key_Up: 2687 case Qt::Key_Down: { 2688 key_consumed = true; 2689 QAction *nextAction = 0; 2690 KexiMenuWidgetPrivate::QMenuScroller::ScrollLocation scroll_loc = KexiMenuWidgetPrivate::QMenuScroller::ScrollStay; 2691 if (!d->currentAction) { 2692 if(key == Qt::Key_Down) { 2693 for(int i = 0; i < actionsList.count(); ++i) { 2694 QAction *act = actionsList.at(i); 2695 if (d->actionRects.at(i).isNull()) 2696 continue; 2697 if (!act->isSeparator() && 2698 (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this) 2699 || act->isEnabled())) { 2700 nextAction = act; 2701 break; 2702 } 2703 } 2704 } else { 2705 for(int i = actionsList.count()-1; i >= 0; --i) { 2706 QAction *act = actionsList.at(i); 2707 if (d->actionRects.at(i).isNull()) 2708 continue; 2709 if (!act->isSeparator() && 2710 (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this) 2711 || act->isEnabled())) { 2712 nextAction = act; 2713 break; 2714 } 2715 } 2716 } 2717 } else { 2718 for(int i = 0, y = 0; !nextAction && i < actionsList.count(); i++) { 2719 QAction *act = actionsList.at(i); 2720 if (act == d->currentAction) { 2721 if (key == Qt::Key_Up) { 2722 for(int next_i = i-1; true; next_i--) { 2723 if (next_i == -1) { 2724 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this)) 2725 break; 2726 if (d->scroll) 2727 scroll_loc = KexiMenuWidgetPrivate::QMenuScroller::ScrollBottom; 2728 next_i = d->actionRects.count()-1; 2729 } 2730 QAction *next = actionsList.at(next_i); 2731 if (next == d->currentAction) 2732 break; 2733 if (d->actionRects.at(next_i).isNull()) 2734 continue; 2735 if (next->isSeparator() || 2736 (!next->isEnabled() && 2737 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this))) 2738 continue; 2739 nextAction = next; 2740 if (d->scroll && (d->scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollUp)) { 2741 int topVisible = d->scrollerHeight(); 2742 /* if (d->tearoff) 2743 topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);*/ 2744 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height()) 2745 scroll_loc = KexiMenuWidgetPrivate::QMenuScroller::ScrollTop; 2746 } 2747 break; 2748 } 2749 /* if (!nextAction && d->tearoff) 2750 d->tearoffHighlighted = 1;*/ 2751 } else { 2752 y += d->actionRects.at(i).height(); 2753 for(int next_i = i+1; true; next_i++) { 2754 if (next_i == d->actionRects.count()) { 2755 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this)) 2756 break; 2757 if (d->scroll) 2758 scroll_loc = KexiMenuWidgetPrivate::QMenuScroller::ScrollTop; 2759 next_i = 0; 2760 } 2761 QAction *next = actionsList.at(next_i); 2762 if (next == d->currentAction) 2763 break; 2764 if (d->actionRects.at(next_i).isNull()) 2765 continue; 2766 if (next->isSeparator() || 2767 (!next->isEnabled() && 2768 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this))) 2769 continue; 2770 nextAction = next; 2771 if (d->scroll && (d->scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollDown)) { 2772 int bottomVisible = height() - d->scrollerHeight(); 2773 if (d->scroll->scrollFlags & KexiMenuWidgetPrivate::QMenuScroller::ScrollUp) 2774 bottomVisible -= d->scrollerHeight(); 2775 /* if (d->tearoff) 2776 bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);*/ 2777 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible) 2778 scroll_loc = KexiMenuWidgetPrivate::QMenuScroller::ScrollBottom; 2779 } 2780 break; 2781 } 2782 } 2783 break; 2784 } 2785 y += d->actionRects.at(i).height(); 2786 } 2787 } 2788 if (nextAction) { 2789 if (d->scroll && scroll_loc != KexiMenuWidgetPrivate::QMenuScroller::ScrollStay) { 2790 d->scroll->scrollTimer.stop(); 2791 d->scrollMenu(nextAction, scroll_loc); 2792 } 2793 d->setCurrentAction(nextAction, /*popup*/-1, KexiMenuWidget::SelectedFromKeyboard); 2794 } 2795 break; } 2796 2797 /*case Qt::Key_Right: 2798 if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) { 2799 d->popupAction(d->currentAction, 0, true); 2800 key_consumed = true; 2801 break; 2802 } 2803 //FALL THROUGH*/ 2804 case Qt::Key_Left: { 2805 if (d->currentAction && !d->scroll) { 2806 QAction *nextAction = 0; 2807 QRect actionR = d->actionRect(d->currentAction); 2808 for(int x = actionR.left()-1; !nextAction && x >= 0; x--) 2809 nextAction = d->actionAt(QPoint(x, actionR.center().y())); 2810 if (nextAction) { 2811 d->setCurrentAction(nextAction, /*popup*/-1, KexiMenuWidget::SelectedFromKeyboard); 2812 key_consumed = true; 2813 } 2814 } 2815 if (!key_consumed && key == Qt::Key_Left && qobject_cast<KexiMenuWidget*>(d->causedPopup.widget)) { 2816 QPointer<QWidget> caused = d->causedPopup.widget; 2817 d->hideMenu(this); 2818 if (caused) 2819 caused->setFocus(); 2820 key_consumed = true; 2821 } 2822 break; } 2823 2824 case Qt::Key_Alt: 2825 2826 key_consumed = true; 2827 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this)) 2828 { 2829 d->hideMenu(this); 2830 } 2831 break; 2832 2833 case Qt::Key_Escape: 2834 #ifdef QT_KEYPAD_NAVIGATION 2835 case Qt::Key_Back: 2836 #endif 2837 key_consumed = true; 2838 { 2839 QPointer<QWidget> caused = d->causedPopup.widget; 2840 d->hideMenu(this); // hide after getting causedPopup 2841 } 2842 break; 2843 2844 case Qt::Key_Space: 2845 if (style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this)) { 2846 key_consumed = true; // for motif 2847 } 2848 break; 2849 #ifdef QT_KEYPAD_NAVIGATION 2850 case Qt::Key_Select: 2851 #endif 2852 case Qt::Key_Return: 2853 case Qt::Key_Enter: { 2854 if (!d->currentAction) { 2855 d->setFirstActionActive(); 2856 key_consumed = true; 2857 break; 2858 } 2859 2860 d->setSyncAction(); 2861 2862 if (d->currentAction->menu()) { 2863 /*d->popupAction(d->currentAction, 0, true);*/ 2864 } 2865 else { 2866 d->activateAction(d->currentAction, QAction::Trigger); 2867 } 2868 key_consumed = true; 2869 break; 2870 } 2871 #ifndef QT_NO_WHATSTHIS 2872 case Qt::Key_F1: 2873 if (!d->currentAction || d->currentAction->whatsThis().isNull()) 2874 break; 2875 QWhatsThis::enterWhatsThisMode(); 2876 d->activateAction(d->currentAction, QAction::Trigger); 2877 return; 2878 #endif 2879 default: 2880 break; 2881 } 2882 2883 if (!key_consumed) { // send to menu bar 2884 if ((!e->modifiers() || e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ShiftModifier) && 2885 e->text().length()==1) { 2886 bool activateAction = false; 2887 QAction *nextAction = 0; 2888 if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch, 0, this) && !e->modifiers()) { 2889 int best_match_count = 0; 2890 d->searchBufferTimer.start(2000, this); 2891 d->searchBuffer += e->text(); 2892 for(int i = 0; i < actionsList.size(); ++i) { 2893 int match_count = 0; 2894 if (d->actionRects.at(i).isNull()) 2895 continue; 2896 QAction *act = actionsList.at(i); 2897 const QString act_text = act->text(); 2898 for(int c = 0; c < d->searchBuffer.size(); ++c) { 2899 if(act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1) 2900 ++match_count; 2901 } 2902 if(match_count > best_match_count) { 2903 best_match_count = match_count; 2904 nextAction = act; 2905 } 2906 } 2907 } 2908 #ifndef QT_NO_SHORTCUT 2909 else { 2910 int clashCount = 0; 2911 QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0; 2912 QChar c = e->text().at(0).toUpper(); 2913 for(int i = 0; i < actionsList.size(); ++i) { 2914 if (d->actionRects.at(i).isNull()) 2915 continue; 2916 QAction *act = actionsList.at(i); 2917 QKeySequence sequence = QKeySequence::mnemonic(act->text()); 2918 int key = sequence[0] & 0xffff; 2919 if (key == c.unicode()) { 2920 clashCount++; 2921 if (!first) 2922 first = act; 2923 if (act == d->currentAction) 2924 currentSelected = act; 2925 else if (!firstAfterCurrent && currentSelected) 2926 firstAfterCurrent = act; 2927 } 2928 } 2929 if (clashCount == 1) 2930 activateAction = true; 2931 if (clashCount >= 1) { 2932 if (clashCount == 1 || !currentSelected || !firstAfterCurrent) 2933 nextAction = first; 2934 else 2935 nextAction = firstAfterCurrent; 2936 } 2937 } 2938 #endif 2939 if (nextAction) { 2940 key_consumed = true; 2941 if(d->scroll) 2942 d->scrollMenu(nextAction, KexiMenuWidgetPrivate::QMenuScroller::ScrollCenter, false); 2943 d->setCurrentAction(nextAction, 20, KexiMenuWidget::SelectedFromElsewhere, true); 2944 if (!nextAction->menu() && activateAction) { 2945 d->setSyncAction(); 2946 d->activateAction(nextAction, QAction::Trigger); 2947 } 2948 } 2949 } 2950 if (!key_consumed) { 2951 } 2952 2953 #ifdef Q_OS_WIN32 2954 if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta)) 2955 QApplication::beep(); 2956 #endif // Q_OS_WIN32 2957 } 2958 if (key_consumed) 2959 e->accept(); 2960 else 2961 e->ignore(); 2962 } 2963 2964 /*! 2965 \reimp 2966 */ 2967 void KexiMenuWidget::mouseMoveEvent(QMouseEvent *e) 2968 { 2969 if (!isVisible() || d->aboutToHide || d->mouseEventTaken(e)) 2970 return; 2971 d->motions++; 2972 if (d->motions == 0) // ignore first mouse move event (see enterEvent()) 2973 return; 2974 d->hasHadMouse = d->hasHadMouse || rect().contains(e->pos()); 2975 2976 QAction *action = d->actionAt(e->pos()); 2977 if (!action) { 2978 if (d->hasHadMouse 2979 && (!d->currentAction 2980 || !(d->currentAction->menu() && d->currentAction->menu()->isVisible()))) 2981 d->setCurrentAction(0); 2982 return; 2983 } else if(e->buttons()) { 2984 d->mouseDown = this; 2985 } 2986 if (d->sloppyRegion.contains(e->pos())) { 2987 d->sloppyAction = action; 2988 KexiMenuWidgetPrivate::sloppyDelayTimer = startTimer(style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)*6); 2989 } else { 2990 d->setCurrentAction(action, style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)); 2991 } 2992 } 2993 2994 /*! 2995 \reimp 2996 */ 2997 void KexiMenuWidget::enterEvent(QEvent *) 2998 { 2999 d->motions = -1; // force us to ignore the generate mouse move in mouseMoveEvent() 3000 } 3001 3002 /*! 3003 \reimp 3004 */ 3005 void KexiMenuWidget::leaveEvent(QEvent *) 3006 { 3007 d->sloppyAction = 0; 3008 if (!d->sloppyRegion.isEmpty()) 3009 d->sloppyRegion = QRegion(); 3010 if (!d->activeMenu && d->currentAction) 3011 setActiveAction(0); 3012 } 3013 3014 /*! 3015 \reimp 3016 */ 3017 void 3018 KexiMenuWidget::timerEvent(QTimerEvent *e) 3019 { 3020 if (d->scroll && d->scroll->scrollTimer.timerId() == e->timerId()) { 3021 d->scrollMenu((KexiMenuWidgetPrivate::QMenuScroller::ScrollDirection)d->scroll->scrollDirection); 3022 if (d->scroll->scrollFlags == KexiMenuWidgetPrivate::QMenuScroller::ScrollNone) 3023 d->scroll->scrollTimer.stop(); 3024 }/* else if(d->menuDelayTimer.timerId() == e->timerId()) { 3025 d->menuDelayTimer.stop(); 3026 internalDelayedPopup(); 3027 }*/ else if(KexiMenuWidgetPrivate::sloppyDelayTimer == e->timerId()) { 3028 killTimer(KexiMenuWidgetPrivate::sloppyDelayTimer); 3029 KexiMenuWidgetPrivate::sloppyDelayTimer = 0; 3030 internalSetSloppyAction(); 3031 } else if(d->searchBufferTimer.timerId() == e->timerId()) { 3032 d->searchBuffer.clear(); 3033 } 3034 } 3035 3036 /*! 3037 \reimp 3038 */ 3039 void KexiMenuWidget::actionEvent(QActionEvent *e) 3040 { 3041 d->itemsDirty = 1; 3042 //setAttribute(Qt::WA_Resized, false); 3043 if (e->type() == QEvent::ActionAdded) { 3044 connect(e->action(), SIGNAL(triggered()), this, SLOT(actionTriggered())); 3045 connect(e->action(), SIGNAL(hovered()), this, SLOT(actionHovered())); 3046 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) { 3047 QWidget *widget = wa->requestWidget(this); 3048 if (widget) 3049 d->widgetItems.insert(wa, widget); 3050 } 3051 } else if (e->type() == QEvent::ActionRemoved) { 3052 e->action()->disconnect(this); 3053 if (e->action() == d->currentAction) 3054 d->currentAction = 0; 3055 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) { 3056 if (QWidget *widget = d->widgetItems.value(wa)) 3057 wa->releaseWidget(widget); 3058 } 3059 d->widgetItems.remove(e->action()); 3060 } 3061 3062 if (isVisible()) { 3063 d->updateActionRects(); 3064 resize(sizeHint()); 3065 update(); 3066 } 3067 } 3068 3069 /*! 3070 \internal 3071 */ 3072 void KexiMenuWidget::internalSetSloppyAction() 3073 { 3074 if (d->sloppyAction) 3075 d->setCurrentAction(d->sloppyAction, 0); 3076 } 3077 3078 #if 0 3079 /*! 3080 \internal 3081 */ 3082 void KexiMenuWidget::internalDelayedPopup() 3083 { 3084 //hide the current item 3085 if (KexiMenuWidget *menu = d->activeMenu) { 3086 d->activeMenu = 0; 3087 d->hideMenu(menu); 3088 } 3089 3090 if (!d->currentAction || !d->currentAction->isEnabled() || !d->currentAction->menu() || 3091 !d->currentAction->menu()->isEnabled() || d->currentAction->menu()->isVisible()) 3092 return; 3093 3094 //setup 3095 d->activeMenu = d->currentAction->menu(); 3096 d->activeMenu->causedPopup().widget = this; 3097 d->activeMenu->causedPopup().action = d->currentAction; 3098 3099 int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this); 3100 const QRect actionRect(d->actionRect(d->currentAction)); 3101 const QSize menuSize(d->activeMenu->sizeHint()); 3102 const QPoint rightPos(mapToGlobal(QPoint(actionRect.right() + subMenuOffset + 1, actionRect.top()))); 3103 const QPoint leftPos(mapToGlobal(QPoint(actionRect.left() - subMenuOffset - menuSize.width(), actionRect.top()))); 3104 3105 QPoint pos(rightPos); 3106 KexiMenuWidget *caused = qobject_cast<KexiMenuWidget*>(d->activeMenu->causedPopup().widget); 3107 3108 const QRect availGeometry(d->popupGeometry(caused)); 3109 if (isRightToLeft()) { 3110 pos = leftPos; 3111 if ((caused && caused->x() < x()) || pos.x() < availGeometry.left()) { 3112 if(rightPos.x() + menuSize.width() < availGeometry.right()) 3113 pos = rightPos; 3114 else 3115 pos.rx() = availGeometry.left(); 3116 } 3117 } else { 3118 if ((caused && caused->x() > x()) || pos.x() + menuSize.width() > availGeometry.right()) { 3119 if(leftPos.x() < availGeometry.left()) 3120 pos.rx() = availGeometry.right() - menuSize.width(); 3121 else 3122 pos = leftPos; 3123 } 3124 } 3125 3126 //calc sloppy focus buffer 3127 if (style()->styleHint(QStyle::SH_Menu_SloppySubMenus, 0, this)) { 3128 QPoint cur = QCursor::pos(); 3129 if (actionRect.contains(mapFromGlobal(cur))) { 3130 QPoint pts[4]; 3131 pts[0] = QPoint(cur.x(), cur.y() - 2); 3132 pts[3] = QPoint(cur.x(), cur.y() + 2); 3133 if (pos.x() >= cur.x()) { 3134 pts[1] = QPoint(geometry().right(), pos.y()); 3135 pts[2] = QPoint(geometry().right(), pos.y() + menuSize.height()); 3136 } else { 3137 pts[1] = QPoint(pos.x() + menuSize.width(), pos.y()); 3138 pts[2] = QPoint(pos.x() + menuSize.width(), pos.y() + menuSize.height()); 3139 } 3140 QPolygon points(4); 3141 for(int i = 0; i < 4; i++) 3142 points.setPoint(i, mapFromGlobal(pts[i])); 3143 d->sloppyRegion = QRegion(points); 3144 } 3145 } 3146 3147 //do the popup 3148 d->activeMenu->popup(pos); 3149 } 3150 #endif 3151 3152 /*!\internal 3153 */ 3154 void KexiMenuWidget::setNoReplayFor(QWidget *noReplayFor) 3155 { 3156 #ifdef Q_OS_WIN 3157 d->noReplayFor = noReplayFor; 3158 #else 3159 Q_UNUSED(noReplayFor); 3160 #endif 3161 } 3162 3163 /*! 3164 \brief whether consecutive separators should be collapsed 3165 3166 This property specifies whether consecutive separators in the menu 3167 should be visually collapsed to a single one. Separators at the 3168 beginning or the end of the menu are also hidden. 3169 3170 By default, this property is true. 3171 */ 3172 bool KexiMenuWidget::separatorsCollapsible() const 3173 { 3174 return d->collapsibleSeparators; 3175 } 3176 3177 void KexiMenuWidget::setSeparatorsCollapsible(bool collapse) 3178 { 3179 if (d->collapsibleSeparators == collapse) 3180 return; 3181 3182 d->collapsibleSeparators = collapse; 3183 d->itemsDirty = 1; 3184 if (isVisible()) { 3185 d->updateActionRects(); 3186 update(); 3187 } 3188 }