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 &current = 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 }