File indexing completed on 2024-05-12 16:40:04

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
0003    Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "KexiMainWindow_p.h"
0022 
0023 #include <QPainter>
0024 #include <QDebug>
0025 
0026 #include <KConfigGroup>
0027 
0028 #include <KDbUtils>
0029 
0030 #include <kexiutils/SmallToolButton.h>
0031 #include <kexiutils/KexiTester.h>
0032 #include <kexiutils/KexiFadeWidgetEffect.h>
0033 #include <KexiIcon.h>
0034 #include <core/kexipartmanager.h>
0035 #include <KexiAssistantWidget.h>
0036 
0037 KexiWindowContainer::KexiWindowContainer(QWidget* parent)
0038     : QWidget(parent)
0039     , window(0)
0040     , lyr(new QVBoxLayout(this))
0041 {
0042     lyr->setContentsMargins(0, 0, 0, 0);
0043 }
0044 KexiWindowContainer::~KexiWindowContainer()
0045 {
0046     //! @todo warning if saveSettings() failed?
0047     if (window) {
0048         window->saveSettings();
0049         delete (KexiWindow*)window;
0050     }
0051 }
0052 
0053 void KexiWindowContainer::setWindow(KexiWindow* w)
0054 {
0055     window = w;
0056     if (w)
0057         lyr->addWidget(w);
0058 }
0059 
0060 // ---
0061 
0062 EmptyMenuContentWidget::EmptyMenuContentWidget(QWidget* parent)
0063  : QWidget(parent)
0064 {
0065     setAutoFillBackground(true);
0066     alterBackground();
0067 }
0068 
0069 void EmptyMenuContentWidget::alterBackground()
0070 {
0071     QPalette pal(palette());
0072     QColor bg(pal.color(QPalette::Window));
0073     bg.setAlpha(200);
0074     pal.setColor(QPalette::Window, bg);
0075     setPalette(pal);
0076 }
0077 
0078 void EmptyMenuContentWidget::changeEvent(QEvent *e)
0079 {
0080     if (e->type() == QEvent::PaletteChange) {
0081         alterBackground();
0082     }
0083     QWidget::changeEvent(e);
0084 }
0085 
0086 //! @todo KEXI3 is KexiMenuWidgetStyle needed?
0087 #if 0
0088 KexiMenuWidgetStyle::KexiMenuWidgetStyle(QStyle *style, QObject *parent)
0089     : KexiUtils::StyleProxy(style, parent)
0090 {
0091 }
0092 
0093 KexiMenuWidgetStyle::~KexiMenuWidgetStyle()
0094 {
0095 }
0096 
0097 void KexiMenuWidgetStyle::drawControl(ControlElement element, const QStyleOption *option,
0098                          QPainter *painter, const QWidget *widget = 0) const
0099 {
0100     if (element == QStyle::CE_MenuItem
0101         && (option->state & QStyle::State_Selected) && (option->state & QStyle::State_Enabled)
0102         && parentStyle()->objectName() == QLatin1String("oxygen"))
0103     {
0104         // Ugly fix for visual glitch of oxygen; no chance for improvement since
0105         // we've forked QMenu and oxygen checks for qobject_cast<QMenu*> directly.
0106         QColor c(option->palette.color(QPalette::Window));
0107         int h, s, v, a;
0108         c.getHsv(&h, &s, &v, &a);
0109         // Why 0.91208791? I knew you're curious. There are some algorithms in Oxygen
0110         // to make color a bit lighter. They are not in the public API nor they are simple.
0111         // So the number was computed by me to find the proper value for the color
0112         // (the accuracy is quite OK).
0113         // It's also related to the fact that Oxygen's menus have gradient background.
0114         // A lot of computation happens under the mask...
0115         c.setHsv(h, s, v * 0.91208791, a);
0116         painter->fillRect(option->rect.x() + 6, option->rect.y() + 6,
0117                           option->rect.width() - 12, option->rect.height() - 12,
0118                           c);
0119     }
0120     KexiUtils::StyleProxy::drawControl(element, option, painter, widget);
0121 }
0122 #endif
0123 
0124 KexiMainMenu::KexiMainMenu(KexiTabbedToolBar *toolBar, QWidget* parent)
0125  : QWidget(parent),
0126     m_toolBar(toolBar), m_initialized(false)
0127 {
0128     m_content = 0;
0129     m_selectFirstItem = false;
0130 }
0131 
0132 KexiMainMenu::~KexiMainMenu()
0133 {
0134     delete (QWidget*)m_contentWidget;
0135 }
0136 
0137 bool KexiMainMenu::eventFilter(QObject * watched, QEvent* event)
0138 {
0139     if (event->type() == QEvent::MouseButtonPress && watched == m_content && !m_contentWidget) {
0140         emit contentAreaPressed();
0141     }
0142     else if (event->type() == QEvent::KeyPress) {
0143         QKeyEvent* ke = static_cast<QKeyEvent*>(event);
0144         if ((ke->key() == Qt::Key_Escape) && ke->modifiers() == Qt::NoModifier) {
0145             emit hideContentsRequested();
0146             return true;
0147         }
0148     }
0149     return QWidget::eventFilter(watched, event);
0150 }
0151 
0152 void KexiMainMenu::setContent(QWidget *contentWidget)
0153 {
0154     if (m_menuWidget && m_persistentlySelectedAction) {
0155         m_menuWidget->setPersistentlySelectedAction(
0156             m_persistentlySelectedAction,
0157             m_persistentlySelectedAction->persistentlySelected());
0158     }
0159     /*if (m_menuWidget->persistentlySelectedAction())
0160         qDebug() << "****" << m_menuWidget->persistentlySelectedAction()->objectName();*/
0161     KexiFadeWidgetEffect *fadeEffect = 0;
0162 
0163     if (m_contentWidget && contentWidget) {
0164         fadeEffect = new KexiFadeWidgetEffect(m_content);
0165     }
0166     if (m_contentWidget)
0167         m_contentWidget->deleteLater();
0168     m_contentWidget = contentWidget;
0169     if (m_contentWidget) {
0170         QPalette contentWidgetPalette(m_contentWidget->palette());
0171         contentWidgetPalette.setBrush(QPalette::Active, QPalette::Window, contentWidgetPalette.brush(QPalette::Active, QPalette::Base));
0172         contentWidgetPalette.setBrush(QPalette::Inactive, QPalette::Window, contentWidgetPalette.brush(QPalette::Inactive, QPalette::Base));
0173         contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::Window, contentWidgetPalette.brush(QPalette::Disabled, QPalette::Base));
0174         contentWidgetPalette.setBrush(QPalette::Active, QPalette::WindowText, contentWidgetPalette.brush(QPalette::Active, QPalette::Text));
0175         contentWidgetPalette.setBrush(QPalette::Inactive, QPalette::WindowText, contentWidgetPalette.brush(QPalette::Inactive, QPalette::Text));
0176         contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::WindowText, contentWidgetPalette.brush(QPalette::Disabled, QPalette::Text));
0177         const QColor highlightDisabled(KexiUtils::blendedColors(
0178                                      contentWidgetPalette.color(QPalette::Active, QPalette::Highlight),
0179                                      contentWidgetPalette.color(QPalette::Disabled, QPalette::Window), 1, 2));
0180         contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::Highlight, highlightDisabled);
0181         const QColor highlightedTextDisabled(KexiUtils::blendedColors(
0182                                      contentWidgetPalette.color(QPalette::Active, QPalette::HighlightedText),
0183                                      contentWidgetPalette.color(QPalette::Disabled, QPalette::WindowText), 1, 2));
0184         contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::HighlightedText, highlightedTextDisabled);
0185         m_contentWidget->setPalette(contentWidgetPalette);
0186         for(QAbstractScrollArea *area : m_contentWidget->findChildren<QAbstractScrollArea*>()) {
0187             QPalette pal(area->viewport()->palette());
0188             pal.setBrush(QPalette::Disabled, QPalette::Base, contentWidgetPalette.brush(QPalette::Disabled, QPalette::Base));
0189             area->viewport()->setPalette(pal);
0190         }
0191 
0192         m_contentWidget->setAutoFillBackground(true);
0193         m_contentWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
0194         m_contentWidget->setContentsMargins(0, 0, 0, 0);
0195         m_contentLayout->addWidget(m_contentWidget);
0196         m_contentLayout->setCurrentWidget(m_contentWidget);
0197         m_contentWidget->setFocus();
0198         m_contentWidget->installEventFilter(this);
0199         //connect(m_contentWidget, SIGNAL(destroyed()), this, SLOT(contentWidgetDestroyed()));
0200     }
0201     if (fadeEffect) {
0202         if (m_contentWidget)
0203             m_contentLayout->update();
0204 
0205         QTimer::singleShot(10, fadeEffect, SLOT(start()));
0206     }
0207 }
0208 
0209 const QWidget *KexiMainMenu::contentWidget() const
0210 {
0211     return m_contentWidget;
0212 }
0213 
0214 void KexiMainMenu::setPersistentlySelectedAction(KexiMenuWidgetAction* action, bool set)
0215 {
0216     m_persistentlySelectedAction = action;
0217     m_persistentlySelectedAction->setPersistentlySelected(set);
0218 }
0219 
0220 /*
0221 void KexiMainMenu::setActiveAction(QAction* action = 0)
0222 {
0223     if (!action && !m_menuWidget->actions().isEmpty()) {
0224         action = actions().first();
0225     }
0226     if (action) {
0227         m_menuWidget->setActiveAction(action);
0228     }
0229 }
0230 */
0231 
0232 void KexiMainMenu::selectFirstItem()
0233 {
0234     m_selectFirstItem = true;
0235 }
0236 
0237 void KexiMainMenu::showEvent(QShowEvent * event)
0238 {
0239     if (!m_initialized) {
0240         m_initialized = true;
0241         KActionCollection *ac = KexiMainWindowIface::global()->actionCollection();
0242         QHBoxLayout *hlyr = new QHBoxLayout(this);
0243 
0244         hlyr->setSpacing(0);
0245         hlyr->setMargin(0);
0246 
0247         m_menuWidget = new KexiMenuWidget;
0248 //! @todo KEXI3 is KexiMenuWidgetStyle needed?
0249 #if 0
0250         QString styleName(m_menuWidget->style()->objectName());
0251         if (KDE::version() < KDE_MAKE_VERSION(4, 8, 0) // a fix is apparently needed for glitch in KDE < 4.8
0252             && styleName == "oxygen")
0253         {
0254             KexiMenuWidgetStyle *customStyle = new KexiMenuWidgetStyle(m_menuWidget->style()->objectName(), this);
0255             m_menuWidget->setStyle(customStyle);
0256         }
0257 #endif
0258         m_menuWidget->installEventFilter(this);
0259         m_menuWidget->setFocusPolicy(Qt::StrongFocus);
0260         setFocusProxy(m_menuWidget);
0261         m_menuWidget->setFrame(false);
0262         m_menuWidget->setAutoFillBackground(true);
0263 
0264         m_menuWidget->addAction(ac->action("project_welcome"));
0265         m_menuWidget->addAction(ac->action("project_open"));
0266         m_menuWidget->addAction(ac->action("project_close"));
0267         m_menuWidget->addSeparator();
0268         m_menuWidget->addAction(ac->action("project_new"));
0269         m_menuWidget->addAction(ac->action("project_import_export_send"));
0270 #ifdef KEXI_SHOW_UNIMPLEMENTED
0271         m_menuWidget->addAction(ac->action("project_properties"));
0272         //! @todo project information
0273         m_menuWidget->addAction(ac->action("settings"));
0274 #endif
0275         m_menuWidget->addSeparator();
0276         m_menuWidget->addAction(ac->action("quit"));
0277         hlyr->addWidget(m_menuWidget);
0278 
0279         m_content = new EmptyMenuContentWidget;
0280         m_content->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
0281         m_content->installEventFilter(this);
0282         m_mainContentLayout = new QVBoxLayout;
0283         hlyr->addLayout(m_mainContentLayout);
0284         m_contentLayout = new QStackedLayout(m_content);
0285         m_contentLayout->setStackingMode(QStackedLayout::StackAll);
0286         m_contentLayout->setContentsMargins(0, 0, 0, 0);
0287         m_mainContentLayout->addWidget(m_content);
0288         hlyr->setStretchFactor(m_mainContentLayout, 1);
0289     }
0290     QWidget::showEvent(event);
0291     if (m_selectFirstItem && !m_menuWidget->actions().isEmpty()) {
0292         QAction* action = m_menuWidget->actions().first();
0293         m_menuWidget->setActiveAction(action);
0294         m_selectFirstItem = false;
0295     }
0296 }
0297 
0298 // ---
0299 
0300 KexiTabbedToolBar::Private::Private(KexiTabbedToolBar *t)
0301             : q(t), createWidgetToolBar(0)
0302 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
0303             , tabToRaise(-1)
0304 #endif
0305             , rolledUp(false)
0306 {
0307 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
0308     tabRaiseTimer.setSingleShot(true);
0309     tabRaiseTimer.setInterval(300);
0310 #endif
0311     tabBarAnimation.setPropertyName("opacity");
0312     tabBarAnimation.setDuration(500);
0313     connect(&tabBarAnimation, SIGNAL(finished()), q, SLOT(tabBarAnimationFinished()));
0314     tabIndex = 0;
0315     lowestIndex = 2;
0316 }
0317 
0318 //! @return true if @a style name is specific regarding tab styling
0319 static bool isSpecificTabStyle(const QString &styleName)
0320 {
0321     return styleName == "oxygen" || styleName == "qtcurve" || styleName == "gtk+"
0322            || styleName == "gtk2";
0323 }
0324 
0325 KexiTabbedToolBarStyle::KexiTabbedToolBarStyle(const QString &baseStyleName)
0326   : QProxyStyle(baseStyleName)
0327 {
0328 }
0329 
0330 KexiTabbedToolBarStyle::~KexiTabbedToolBarStyle()
0331 {
0332 }
0333 
0334 void KexiTabbedToolBarStyle::drawControl(ControlElement element, const QStyleOption *option,
0335                                          QPainter *painter, const QWidget *widget) const
0336 {
0337     const QString styleName(baseStyle()->objectName());
0338     qreal origOpacity = -1.0;
0339     if (element == CE_TabBarTab) {
0340         const QStyleOptionTab* opt
0341             = qstyleoption_cast<const QStyleOptionTab*>(option);
0342         const QTabBar* tabBar = qobject_cast<const QTabBar*>(widget);
0343         KexiTabbedToolBar* tbar = tabBar
0344             ? qobject_cast<KexiTabbedToolBar*>(tabBar->parentWidget()) : 0;
0345         if (opt && tbar) {
0346             const int index = tabBar->tabAt(opt->rect.center());
0347             if (index == KEXITABBEDTOOLBAR_SPACER_TAB_INDEX)
0348                 return;
0349             bool mouseOver = opt->state & QStyle::State_MouseOver;
0350             bool unselectedOrMenuVisible
0351                 = !(opt->state & State_Selected) || tbar->mainMenuVisible();
0352             if (unselectedOrMenuVisible) {
0353                 if (styleName == "bespin") {
0354                     unselectedOrMenuVisible = false;
0355                 }
0356             }
0357 
0358             QStyleOptionTab newOpt(*opt);
0359             const bool specificStyle = isSpecificTabStyle(styleName);
0360             newOpt.text = (specificStyle ? " " : "")
0361                     + tabBar->tabText(index)
0362                     + (specificStyle ? " " : "");
0363             if (!mouseOver
0364                 && unselectedOrMenuVisible
0365                 && index > 0)
0366             {
0367                 if (tbar->mainMenuVisible())
0368                     newOpt.state &= ~QStyle::State_HasFocus;
0369                 QProxyStyle::drawControl(CE_TabBarTabLabel, &newOpt, painter, widget);
0370                 return;
0371             }
0372             else if (index == 0) {
0373                 QBrush bg;
0374                 newOpt.state |= State_Selected;
0375                 if (tbar->mainMenuVisible()) {
0376                     bg = newOpt.palette.brush(QPalette::Active, QPalette::Highlight);
0377                     if (!specificStyle) {
0378                         newOpt.palette.setBrush(QPalette::WindowText,
0379                                                 newOpt.palette.brush(QPalette::Active, QPalette::HighlightedText));
0380                         newOpt.palette.setBrush(QPalette::ButtonText,
0381                                                 newOpt.palette.brush(QPalette::Active, QPalette::HighlightedText));
0382                     }
0383                 }
0384                 else {
0385                     if (styleName == "fusion") {
0386                         bg = newOpt.palette.brush(QPalette::Active, QPalette::Button);
0387                     } else {
0388                         bg = Qt::transparent;
0389                     }
0390                 }
0391                 QFont origFont(painter->font());
0392                 QFont f(origFont);
0393                 f.setBold(true);
0394                 painter->setFont(f);
0395                 newOpt.palette.setBrush(QPalette::Window, bg);
0396                 newOpt.palette.setBrush(QPalette::Button, // needed e.g. for Plastique style
0397                                         bg);
0398                 QProxyStyle::drawControl(element, &newOpt, painter, widget);
0399                 painter->setFont(origFont);
0400                 if (!mouseOver || tbar->mainMenuVisible() || styleName == "gtk+") {
0401                     return;
0402                 }
0403             }
0404             if (index > 0 || mouseOver) {
0405                 const QPalette::ColorGroup hbGroup =  (styleName == "oxygen")
0406                         ? QPalette::Active : QPalette::Inactive;
0407                 const QBrush hb(newOpt.palette.brush(hbGroup, QPalette::Highlight));
0408                 newOpt.palette.setBrush(QPalette::Window, hb);
0409                 newOpt.palette.setBrush(QPalette::Button, hb); // needed e.g. for Plastique style
0410                 if (mouseOver && (index != tbar->currentIndex() || tbar->mainMenuVisible())) {
0411                     // use lower opacity for diplaying hovered tabs
0412                     origOpacity = painter->opacity();
0413                     painter->setOpacity(styleName == "qtcurve" ? 0.2 : 0.3);
0414                     newOpt.state |= State_Selected;
0415                 }
0416                 else {
0417                     if (!specificStyle) {
0418                         newOpt.palette.setBrush(QPalette::WindowText,
0419                                                 newOpt.palette.brush(QPalette::Inactive, QPalette::HighlightedText));
0420                         newOpt.palette.setBrush(QPalette::ButtonText,
0421                                                 newOpt.palette.brush(QPalette::Inactive, QPalette::HighlightedText));
0422                     }
0423                 }
0424                 if (index == tbar->currentIndex() && styleName == "qtcurve") {
0425                     origOpacity = painter->opacity();
0426                     painter->setOpacity(0.5);
0427                 }
0428                 (newOpt.state |= State_Active) ^= State_Active;
0429                 QProxyStyle::drawControl(element, &newOpt, painter, widget);
0430                 if (origOpacity != -1.0) {
0431                     // restore opacity and draw labels using full this opacity
0432                     painter->setOpacity(origOpacity);
0433                     if (index > 0) {
0434                         QProxyStyle::drawControl(CE_TabBarTabLabel, &newOpt, painter, widget);
0435                     }
0436                 }
0437                 return;
0438             }
0439         }
0440     }
0441     else if (element == CE_ToolBar) {
0442         return;
0443     }
0444     QProxyStyle::drawControl(element, option, painter, widget);
0445 }
0446 
0447 void KexiTabbedToolBarStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
0448                                            QPainter *painter, const QWidget *widget) const
0449 {
0450     const QString styleName(baseStyle()->objectName());
0451     if (element == PE_FrameTabWidget) {
0452         return;
0453     }
0454     if (element == PE_FrameTabBarBase) {
0455         const QTabBar* tabBar = qobject_cast<const QTabBar*>(widget);
0456         KexiTabbedToolBar* tbar = tabBar
0457             ? qobject_cast<KexiTabbedToolBar*>(tabBar->parentWidget()) : 0;
0458         if (tbar && tbar->mainMenuVisible() && styleName != "bespin") {
0459             return;
0460         }
0461     }
0462     if (element == QStyle::PE_PanelToolBar || element == QStyle::PE_FrameMenu) {
0463         return;
0464     }
0465     QProxyStyle::drawPrimitive(element, option, painter, widget);
0466 }
0467 
0468 int KexiTabbedToolBarStyle::pixelMetric(PixelMetric metric, const QStyleOption* option,
0469                                         const QWidget* widget) const
0470 {
0471     if (metric == QStyle::PM_SmallIconSize)
0472         return KIconLoader::SizeMedium;
0473     return QProxyStyle::pixelMetric(metric, option, widget);
0474 }
0475 
0476 // ---
0477 
0478 KexiTabbedToolBarTabBar::KexiTabbedToolBarTabBar(QWidget *parent)
0479     : QTabBar(parent)
0480 {
0481     setObjectName("tabbar");
0482     customStyle = new KexiTabbedToolBarStyle(style()->objectName());
0483     customStyle->setParent(this);
0484     setStyle(customStyle);
0485     installEventFilter(parent);
0486     QWidget *mainWindow = KexiMainWindowIface::global()->thisWidget();
0487     mainWindow->installEventFilter(parent);
0488     setAttribute(Qt::WA_Hover, true);
0489 }
0490 
0491 QSize KexiTabbedToolBarTabBar::originalTabSizeHint(int index) const
0492 {
0493     return QTabBar::tabSizeHint(index);
0494 }
0495 
0496 QSize KexiTabbedToolBarTabBar::tabSizeHint(int index) const
0497 {
0498     QSize s = QTabBar::tabSizeHint(index);
0499     QStyleOptionTab ot;
0500     ot.initFrom(this);
0501     QFont f(font());
0502     f.setBold(true);
0503     ot.text = (isSpecificTabStyle(customStyle->baseStyle()->objectName()) ? "  " : "") + tabText(index);
0504     ot.fontMetrics = QFontMetrics(f);
0505     int w = customStyle->pixelMetric(QStyle::PM_TabBarTabHSpace, &ot, this);
0506     if (w <= 0) { // needed e.g. for oxygen
0507         w = fontMetrics().width("   ");
0508     }
0509     if (index == 0) {
0510         s.setWidth(QFontMetrics(f).width(ot.text) + w * 2);
0511         return s;
0512     }
0513     else if (index == KEXITABBEDTOOLBAR_SPACER_TAB_INDEX) {
0514         // fix width of the spacer tab
0515         s.setWidth(w);
0516     }
0517     return s;
0518 }
0519 
0520 void KexiTabbedToolBar::Private::toggleMainMenu()
0521 {
0522     if (q->mainMenuVisible())
0523         hideMainMenu();
0524     else
0525         showMainMenu();
0526 }
0527 
0528 void KexiTabbedToolBar::Private::showMainMenu(const char* actionName)
0529 {
0530     QWidget *mainWindow = KexiMainWindowIface::global()->thisWidget();
0531     if (!mainMenu) {
0532         mainMenu = new KexiMainMenu(q, mainWindow);
0533         connect(mainMenu, SIGNAL(contentAreaPressed()), this, SLOT(hideMainMenu()));
0534         connect(mainMenu, SIGNAL(hideContentsRequested()), this, SLOT(hideContentsOrMainMenu()));
0535     }
0536     updateMainMenuGeometry();
0537     if (actionName) {
0538         q->selectMainMenuItem(actionName);
0539     }
0540     else {
0541         mainMenu->selectFirstItem();
0542     }
0543     mainMenu->show();
0544     mainMenu->setFocus();
0545     q->update(); // tab bar could have a line that should be repainted
0546 }
0547 
0548 void KexiTabbedToolBar::Private::updateMainMenuGeometry()
0549 {
0550     if (!mainMenu)
0551         return;
0552     QWidget *mainWindow = KexiMainWindowIface::global()->thisWidget();
0553     KexiTabbedToolBarTabBar *tabBar = static_cast<KexiTabbedToolBarTabBar*>(q->tabBar());
0554     QPoint pos = q->mapToGlobal(QPoint(0, tabBar->originalTabSizeHint(0).height() - 1));
0555 //     qDebug() << "1." << pos;
0556     pos = mainWindow->mapFromGlobal(pos);
0557 //     qDebug() << "2." << pos;
0558 //     qDebug() << "3." << q->pos();
0559 
0560     QStyleOptionTab ot;
0561     ot.initFrom(tabBar);
0562     int overlap = tabBar->style()->pixelMetric(QStyle::PM_TabBarBaseOverlap, &ot, tabBar)
0563                   - tabBar->style()->pixelMetric(QStyle::PM_TabBarBaseHeight, &ot, tabBar);
0564 //     qDebug() << "4. overlap=" << overlap;
0565 
0566     mainMenu->setGeometry(0, pos.y() - overlap /*- q->y()*/,
0567                           mainWindow->width(),
0568                           mainWindow->height() - pos.y() + overlap /*+ q->y()*/);
0569 }
0570 
0571 void KexiTabbedToolBar::Private::initSearchLineEdit()
0572 {
0573     //! @todo use KexiConfig
0574     KConfigGroup mainWindowGroup(KSharedConfig::openConfig()->group("MainWindow"));
0575     const bool enabled = mainWindowGroup.readEntry("GlobalSearchBoxEnabled", true);
0576     if (enabled && !searchLineEdit) {
0577         searchLineEdit = new KexiSearchLineEdit;
0578         kexiTester() << KexiTestObject(searchLineEdit, "globalSearch.lineEdit");
0579         searchLineEdit->installEventFilter(q);
0580         helpLayer->addWidget(searchLineEdit);
0581     } else if (!enabled && searchLineEdit) {
0582         helpLayer->removeWidget(searchLineEdit);
0583         delete searchLineEdit;
0584         searchLineEdit = nullptr;
0585     }
0586 }
0587 
0588 void KexiTabbedToolBar::activateSearchLineEdit()
0589 {
0590     if (!d->searchLineEdit) {
0591         return;
0592     }
0593     d->searchLineEdit->selectAll();
0594     d->searchLineEdit->setFocus();
0595 }
0596 
0597 void KexiTabbedToolBar::Private::hideMainMenu()
0598 {
0599     if (!mainMenu || !mainMenu->isVisible())
0600         return;
0601     mainMenu->hide();
0602     mainMenu->setContent(0);
0603     q->update();  // tab bar could have a line that should be repainted
0604 }
0605 
0606 void KexiTabbedToolBar::Private::hideContentsOrMainMenu()
0607 {
0608     if (!mainMenu || !mainMenu->isVisible())
0609         return;
0610     if (mainMenu->contentWidget()) {
0611         mainMenu->setContent(0);
0612     }
0613     else {
0614         hideMainMenu();
0615     }
0616 }
0617 
0618 KToolBar *KexiTabbedToolBar::Private::createToolBar(const char *name, const QString& caption)
0619 {
0620     KToolBar *tbar = new KToolBar(q, true /*main toolbar*/, false /*read config*/);
0621     tbar->setIconDimensions(22); //!< @todo make configurable or system-dependent?
0622     // needed e.g. for Windows style to remove the toolbar's frame
0623     tbar->setStyle(customTabBar->customStyle);
0624     toolbarsForName.insert(name, tbar);
0625     tbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
0626     tbar->setObjectName(name);
0627     toolbarsCaptionForName.insert(name, caption);
0628     tabIndex = q->addTab(tbar, caption);
0629     toolbarsVisibleForIndex.append(true);
0630     toolbarsIndexForName.insert(name, tabIndex);
0631     return tbar;
0632 }
0633 
0634 KexiTabbedToolBar::KexiTabbedToolBar(QWidget *parent)
0635         : QTabWidget(parent)
0636         , d(new Private(this))
0637 {
0638     d->customTabBar = new KexiTabbedToolBarTabBar(this);
0639     setTabBar(d->customTabBar);
0640     setStyle(d->customTabBar->customStyle);
0641 
0642     // from ktabwidget.cpp
0643     //! @todo QTabWidget::setTabBar() should be added with this code
0644     //! @todo KEXI3 Are these tabBar() connections useful to port?
0645 #if 0
0646     connect(tabBar(), SIGNAL(contextMenu(int,QPoint)), SLOT(contextMenu(int,QPoint&)));
0647     connect(tabBar(), SIGNAL(tabDoubleClicked(int)), SLOT(mouseDoubleClick(int)));
0648     connect(tabBar(), SIGNAL(newTabRequest()), this, SIGNAL(mouseDoubleClick())); // #185487
0649     connect(tabBar(), SIGNAL(mouseMiddleClick(int)), SLOT(mouseMiddleClick(int)));
0650     connect(tabBar(), SIGNAL(initiateDrag( int )), SLOT(initiateDrag( int )));
0651     connect(tabBar(), SIGNAL(testCanDecode(QDragMoveEvent*,bool*)), SIGNAL(testCanDecode(QDragMoveEvent*,bool*)));
0652     connect(tabBar(), SIGNAL(receivedDropEvent(int,QDropEvent*)), SLOT(receivedDropEvent(int,QDropEvent*)));
0653     connect(tabBar(), SIGNAL(moveTab(int,int)), SLOT(moveTab(int,int)));
0654     connect(tabBar(), SIGNAL(tabCloseRequested(int)), SLOT(closeRequest(int)));
0655 #endif
0656 
0657     setMouseTracking(true); // for mouseMoveEvent()
0658     setWhatsThis(xi18n("Task-oriented toolbar. Groups commands using tabs."));
0659 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
0660     connect(&d->tabRaiseTimer, SIGNAL(timeout()), this, SLOT(slotDelayedTabRaise()));
0661 #endif
0662     connect(tabBar(), SIGNAL(tabBarDoubleClicked(int)), this, SLOT(slotTabDoubleClicked(int)));
0663 
0664     d->ac = KexiMainWindowIface::global()->actionCollection();
0665     QWidget *mainWin = KexiMainWindowIface::global()->thisWidget();
0666     const bool userMode = KexiMainWindowIface::global()->userMode();
0667     KToolBar *tbar;
0668 
0669     slotSettingsChanged(0);//KGlobalSettings::FontChanged
0670     //! @todo KEXI3 port from KGlobalSettings::Private::_k_slotNotifyChange:
0671     //! connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(slotSettingsChanged(int)));
0672 
0673     // help area
0674     QWidget *helpWidget = new QWidget(this);
0675     helpWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
0676     d->helpLayer = new QHBoxLayout(helpWidget);
0677     d->helpLayer->setContentsMargins(0, 0, 0, 0);
0678 
0679     // * HELP MENU
0680     // add help menu actions... (KexiTabbedToolBar depends on them)
0681     d->helpMenu = new KHelpMenu(this, KAboutData::applicationData(),
0682                                 true/*showWhatsThis*/);
0683     QAction* help_report_bug_action = d->helpMenu->action(KHelpMenu::menuReportBug);
0684     d->ac->addAction(help_report_bug_action->objectName(), help_report_bug_action);
0685     QObject::disconnect(help_report_bug_action, 0, 0, 0);
0686     QObject::connect(help_report_bug_action, SIGNAL(triggered()), mainWin, SLOT(slotReportBug()));
0687     help_report_bug_action->setText(xi18nc("Report a bug or wish for Kexi application", "Report a &Bug or Wish..."));
0688     help_report_bug_action->setIcon(koIcon("tools-report-bug")); // good icon for toolbar
0689     help_report_bug_action->setWhatsThis(xi18n("Files a bug or wish for Kexi application."));
0690     QAction* help_whats_this_action =  d->helpMenu->action(KHelpMenu::menuWhatsThis);
0691     d->ac->addAction(help_whats_this_action->objectName(), help_whats_this_action);
0692     help_whats_this_action->setWhatsThis(xi18n("Activates a \"What's This?\" tool."));
0693     QAction* help_contents_action = d->helpMenu->action(KHelpMenu::menuHelpContents);
0694     d->ac->addAction(help_contents_action->objectName(), help_contents_action);
0695     help_contents_action->setText(xi18n("Help"));
0696     help_contents_action->setWhatsThis(xi18n("Shows Kexi Handbook."));
0697     QAction* help_about_app_action = d->helpMenu->action(KHelpMenu::menuAboutApp);
0698     d->ac->addAction(help_about_app_action->objectName(), help_about_app_action);
0699     help_about_app_action->setWhatsThis(xi18n("Shows information about Kexi application."));
0700     QAction* help_about_kde_action = d->helpMenu->action(KHelpMenu::menuAboutKDE);
0701     d->ac->addAction(help_about_kde_action->objectName(), help_about_kde_action);
0702     help_about_kde_action->setWhatsThis(xi18n("Shows information about KDE."));
0703     QAction* help_switch_language_action = d->helpMenu->action(KHelpMenu::menuSwitchLanguage);
0704     if (help_switch_language_action) {
0705         d->ac->addAction(help_switch_language_action->objectName(), help_switch_language_action);
0706     }
0707     // extra action such as help_donate may be confusing for the user or conflicting with existing so hide it
0708     QAction *extraAction = d->helpMenu->action(static_cast<KHelpMenu::MenuId>(KHelpMenu::menuSwitchLanguage + 1));
0709     if (extraAction) {
0710         extraAction->setVisible(false);
0711     }
0712 
0713     QAction *action_show_help_menu = d->ac->action("help_show_menu");
0714     KexiSmallToolButton *btn = new KexiSmallToolButton(koIcon("help-about"), QString(), helpWidget);
0715     btn->setToolButtonStyle(Qt::ToolButtonIconOnly);
0716     btn->setPopupMode(QToolButton::InstantPopup);
0717     btn->setToolTip(action_show_help_menu->toolTip());
0718     btn->setWhatsThis(action_show_help_menu->whatsThis());
0719     btn->setFocusPolicy(Qt::NoFocus);
0720     QStyleOptionToolButton opt;
0721     opt.initFrom(btn);
0722     int w = btn->sizeHint().width();
0723     int wAdd = btn->style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, btn);
0724     if (w <= (2 * (wAdd + 1))) {
0725         w += wAdd + 2;
0726     }
0727     btn->setMinimumWidth(w);
0728     connect(action_show_help_menu, SIGNAL(triggered()), btn, SLOT(showMenu()));
0729     d->helpLayer->addWidget(btn);
0730     btn->setMenu(d->helpMenu->menu());
0731     setCornerWidget(helpWidget, Qt::TopRightCorner);
0732     d->initSearchLineEdit();
0733 
0734     // needed e.g. for Windows style to remove the toolbar's frame
0735     QWidget *dummyWidgetForMainMenu = new QWidget(this);
0736     dummyWidgetForMainMenu->setObjectName("kexi");
0737     addTab(dummyWidgetForMainMenu, KAboutData::applicationData().displayName());
0738     d->toolbarsVisibleForIndex.append(true);
0739     addTab(new QWidget(this), QString()); // dummy for spacer
0740     d->toolbarsVisibleForIndex.append(true);
0741 
0742     if (!userMode) {
0743         d->createWidgetToolBar = d->createToolBar("create", xi18n("Create"));
0744     }
0745 
0746     tbar = d->createToolBar("data", xi18n("Data"));
0747     addAction(tbar, "edit_cut");
0748     addAction(tbar, "edit_copy");
0749     addAction(tbar, "edit_paste");
0750     if (!userMode)
0751         addAction(tbar, "edit_paste_special_data_table");
0752 //! @todo move undo/redo to quickbar:
0753 
0754     tbar = d->createToolBar("external", xi18n("External Data"));
0755     if (!userMode) {
0756         addAction(tbar, "project_import_data_table");
0757         addAction(tbar, "tools_import_tables");
0758     }
0759 
0760     tbar = d->createToolBar("tools", xi18n("Tools"));
0761     addAction(tbar, "tools_compact_database");
0762 
0763 //! @todo move to form plugin
0764     tbar = d->createToolBar("form", xi18n("Form Design"));
0765 
0766 //! @todo move to report plugin
0767     tbar = d->createToolBar("report", xi18n("Report Design"));
0768 
0769     connect(this, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentChanged(int)));
0770     setCurrentWidget(widget(KEXITABBEDTOOLBAR_SPACER_TAB_INDEX + 1)); // the default
0771     setFocusPolicy(Qt::NoFocus);
0772 }
0773 
0774 void KexiTabbedToolBar::Private::setCurrentTab(const QString& name)
0775 {
0776     q->setCurrentWidget(q->toolBar(name));
0777 }
0778 
0779 void KexiTabbedToolBar::Private::hideTab(const QString& name)
0780 {
0781     q->removeTab(q->indexOf(toolbarsForName.value(name)));
0782     toolbarsVisibleForIndex[toolbarsIndexForName.value(name)] = false;
0783 }
0784 
0785 bool KexiTabbedToolBar::Private::isTabVisible(const QString& name) const
0786 {
0787     return q->indexOf(toolbarsForName.value(name)) != -1
0788            && toolbarsVisibleForIndex[toolbarsIndexForName.value(name)];
0789 }
0790 
0791 #ifndef NDEBUG
0792 void KexiTabbedToolBar::Private::debugToolbars() const
0793 {
0794     qDebug() << "QHash<QString, KToolBar*> toolbarsForName:";
0795     for (QHash<QString, KToolBar*>::ConstIterator it(toolbarsForName.constBegin());
0796          it!=toolbarsForName.constEnd(); ++it)
0797     {
0798         qDebug() << it.key() << "->" << it.value();
0799     }
0800     qDebug() << "QHash<QString, int> toolbarsIndexForName:";
0801     for (QHash<QString, int>::ConstIterator it(toolbarsIndexForName.constBegin());
0802          it!=toolbarsIndexForName.constEnd(); ++it)
0803     {
0804         qDebug() << it.key() << "->" << it.value();
0805     }
0806     qDebug() << "QHash<QString, QString> toolbarsCaptionForName:";
0807     for (QHash<QString, QString>::ConstIterator it(toolbarsCaptionForName.constBegin());
0808          it!=toolbarsCaptionForName.constEnd(); ++it)
0809     {
0810         qDebug() << it.key() << "->" << it.value();
0811     }
0812     qDebug() << "QVector<bool> toolbarsVisibleForIndex:";
0813     for (int i = 0; i < toolbarsVisibleForIndex.size(); i++) {
0814         qDebug() << i << "->" << toolbarsVisibleForIndex[i];
0815     }
0816 }
0817 #endif
0818 
0819 void KexiTabbedToolBar::Private::showTab(const QString& name)
0820 {
0821 //    qDebug() << "name:" << name;
0822 //    qDebug() << "toolbarsForName.value(name):" << toolbarsForName.value(name);
0823 //    qDebug() << "toolbarsIndexForName.value(name):" << toolbarsIndexForName.value(name);
0824 //    qDebug() << "q->indexOf(toolbarsForName.value(name))" << q->indexOf(toolbarsForName.value(name));
0825 #ifndef NDEBUG
0826     //debugToolbars();
0827 #endif
0828     if (q->indexOf(toolbarsForName.value(name)) == -1) {
0829         int h = 0;
0830         // count h = invisible tabs before this
0831         for (int i = lowestIndex; i < toolbarsIndexForName.value(name); i++) {
0832             if (!toolbarsVisibleForIndex.at(i))
0833                 h++;
0834         }
0835         q->insertTab(toolbarsIndexForName.value(name) - h,
0836                      toolbarsForName.value(name), toolbarsCaptionForName.value(name));
0837         toolbarsVisibleForIndex[toolbarsIndexForName.value(name)] = true;
0838     }
0839 }
0840 
0841 // ---
0842 
0843 KexiTabbedToolBar::~KexiTabbedToolBar()
0844 {
0845     delete d;
0846 }
0847 
0848 bool KexiTabbedToolBar::mainMenuVisible() const
0849 {
0850     return d->mainMenu && d->mainMenu->isVisible();
0851 }
0852 
0853 QRect KexiTabbedToolBar::tabRect(int index) const
0854 {
0855     return tabBar()->tabRect(index);
0856 }
0857 
0858 KHelpMenu* KexiTabbedToolBar::helpMenu() const
0859 {
0860     return d->helpMenu;
0861 }
0862 
0863 void KexiTabbedToolBar::slotSettingsChanged(int category)
0864 {
0865     Q_UNUSED(category);
0866     //! @todo if (category == KGlobalSettings::FontChanged) {
0867         //! @todo KEXI3 KGlobalSettings::menuFont() not available (using QFontDatabase::systemFont(QFontDatabase::GeneralFont) for now)
0868         //!       https://community.kde.org/Frameworks/Porting_Notes#Global_Settings
0869         setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); // the toolbar acts like a menu
0870     //}
0871 }
0872 
0873 KToolBar* KexiTabbedToolBar::createWidgetToolBar() const
0874 {
0875     return d->createWidgetToolBar;
0876 }
0877 
0878 void KexiTabbedToolBar::mouseMoveEvent(QMouseEvent* event)
0879 {
0880 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
0881     QPoint p = event->pos();
0882     int tab = tabBar()->tabAt(p);
0883     if (d->tabToRaise != -1 && (tab == -1 || tab == currentIndex())) {
0884         d->tabRaiseTimer.stop();
0885         d->tabToRaise = -1;
0886     } else if (d->tabToRaise != tab) {
0887         d->tabRaiseTimer.start();
0888 
0889         d->tabToRaise = tab;
0890     }
0891 #endif
0892     QTabWidget::mouseMoveEvent(event);
0893 }
0894 
0895 void KexiTabbedToolBar::leaveEvent(QEvent* event)
0896 {
0897 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
0898     d->tabRaiseTimer.stop();
0899     d->tabToRaise = -1;
0900 #endif
0901     QTabWidget::leaveEvent(event);
0902 }
0903 
0904 bool KexiTabbedToolBar::eventFilter(QObject* watched, QEvent* event)
0905 {
0906     switch (event->type()) {
0907     case QEvent::MouseButtonPress: {
0908         QWidget *mainWin = KexiMainWindowIface::global()->thisWidget();
0909         // qDebug() << "MouseButtonPress: watched:" << watched << "window()->focusWidget():" << window()->focusWidget();
0910         if (d->searchLineEdit && watched == d->searchLineEdit) {
0911             activateSearchLineEdit(); // custom setFocus() for search box, so it's possible to focus
0912                                       // back on Escape key press
0913             return false;
0914         }
0915         else if (watched == tabBar()) {
0916             QMouseEvent* me = static_cast<QMouseEvent*>(event);
0917             QPoint p = me->pos();
0918             KexiTabbedToolBarTabBar *tb = static_cast<KexiTabbedToolBarTabBar*>(tabBar());
0919             int index = tb->tabAt(p);
0920             if (index == 0) {
0921                 d->toggleMainMenu();
0922                 return true;
0923             }
0924             d->hideMainMenu();
0925             if (index == KEXITABBEDTOOLBAR_SPACER_TAB_INDEX) {
0926                 return true;
0927             }
0928         }
0929         else if (watched == mainWin && d->mainMenu) {
0930             QMouseEvent* me = static_cast<QMouseEvent*>(event);
0931             if (!QRect(d->mainMenu->mapToGlobal(QPoint(0,0)), d->mainMenu->size())
0932                     .contains(mainWin->mapToGlobal(me->pos())))
0933             {
0934                 // hide if clicked outside of the menu
0935                 d->hideMainMenu();
0936             }
0937         }
0938         }
0939         break;
0940     case QEvent::KeyPress: {
0941         QKeyEvent* ke = static_cast<QKeyEvent*>(event);
0942 //         qDebug() << "**********" << QString::number(ke->key(), 16)
0943 //                  << QKeySequence::mnemonic(tabText(0))[0];
0944         if (QKeySequence::mnemonic(tabText(0)) == QKeySequence(ke->key())) {
0945 //             qDebug() << "eat the &File accel";
0946             if (!d->mainMenu || !d->mainMenu->isVisible()) {
0947                 d->showMainMenu();
0948             }
0949             /*this could be unexpected:
0950             else if (d->mainMenu && d->mainMenu->isVisible()) {
0951                 d->hideMainMenu();
0952             }*/
0953             return true;
0954         }
0955         if (d->mainMenu && d->mainMenu->isVisible() && (ke->key() == Qt::Key_Escape) && ke->modifiers() == Qt::NoModifier) {
0956             d->hideContentsOrMainMenu();
0957             return true;
0958         }
0959         break;
0960     }
0961     case QEvent::Resize:
0962         if (watched == KexiMainWindowIface::global()->thisWidget()) {
0963             d->updateMainMenuGeometry();
0964         }
0965         break;
0966     case QEvent::Shortcut: {
0967         QShortcutEvent *se = static_cast<QShortcutEvent*>(event);
0968         if (watched == tabBar() && QKeySequence::mnemonic(tabText(0)) == se->key()) {
0969 //             qDebug() << "eat the &File accel";
0970             if (!d->mainMenu || !d->mainMenu->isVisible()) {
0971                 d->showMainMenu();
0972                 return true;
0973             }
0974         }
0975         break;
0976     }
0977     default:;
0978     }
0979     return QTabWidget::eventFilter(watched, event);
0980 }
0981 
0982 void KexiTabbedToolBar::slotCurrentChanged(int index)
0983 {
0984     if (index == indexOf(d->createWidgetToolBar) && index != -1) {
0985         if (d->createWidgetToolBar->actions().isEmpty()) {
0986             QTimer::singleShot(10, this, SLOT(setupCreateWidgetToolbar()));
0987         }
0988     }
0989     if (d->rolledUp) { // switching the tab rolls down
0990         slotTabDoubleClicked(index);
0991     }
0992     if (index == 0) { // main menu
0993         d->showMainMenu();
0994     }
0995     else {
0996         d->hideMainMenu();
0997     }
0998 }
0999 
1000 void KexiTabbedToolBar::slotTabDoubleClicked(int index)
1001 {
1002     if (index <= 0) {
1003         return; // main item does not count here
1004     }
1005     d->rolledUp = !d->rolledUp;
1006     d->tabBarAnimation.stop();
1007     QWidget *w = widget(index);
1008     if (!w) {
1009         return;
1010     }
1011     w->setGraphicsEffect(&d->tabBarOpacityEffect);
1012     if (d->rolledUp) {
1013         d->tabBarOpacityEffect.setOpacity(1.0);
1014         d->tabBarAnimation.setTargetObject(&d->tabBarOpacityEffect);
1015         d->tabBarAnimation.setStartValue(1.0);
1016         d->tabBarAnimation.setEndValue(0.0);
1017         d->tabBarAnimation.start();
1018     }
1019     else { // roll down
1020         d->tabBarOpacityEffect.setOpacity(0.0);
1021         setMaximumHeight(QWIDGETSIZE_MAX);
1022         widget(d->rolledUpIndex)->show();
1023         widget(d->rolledUpIndex)->setMaximumHeight(QWIDGETSIZE_MAX);
1024         w->setMaximumHeight(QWIDGETSIZE_MAX);
1025         w->show();
1026         d->tabBarAnimation.setTargetObject(&d->tabBarOpacityEffect);
1027         d->tabBarAnimation.setStartValue(0.0);
1028         d->tabBarAnimation.setEndValue(1.0);
1029         d->tabBarAnimation.start();
1030     }
1031 }
1032 
1033 void KexiTabbedToolBar::tabBarAnimationFinished()
1034 {
1035     if (d->rolledUp) {
1036         // hide and collapse the area
1037         widget(currentIndex())->hide();
1038         KexiTabbedToolBarTabBar *tb = static_cast<KexiTabbedToolBarTabBar*>(tabBar());
1039         setFixedHeight(tb->tabSizeHint(currentIndex()).height());
1040         widget(currentIndex())->setFixedHeight(0);
1041         d->rolledUpIndex = currentIndex();
1042     }
1043 }
1044 
1045 void KexiTabbedToolBar::setupCreateWidgetToolbar()
1046 {
1047     if (!d->createWidgetToolBar->actions().isEmpty())
1048         return;
1049 //! @todo separate core object types from custom....
1050     KexiPart::PartInfoList *plist = Kexi::partManager().infoList(); //this list is properly sorted
1051     if (plist) {
1052         foreach(KexiPart::Info *info, *plist) {
1053             QAction* a = info->newObjectAction();
1054             if (a) {
1055                 d->createWidgetToolBar->addAction(a);
1056             } else {
1057                 //! @todo err
1058             }
1059         }
1060     }
1061 }
1062 
1063 void KexiTabbedToolBar::slotDelayedTabRaise()
1064 {
1065 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
1066     QPoint p = mapFromGlobal(QCursor::pos()); // make sure cursor is still over the tab
1067     int tab = tabBar()->tabAt(p);
1068     if (tab != d->tabToRaise) {
1069         d->tabToRaise = -1;
1070     } else if (d->tabToRaise != -1) {
1071         setCurrentIndex(d->tabToRaise);
1072         d->tabToRaise = -1;
1073     }
1074 #endif
1075 }
1076 
1077 KToolBar *KexiTabbedToolBar::toolBar(const QString& name) const
1078 {
1079     return d->toolbarsForName[name];
1080 }
1081 
1082 void KexiTabbedToolBar::addAction(KToolBar *tbar, const char* actionName)
1083 {
1084     QAction *a = d->ac->action(actionName);
1085     if (a)
1086         tbar->addAction(a);
1087 }
1088 
1089 void KexiTabbedToolBar::addAction(const QString& toolBarName, QAction *action)
1090 {
1091     if (!action)
1092         return;
1093     KToolBar *tbar = d->toolbarsForName[toolBarName];
1094     if (!tbar)
1095         return;
1096     tbar->addAction(action);
1097 }
1098 
1099 void KexiTabbedToolBar::addSeparatorAndAction(KToolBar *tbar, const char* actionName)
1100 {
1101     QAction *a = d->ac->action(actionName);
1102     if (a) {
1103         tbar->addSeparator();
1104         tbar->addAction(a);
1105     }
1106 }
1107 
1108 void KexiTabbedToolBar::appendWidgetToToolbar(const QString& name, QWidget* widget)
1109 {
1110     KToolBar *tbar = d->toolbarsForName[name];
1111     if (!tbar) {
1112         return;
1113     }
1114     QAction *action = tbar->addWidget(widget);
1115     d->extraActions.insert(widget, action);
1116 }
1117 
1118 void KexiTabbedToolBar::setWidgetVisibleInToolbar(QWidget* widget, bool visible)
1119 {
1120     QAction *action = d->extraActions[widget];
1121     if (!action) {
1122         return;
1123     }
1124     action->setVisible(visible);
1125 }
1126 
1127 void KexiTabbedToolBar::showMainMenu(const char* actionName)
1128 {
1129     d->showMainMenu(actionName);
1130 }
1131 
1132 void KexiTabbedToolBar::hideMainMenu()
1133 {
1134     d->hideMainMenu();
1135 }
1136 
1137 void KexiTabbedToolBar::toggleMainMenu()
1138 {
1139     d->toggleMainMenu();
1140 }
1141 
1142 void KexiTabbedToolBar::setMainMenuContent(QWidget *w)
1143 {
1144     d->mainMenu->setContent(w);
1145 }
1146 
1147 const QWidget* KexiTabbedToolBar::mainMenuContent()
1148 {
1149     return d->mainMenu->contentWidget();
1150 }
1151 
1152 void KexiTabbedToolBar::selectMainMenuItem(const char *actionName)
1153 {
1154     if (actionName) {
1155         KActionCollection *ac = KexiMainWindowIface::global()->actionCollection();
1156         KexiMenuWidgetAction *a = qobject_cast<KexiMenuWidgetAction*>(ac->action(actionName));
1157         if (a) {
1158             d->mainMenu->setPersistentlySelectedAction(a, true);
1159         }
1160     }
1161 }
1162 
1163 void KexiTabbedToolBar::addSearchableModel(KexiSearchableModel *model)
1164 {
1165     if (!d->searchLineEdit) {
1166         return;
1167     }
1168     d->searchLineEdit->addSearchableModel(model);
1169 }
1170 
1171 void KexiTabbedToolBar::removeSearchableModel(KexiSearchableModel *model)
1172 {
1173     if (!d->searchLineEdit) {
1174         return;
1175     }
1176     d->searchLineEdit->removeSearchableModel(model);
1177 }
1178 
1179 KToolBar* KexiTabbedToolBar::createToolBar(const char* name, const QString& caption)
1180 {
1181     return d->createToolBar(name, caption);
1182 }
1183 
1184 void KexiTabbedToolBar::setCurrentTab(const QString& name)
1185 {
1186     //qDebug() << name;
1187     d->setCurrentTab(name);
1188 }
1189 
1190 void KexiTabbedToolBar::setCurrentTab(int index)
1191 {
1192     setCurrentIndex(d->lowestIndex + index);
1193 }
1194 
1195 void KexiTabbedToolBar::hideTab(const QString& name)
1196 {
1197     //qDebug() << name;
1198     d->hideTab(name);
1199 }
1200 
1201 void KexiTabbedToolBar::showTab(const QString& name)
1202 {
1203     //qDebug() << name;
1204     d->showTab(name);
1205 }
1206 
1207 bool KexiTabbedToolBar::isTabVisible(const QString& name) const
1208 {
1209     return d->isTabVisible(name);
1210 }
1211 
1212 bool KexiTabbedToolBar::isRolledUp()
1213 {
1214     return d->rolledUp;
1215 }
1216 
1217 void KexiTabbedToolBar::toggleRollDown()
1218 {
1219     slotTabDoubleClicked(-1);//use -1 just to rolldown/up the tabbar
1220 }
1221 
1222 // ---
1223 
1224 KexiMainWidget::KexiMainWidget()
1225         : KMainWindow(0, Qt::Widget)
1226         , m_mainWindow(0)
1227 {
1228     setupCentralWidget();
1229 }
1230 
1231 KexiMainWidget::~KexiMainWidget()
1232 {
1233 }
1234 
1235 void KexiMainWidget::setParent(KexiMainWindow* mainWindow)
1236 {
1237     KMainWindow::setParent(mainWindow);
1238     m_mainWindow = mainWindow;
1239 }
1240 
1241 KexiMainWindowTabWidget* KexiMainWidget::tabWidget() const
1242 {
1243     return m_tabWidget;
1244 }
1245 
1246 void KexiMainWidget::setupCentralWidget()
1247 {
1248     QWidget *centralWidget = new QWidget(this);
1249     QVBoxLayout *centralWidgetLyr = new QVBoxLayout(centralWidget);
1250     m_tabWidget = new KexiMainWindowTabWidget(centralWidget, this);
1251     connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentTabIndexChanged(int)));
1252     centralWidgetLyr->setContentsMargins(0, 0, 0, 0);
1253     centralWidgetLyr->setSpacing(0);
1254     centralWidgetLyr->addWidget(m_tabWidget);
1255     setCentralWidget(centralWidget);
1256     layout()->setContentsMargins(0, 0, 0, 0);
1257     layout()->setSpacing(0);
1258 }
1259 
1260 bool KexiMainWidget::queryClose()
1261 {
1262     return m_mainWindow ? m_mainWindow->queryClose() : true;
1263 }
1264 
1265 void KexiMainWidget::slotCurrentTabIndexChanged(int index)
1266 {
1267     KexiWindowContainer* cont = dynamic_cast<KexiWindowContainer*>(m_tabWidget->widget(index));
1268     if (! cont || (KexiWindow*)m_previouslyActiveWindow == cont->window)
1269         return;
1270     if (m_mainWindow)
1271         m_mainWindow->activeWindowChanged(cont->window, (KexiWindow*)m_previouslyActiveWindow);
1272     m_previouslyActiveWindow = cont->window;
1273     emit currentTabIndexChanged(index);
1274 }
1275 
1276 //------------------------------------------
1277 
1278 KexiMainWindow::Private::Private(KexiMainWindow* w)
1279     : wnd(w)
1280 {
1281     actionCollection = new KActionCollection(w);
1282     propEditor = 0;
1283     propEditorDockWidget = 0;
1284     navDockWidget = 0;
1285     propEditorTabWidget = 0;
1286     KexiProjectData *pdata = KexiStartupHandler::global()->projectData();
1287     userMode = KexiStartupHandler::global()->forcedUserMode() /* <-- simply forced the user mode */
1288                /* project has 'user mode' set as default and not 'design mode' override is found: */
1289                || (pdata && pdata->userMode() && !KexiStartupHandler::global()->forcedDesignMode());
1290     isProjectNavigatorVisible = KexiStartupHandler::global()->isProjectNavigatorVisible();
1291     isMainMenuVisible = KexiStartupHandler::global()->isMainMenuVisible();
1292     navigator = 0;
1293     prj = 0;
1294     config = KSharedConfig::openConfig();
1295     nameDialog = 0;
1296     m_findDialog = 0;
1297     focus_before_popup = 0;
1298     action_show_nav = 0;
1299     action_show_propeditor = 0;
1300     action_activate_nav = 0;
1301     action_activate_propeditor = 0;
1302     action_welcome_projects_title_id = -1;
1303     action_welcome_connections_title_id = -1;
1304     forceWindowClosing = false;
1305     insideCloseWindow = false;
1306 #ifndef KEXI_NO_PENDING_DIALOGS
1307     actionToExecuteWhenPendingJobsAreFinished = NoAction;
1308 #endif
1309     propEditorDockSeparatorPos = -1;
1310     navDockSeparatorPos = -1;
1311     wasAutoOpen = false;
1312     windowExistedBeforeCloseProject = false;
1313 #ifndef KEXI_SHOW_UNIMPLEMENTED
1314     dummy_action = new KActionMenu(QString(), wnd);
1315 #endif
1316     forceShowProjectNavigatorOnCreation = false;
1317     forceHideProjectNavigatorOnCreation = false;
1318     navWasVisibleBeforeProjectClosing = false;
1319     saveSettingsForShowProjectNavigator = true;
1320     propertyEditorCollapsed = false;
1321     enable_slotPropertyEditorVisibilityChanged = true;
1322     migrateManager = 0;
1323 }
1324 
1325 KexiMainWindow::Private::~Private()
1326 {
1327     qDeleteAll(m_openedCustomObjectsForItem);
1328 }
1329 
1330 void KexiMainWindow::Private::insertWindow(KexiWindow *window)
1331 {
1332 //! @todo (threads)  QMutexLocker dialogsLocker( &dialogsMutex );
1333     windows.insert(window->id(), window);
1334 #ifndef KEXI_NO_PENDING_DIALOGS
1335     pendingWindows.remove(window->id());
1336 #endif
1337 }
1338 
1339 bool KexiMainWindow::Private::windowContainerExistsFor(int identifier) const
1340 {
1341     return windowContainers.contains(identifier);
1342 }
1343 
1344 void KexiMainWindow::Private::setWindowContainerExistsFor(int identifier, bool set)
1345 {
1346     if (set) {
1347         windowContainers.insert(identifier);
1348     }
1349     else {
1350         windowContainers.remove(identifier);
1351     }
1352 }
1353 
1354 void KexiMainWindow::Private::updateWindowId(KexiWindow *window, int oldItemID)
1355 {
1356 //! @todo (threads)  QMutexLocker dialogsLocker( &dialogsMutex );
1357     windows.remove(oldItemID);
1358 #ifndef KEXI_NO_PENDING_DIALOGS
1359     pendingWindows.remove(oldItemID);
1360 #endif
1361     windows.insert(window->id(), window);
1362 }
1363 
1364 void KexiMainWindow::Private::removeWindow(int identifier)
1365 {
1366 //! @todo (threads)  QMutexLocker dialogsLocker( &dialogsMutex );
1367     windows.remove(identifier);
1368 }
1369 
1370 int KexiMainWindow::Private::openedWindowsCount()
1371 {
1372 //! @todo (threads)  QMutexLocker dialogsLocker( &dialogsMutex );
1373     return windows.count();
1374 }
1375 
1376 //! Used in KexiMainWindowe::closeProject()
1377 void KexiMainWindow::Private::clearWindows()
1378 {
1379 //! @todo (threads)  QMutexLocker dialogsLocker( &dialogsMutex );
1380     windows.clear();
1381 #ifndef KEXI_NO_PENDING_DIALOGS
1382     pendingWindows.clear();
1383 #endif
1384 }
1385 
1386 void KexiMainWindow::Private::showStartProcessMsg(const QStringList& args)
1387 {
1388     wnd->showErrorMessage(xi18nc("@info", "Could not start <application>%1</application> application.",
1389                                  QString::fromLatin1(KEXI_APP_NAME)),
1390                           xi18nc("@info",
1391                                  "Command <command>%1</command> failed.", args.join(" ")));
1392 }
1393 
1394 void KexiMainWindow::Private::updatePropEditorVisibility(Kexi::ViewMode viewMode, KexiPart::Info *info)
1395 {
1396     if (!propEditorDockWidget)
1397         return;
1398     KexiWindow *currentWindow = wnd->currentWindow();
1399     if (!info && currentWindow) {
1400         info = currentWindow->part()->info();
1401     }
1402     const bool visible = (viewMode == Kexi::DesignViewMode)
1403         && ((currentWindow && currentWindow->propertySet()) || (info && info->isPropertyEditorAlwaysVisibleInDesignMode()));
1404     //qDebug() << "visible == " << visible;
1405     enable_slotPropertyEditorVisibilityChanged = false;
1406     if (visible && propertyEditorCollapsed) { // used when we're switching back to a window with propeditor available but collapsed
1407         propEditorDockWidget->setVisible(!visible);
1408         setPropertyEditorTabBarVisible(true);
1409     }
1410     else {
1411         propEditorDockWidget->setVisible(visible);
1412         setPropertyEditorTabBarVisible(false);
1413     }
1414     enable_slotPropertyEditorVisibilityChanged = true;
1415 }
1416 
1417 void KexiMainWindow::Private::setTabBarVisible(KMultiTabBar::KMultiTabBarPosition position, int id,
1418                                                KexiDockWidget *dockWidget, bool visible)
1419 {
1420     KMultiTabBar *mtbar = multiTabBars.value(position);
1421     if (!mtbar) {
1422         return;
1423     }
1424     if (!visible) {
1425         mtbar->removeTab(id);
1426     }
1427     else if (!mtbar->tab(id)) {
1428         mtbar->appendTab(koIcon("document-properties"), id, dockWidget->tabText);
1429         KMultiTabBarTab *tab = mtbar->tab(id);
1430         QObject::connect(tab, SIGNAL(clicked(int)),
1431                          wnd, SLOT(slotMultiTabBarTabClicked(int)),
1432                          Qt::UniqueConnection);
1433     }
1434 }
1435 
1436 void KexiMainWindow::Private::setPropertyEditorTabBarVisible(bool visible)
1437 {
1438     setTabBarVisible(KMultiTabBar::Right, PROPERTY_EDITOR_TABBAR_ID,
1439                      propEditorDockWidget, visible);
1440 }
1441 
1442 QObject *KexiMainWindow::Private::openedCustomObjectsForItem(KexiPart::Item* item, const char* name)
1443 {
1444     if (!item || !name) {
1445         qWarning() << "!item || !name";
1446         return 0;
1447     }
1448     QByteArray key(QByteArray::number(item->identifier()) + name);
1449     return m_openedCustomObjectsForItem.value(key);
1450 }
1451 
1452 void KexiMainWindow::Private::addOpenedCustomObjectForItem(KexiPart::Item* item, QObject* object, const char* name)
1453 {
1454     QByteArray key(QByteArray::number(item->identifier()) + name);
1455     m_openedCustomObjectsForItem.insert(key, object);
1456 }
1457 
1458 KexiFindDialog *KexiMainWindow::Private::findDialog()
1459 {
1460     if (!m_findDialog) {
1461         m_findDialog = new KexiFindDialog(wnd);
1462         m_findDialog->setActions(action_edit_findnext, action_edit_findprev,
1463                                  action_edit_replace, action_edit_replace_all);
1464     }
1465     return m_findDialog;
1466 }
1467 
1468 void KexiMainWindow::Private::updateFindDialogContents(bool createIfDoesNotExist)
1469 {
1470     if (!wnd->currentWindow())
1471         return;
1472     if (!createIfDoesNotExist && (!m_findDialog || !m_findDialog->isVisible()))
1473         return;
1474     KexiSearchAndReplaceViewInterface* iface = currentViewSupportingSearchAndReplaceInterface();
1475     if (!iface) {
1476         if (m_findDialog) {
1477             m_findDialog->setButtonsEnabled(false);
1478             m_findDialog->setLookInColumnList(QStringList(), QStringList());
1479         }
1480         return;
1481     }
1482 //! @todo use ->caption() here, depending on global settings related to displaying captions
1483     findDialog()->setObjectNameForCaption(wnd->currentWindow()->partItem()->name());
1484 
1485     QStringList columnNames;
1486     QStringList columnCaptions;
1487     QString currentColumnName; // for 'look in'
1488     if (!iface->setupFindAndReplace(columnNames, columnCaptions, currentColumnName)) {
1489         m_findDialog->setButtonsEnabled(false);
1490         m_findDialog->setLookInColumnList(QStringList(), QStringList());
1491         return;
1492     }
1493     m_findDialog->setButtonsEnabled(true);
1494     const QString prevColumnName(m_findDialog->currentLookInColumnName());
1495     m_findDialog->setLookInColumnList(columnNames, columnCaptions);
1496     m_findDialog->setCurrentLookInColumnName(prevColumnName);
1497 }
1498 
1499 KexiView *KexiMainWindow::Private::currentViewSupportingAction(const char* actionName) const
1500 {
1501     if (!wnd->currentWindow())
1502         return 0;
1503     KexiView *view = wnd->currentWindow()->selectedView();
1504     if (!view)
1505         return 0;
1506     QAction *action = view->sharedAction(actionName);
1507     if (!action || !action->isEnabled())
1508         return 0;
1509     return view;
1510 }
1511 
1512 KexiSearchAndReplaceViewInterface* KexiMainWindow::Private::currentViewSupportingSearchAndReplaceInterface() const
1513 {
1514     if (!wnd->currentWindow())
1515         return 0;
1516     KexiView *view = wnd->currentWindow()->selectedView();
1517     if (!view)
1518         return 0;
1519     return dynamic_cast<KexiSearchAndReplaceViewInterface*>(view);
1520 }
1521 
1522 tristate KexiMainWindow::Private::showProjectMigrationWizard(
1523     const QString& mimeType, const QString& databaseName, const KDbConnectionData *cdata)
1524 {
1525     //pass arguments
1526     QMap<QString, QString> args;
1527     args.insert("mimeType", mimeType);
1528     args.insert("databaseName", databaseName);
1529     if (cdata) { //pass KDbConnectionData serialized as a string...
1530         QString str;
1531         KDbUtils::serializeMap(cdata->toMap(), &str);
1532         args.insert("connectionData", str);
1533     }
1534 
1535     QDialog *dlg = KexiInternalPart::createModalDialogInstance("org.kexi-project.migration", "migration", wnd, 0, &args);
1536     if (!dlg)
1537         return false; //error msg has been shown by KexiInternalPart
1538 
1539     const int result = dlg->exec();
1540     delete dlg;
1541     if (result != QDialog::Accepted)
1542         return cancelled;
1543 
1544     //open imported project in a new Kexi instance
1545     QString destinationDatabaseName(args["destinationDatabaseName"]);
1546     QString fileName, destinationConnectionShortcut;
1547     if (!destinationDatabaseName.isEmpty()) {
1548         if (args.contains("destinationConnectionShortcut")) {
1549             // server-based
1550             destinationConnectionShortcut = args["destinationConnectionShortcut"];
1551         } else {
1552             // file-based
1553             fileName = destinationDatabaseName;
1554             destinationDatabaseName.clear();
1555         }
1556         tristate res = wnd->openProject(fileName, destinationConnectionShortcut,
1557                                         destinationDatabaseName);
1558         wnd->raise();
1559         return res;
1560     }
1561     return true;
1562 }
1563 
1564 KexiAssistantPage *KexiMainWindow::Private::visibleMainMenuWidgetPage()
1565 {
1566     const KexiAssistantWidget *widget = qobject_cast<const KexiAssistantWidget*>(tabbedToolBar->mainMenuContent());
1567     if (widget && widget->isVisible()) {
1568         return widget->currentPage();
1569     }
1570     return nullptr;
1571 }
1572 
1573 #ifndef KEXI_NO_PENDING_DIALOGS
1574 void KexiMainWindow::Private::executeActionWhenPendingJobsAreFinished()
1575 {
1576     ActionToExecuteWhenPendingJobsAreFinished a = actionToExecuteWhenPendingJobsAreFinished;
1577     actionToExecuteWhenPendingJobsAreFinished = NoAction;
1578     switch (a) {
1579     case QuitAction:
1580         qApp->quit();
1581         break;
1582     case CloseProjectAction:
1583         wnd->closeProject();
1584         break;
1585     default:;
1586     }
1587 }
1588 
1589 KexiWindow *KexiMainWindow::Private::openedWindowFor(const KexiPart::Item* item, PendingJobType &pendingType)
1590 {
1591     return openedWindowFor(item->identifier(), pendingType);
1592 }
1593 
1594 KexiWindow *KexiMainWindow::Private::openedWindowFor(int identifier, PendingJobType &pendingType)
1595 {
1596 //! @todo (threads)  QMutexLocker dialogsLocker( &dialogsMutex );
1597     QHash<int, PendingJobType>::ConstIterator it = pendingWindows.find(identifier);
1598     if (it == pendingWindows.end())
1599         pendingType = NoJob;
1600     else
1601         pendingType = it.value();
1602 
1603     if (pendingType == WindowOpeningJob) {
1604         return 0;
1605     }
1606     return windows.contains(identifier) ? (KexiWindow*)windows.value(identifier) : 0;
1607 }
1608 
1609 void KexiMainWindow::Private::addItemToPendingWindows(const KexiPart::Item* item, PendingJobType jobType)
1610 {
1611 //! @todo (threads)  QMutexLocker dialogsLocker( &dialogsMutex );
1612     pendingWindows.insert(item->identifier(), jobType);
1613 }
1614 
1615 bool KexiMainWindow::Private::pendingWindowsExist()
1616 {
1617     if (pendingWindows.begin() != pendingWindows.end())
1618         qDebug() <<  pendingWindows.constBegin().key() << " " << (int)pendingWindows.constBegin().value();
1619 //! @todo (threads)  QMutexLocker dialogsLocker( &dialogsMutex );
1620     return !pendingWindows.isEmpty();
1621 }
1622 
1623 void KexiMainWindow::Private::removePendingWindow(int identifier)
1624 {
1625 //! @todo (threads)  QMutexLocker dialogsLocker( &dialogsMutex );
1626     pendingWindows.remove(identifier);
1627 }
1628 
1629 #else // KEXI_NO_PENDING_DIALOGS
1630 
1631 KexiWindow *KexiMainWindow::Private::openedWindowFor(int identifier)
1632 {
1633 //! @todo (threads)  QMutexLocker dialogsLocker( &dialogsMutex );
1634     return windows.contains(identifier) ? (KexiWindow*)windows.value(identifier) : 0;
1635 }
1636 #endif