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