1 /* This file is part of the KDE project
2 Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
3 Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include "KexiMainWindow_p.h"
22
23 #include <QPainter>
24 #include <QDebug>
25
26 #include <KConfigGroup>
27
28 #include <KDbUtils>
29
30 #include <kexiutils/SmallToolButton.h>
31 #include <kexiutils/KexiTester.h>
32 #include <kexiutils/KexiFadeWidgetEffect.h>
33 #include <KexiIcon.h>
34 #include <core/kexipartmanager.h>
35 #include <KexiAssistantWidget.h>
36
KexiWindowContainer(QWidget * parent)37 KexiWindowContainer::KexiWindowContainer(QWidget* parent)
38 : QWidget(parent)
39 , window(0)
40 , lyr(new QVBoxLayout(this))
41 {
42 lyr->setContentsMargins(0, 0, 0, 0);
43 }
~KexiWindowContainer()44 KexiWindowContainer::~KexiWindowContainer()
45 {
46 //! @todo warning if saveSettings() failed?
47 if (window) {
48 window->saveSettings();
49 delete (KexiWindow*)window;
50 }
51 }
52
setWindow(KexiWindow * w)53 void KexiWindowContainer::setWindow(KexiWindow* w)
54 {
55 window = w;
56 if (w)
57 lyr->addWidget(w);
58 }
59
60 // ---
61
EmptyMenuContentWidget(QWidget * parent)62 EmptyMenuContentWidget::EmptyMenuContentWidget(QWidget* parent)
63 : QWidget(parent)
64 {
65 setAutoFillBackground(true);
66 alterBackground();
67 }
68
alterBackground()69 void EmptyMenuContentWidget::alterBackground()
70 {
71 QPalette pal(palette());
72 QColor bg(pal.color(QPalette::Window));
73 bg.setAlpha(200);
74 pal.setColor(QPalette::Window, bg);
75 setPalette(pal);
76 }
77
changeEvent(QEvent * e)78 void EmptyMenuContentWidget::changeEvent(QEvent *e)
79 {
80 if (e->type() == QEvent::PaletteChange) {
81 alterBackground();
82 }
83 QWidget::changeEvent(e);
84 }
85
86 //! @todo KEXI3 is KexiMenuWidgetStyle needed?
87 #if 0
88 KexiMenuWidgetStyle::KexiMenuWidgetStyle(QStyle *style, QObject *parent)
89 : KexiUtils::StyleProxy(style, parent)
90 {
91 }
92
93 KexiMenuWidgetStyle::~KexiMenuWidgetStyle()
94 {
95 }
96
97 void KexiMenuWidgetStyle::drawControl(ControlElement element, const QStyleOption *option,
98 QPainter *painter, const QWidget *widget = 0) const
99 {
100 if (element == QStyle::CE_MenuItem
101 && (option->state & QStyle::State_Selected) && (option->state & QStyle::State_Enabled)
102 && parentStyle()->objectName() == QLatin1String("oxygen"))
103 {
104 // Ugly fix for visual glitch of oxygen; no chance for improvement since
105 // we've forked QMenu and oxygen checks for qobject_cast<QMenu*> directly.
106 QColor c(option->palette.color(QPalette::Window));
107 int h, s, v, a;
108 c.getHsv(&h, &s, &v, &a);
109 // Why 0.91208791? I knew you're curious. There are some algorithms in Oxygen
110 // to make color a bit lighter. They are not in the public API nor they are simple.
111 // So the number was computed by me to find the proper value for the color
112 // (the accuracy is quite OK).
113 // It's also related to the fact that Oxygen's menus have gradient background.
114 // A lot of computation happens under the mask...
115 c.setHsv(h, s, v * 0.91208791, a);
116 painter->fillRect(option->rect.x() + 6, option->rect.y() + 6,
117 option->rect.width() - 12, option->rect.height() - 12,
118 c);
119 }
120 KexiUtils::StyleProxy::drawControl(element, option, painter, widget);
121 }
122 #endif
123
KexiMainMenu(KexiTabbedToolBar * toolBar,QWidget * parent)124 KexiMainMenu::KexiMainMenu(KexiTabbedToolBar *toolBar, QWidget* parent)
125 : QWidget(parent),
126 m_toolBar(toolBar), m_initialized(false)
127 {
128 m_content = 0;
129 m_selectFirstItem = false;
130 }
131
~KexiMainMenu()132 KexiMainMenu::~KexiMainMenu()
133 {
134 delete (QWidget*)m_contentWidget;
135 }
136
eventFilter(QObject * watched,QEvent * event)137 bool KexiMainMenu::eventFilter(QObject * watched, QEvent* event)
138 {
139 if (event->type() == QEvent::MouseButtonPress && watched == m_content && !m_contentWidget) {
140 emit contentAreaPressed();
141 }
142 else if (event->type() == QEvent::KeyPress) {
143 QKeyEvent* ke = static_cast<QKeyEvent*>(event);
144 if ((ke->key() == Qt::Key_Escape) && ke->modifiers() == Qt::NoModifier) {
145 emit hideContentsRequested();
146 return true;
147 }
148 }
149 return QWidget::eventFilter(watched, event);
150 }
151
setContent(QWidget * contentWidget)152 void KexiMainMenu::setContent(QWidget *contentWidget)
153 {
154 if (m_menuWidget && m_persistentlySelectedAction) {
155 m_menuWidget->setPersistentlySelectedAction(
156 m_persistentlySelectedAction,
157 m_persistentlySelectedAction->persistentlySelected());
158 }
159 /*if (m_menuWidget->persistentlySelectedAction())
160 qDebug() << "****" << m_menuWidget->persistentlySelectedAction()->objectName();*/
161 KexiFadeWidgetEffect *fadeEffect = 0;
162
163 if (m_contentWidget && contentWidget) {
164 fadeEffect = new KexiFadeWidgetEffect(m_content);
165 }
166 if (m_contentWidget)
167 m_contentWidget->deleteLater();
168 m_contentWidget = contentWidget;
169 if (m_contentWidget) {
170 QPalette contentWidgetPalette(m_contentWidget->palette());
171 contentWidgetPalette.setBrush(QPalette::Active, QPalette::Window, contentWidgetPalette.brush(QPalette::Active, QPalette::Base));
172 contentWidgetPalette.setBrush(QPalette::Inactive, QPalette::Window, contentWidgetPalette.brush(QPalette::Inactive, QPalette::Base));
173 contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::Window, contentWidgetPalette.brush(QPalette::Disabled, QPalette::Base));
174 contentWidgetPalette.setBrush(QPalette::Active, QPalette::WindowText, contentWidgetPalette.brush(QPalette::Active, QPalette::Text));
175 contentWidgetPalette.setBrush(QPalette::Inactive, QPalette::WindowText, contentWidgetPalette.brush(QPalette::Inactive, QPalette::Text));
176 contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::WindowText, contentWidgetPalette.brush(QPalette::Disabled, QPalette::Text));
177 const QColor highlightDisabled(KexiUtils::blendedColors(
178 contentWidgetPalette.color(QPalette::Active, QPalette::Highlight),
179 contentWidgetPalette.color(QPalette::Disabled, QPalette::Window), 1, 2));
180 contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::Highlight, highlightDisabled);
181 const QColor highlightedTextDisabled(KexiUtils::blendedColors(
182 contentWidgetPalette.color(QPalette::Active, QPalette::HighlightedText),
183 contentWidgetPalette.color(QPalette::Disabled, QPalette::WindowText), 1, 2));
184 contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::HighlightedText, highlightedTextDisabled);
185 m_contentWidget->setPalette(contentWidgetPalette);
186 for(QAbstractScrollArea *area : m_contentWidget->findChildren<QAbstractScrollArea*>()) {
187 QPalette pal(area->viewport()->palette());
188 pal.setBrush(QPalette::Disabled, QPalette::Base, contentWidgetPalette.brush(QPalette::Disabled, QPalette::Base));
189 area->viewport()->setPalette(pal);
190 }
191
192 m_contentWidget->setAutoFillBackground(true);
193 m_contentWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
194 m_contentWidget->setContentsMargins(0, 0, 0, 0);
195 m_contentLayout->addWidget(m_contentWidget);
196 m_contentLayout->setCurrentWidget(m_contentWidget);
197 m_contentWidget->setFocus();
198 m_contentWidget->installEventFilter(this);
199 //connect(m_contentWidget, SIGNAL(destroyed()), this, SLOT(contentWidgetDestroyed()));
200 }
201 if (fadeEffect) {
202 if (m_contentWidget)
203 m_contentLayout->update();
204
205 QTimer::singleShot(10, fadeEffect, SLOT(start()));
206 }
207 }
208
contentWidget() const209 const QWidget *KexiMainMenu::contentWidget() const
210 {
211 return m_contentWidget;
212 }
213
setPersistentlySelectedAction(KexiMenuWidgetAction * action,bool set)214 void KexiMainMenu::setPersistentlySelectedAction(KexiMenuWidgetAction* action, bool set)
215 {
216 m_persistentlySelectedAction = action;
217 m_persistentlySelectedAction->setPersistentlySelected(set);
218 }
219
220 /*
221 void KexiMainMenu::setActiveAction(QAction* action = 0)
222 {
223 if (!action && !m_menuWidget->actions().isEmpty()) {
224 action = actions().first();
225 }
226 if (action) {
227 m_menuWidget->setActiveAction(action);
228 }
229 }
230 */
231
selectFirstItem()232 void KexiMainMenu::selectFirstItem()
233 {
234 m_selectFirstItem = true;
235 }
236
showEvent(QShowEvent * event)237 void KexiMainMenu::showEvent(QShowEvent * event)
238 {
239 if (!m_initialized) {
240 m_initialized = true;
241 KActionCollection *ac = KexiMainWindowIface::global()->actionCollection();
242 QHBoxLayout *hlyr = new QHBoxLayout(this);
243
244 hlyr->setSpacing(0);
245 hlyr->setMargin(0);
246
247 m_menuWidget = new KexiMenuWidget;
248 //! @todo KEXI3 is KexiMenuWidgetStyle needed?
249 #if 0
250 QString styleName(m_menuWidget->style()->objectName());
251 if (KDE::version() < KDE_MAKE_VERSION(4, 8, 0) // a fix is apparently needed for glitch in KDE < 4.8
252 && styleName == "oxygen")
253 {
254 KexiMenuWidgetStyle *customStyle = new KexiMenuWidgetStyle(m_menuWidget->style()->objectName(), this);
255 m_menuWidget->setStyle(customStyle);
256 }
257 #endif
258 m_menuWidget->installEventFilter(this);
259 m_menuWidget->setFocusPolicy(Qt::StrongFocus);
260 setFocusProxy(m_menuWidget);
261 m_menuWidget->setFrame(false);
262 m_menuWidget->setAutoFillBackground(true);
263
264 m_menuWidget->addAction(ac->action("project_welcome"));
265 m_menuWidget->addAction(ac->action("project_open"));
266 m_menuWidget->addAction(ac->action("project_close"));
267 m_menuWidget->addSeparator();
268 m_menuWidget->addAction(ac->action("project_new"));
269 m_menuWidget->addAction(ac->action("project_import_export_send"));
270 #ifdef KEXI_SHOW_UNIMPLEMENTED
271 m_menuWidget->addAction(ac->action("project_properties"));
272 //! @todo project information
273 m_menuWidget->addAction(ac->action("settings"));
274 #endif
275 m_menuWidget->addSeparator();
276 m_menuWidget->addAction(ac->action("quit"));
277 hlyr->addWidget(m_menuWidget);
278
279 m_content = new EmptyMenuContentWidget;
280 m_content->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
281 m_content->installEventFilter(this);
282 m_mainContentLayout = new QVBoxLayout;
283 hlyr->addLayout(m_mainContentLayout);
284 m_contentLayout = new QStackedLayout(m_content);
285 m_contentLayout->setStackingMode(QStackedLayout::StackAll);
286 m_contentLayout->setContentsMargins(0, 0, 0, 0);
287 m_mainContentLayout->addWidget(m_content);
288 hlyr->setStretchFactor(m_mainContentLayout, 1);
289 }
290 QWidget::showEvent(event);
291 if (m_selectFirstItem && !m_menuWidget->actions().isEmpty()) {
292 QAction* action = m_menuWidget->actions().first();
293 m_menuWidget->setActiveAction(action);
294 m_selectFirstItem = false;
295 }
296 }
297
298 // ---
299
Private(KexiTabbedToolBar * t)300 KexiTabbedToolBar::Private::Private(KexiTabbedToolBar *t)
301 : q(t), createWidgetToolBar(0)
302 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
303 , tabToRaise(-1)
304 #endif
305 , rolledUp(false)
306 {
307 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
308 tabRaiseTimer.setSingleShot(true);
309 tabRaiseTimer.setInterval(300);
310 #endif
311 tabBarAnimation.setPropertyName("opacity");
312 tabBarAnimation.setDuration(500);
313 connect(&tabBarAnimation, SIGNAL(finished()), q, SLOT(tabBarAnimationFinished()));
314 tabIndex = 0;
315 lowestIndex = 2;
316 }
317
318 //! @return true if @a style name is specific regarding tab styling
isSpecificTabStyle(const QString & styleName)319 static bool isSpecificTabStyle(const QString &styleName)
320 {
321 return styleName == "oxygen" || styleName == "qtcurve" || styleName == "gtk+"
322 || styleName == "gtk2";
323 }
324
KexiTabbedToolBarStyle(const QString & baseStyleName)325 KexiTabbedToolBarStyle::KexiTabbedToolBarStyle(const QString &baseStyleName)
326 : QProxyStyle(baseStyleName)
327 {
328 }
329
~KexiTabbedToolBarStyle()330 KexiTabbedToolBarStyle::~KexiTabbedToolBarStyle()
331 {
332 }
333
drawControl(ControlElement element,const QStyleOption * option,QPainter * painter,const QWidget * widget) const334 void KexiTabbedToolBarStyle::drawControl(ControlElement element, const QStyleOption *option,
335 QPainter *painter, const QWidget *widget) const
336 {
337 const QString styleName(baseStyle()->objectName());
338 qreal origOpacity = -1.0;
339 if (element == CE_TabBarTab) {
340 const QStyleOptionTab* opt
341 = qstyleoption_cast<const QStyleOptionTab*>(option);
342 const QTabBar* tabBar = qobject_cast<const QTabBar*>(widget);
343 KexiTabbedToolBar* tbar = tabBar
344 ? qobject_cast<KexiTabbedToolBar*>(tabBar->parentWidget()) : 0;
345 if (opt && tbar) {
346 const int index = tabBar->tabAt(opt->rect.center());
347 if (index == KEXITABBEDTOOLBAR_SPACER_TAB_INDEX)
348 return;
349 bool mouseOver = opt->state & QStyle::State_MouseOver;
350 bool unselectedOrMenuVisible
351 = !(opt->state & State_Selected) || tbar->mainMenuVisible();
352 if (unselectedOrMenuVisible) {
353 if (styleName == "bespin") {
354 unselectedOrMenuVisible = false;
355 }
356 }
357
358 QStyleOptionTab newOpt(*opt);
359 const bool specificStyle = isSpecificTabStyle(styleName);
360 newOpt.text = (specificStyle ? " " : "")
361 + tabBar->tabText(index)
362 + (specificStyle ? " " : "");
363 if (!mouseOver
364 && unselectedOrMenuVisible
365 && index > 0)
366 {
367 if (tbar->mainMenuVisible())
368 newOpt.state &= ~QStyle::State_HasFocus;
369 QProxyStyle::drawControl(CE_TabBarTabLabel, &newOpt, painter, widget);
370 return;
371 }
372 else if (index == 0) {
373 QBrush bg;
374 newOpt.state |= State_Selected;
375 if (tbar->mainMenuVisible()) {
376 bg = newOpt.palette.brush(QPalette::Active, QPalette::Highlight);
377 if (!specificStyle) {
378 newOpt.palette.setBrush(QPalette::WindowText,
379 newOpt.palette.brush(QPalette::Active, QPalette::HighlightedText));
380 newOpt.palette.setBrush(QPalette::ButtonText,
381 newOpt.palette.brush(QPalette::Active, QPalette::HighlightedText));
382 }
383 }
384 else {
385 if (styleName == "fusion") {
386 bg = newOpt.palette.brush(QPalette::Active, QPalette::Button);
387 } else {
388 bg = Qt::transparent;
389 }
390 }
391 QFont origFont(painter->font());
392 QFont f(origFont);
393 f.setBold(true);
394 painter->setFont(f);
395 newOpt.palette.setBrush(QPalette::Window, bg);
396 newOpt.palette.setBrush(QPalette::Button, // needed e.g. for Plastique style
397 bg);
398 QProxyStyle::drawControl(element, &newOpt, painter, widget);
399 painter->setFont(origFont);
400 if (!mouseOver || tbar->mainMenuVisible() || styleName == "gtk+") {
401 return;
402 }
403 }
404 if (index > 0 || mouseOver) {
405 const QPalette::ColorGroup hbGroup = (styleName == "oxygen")
406 ? QPalette::Active : QPalette::Inactive;
407 const QBrush hb(newOpt.palette.brush(hbGroup, QPalette::Highlight));
408 newOpt.palette.setBrush(QPalette::Window, hb);
409 newOpt.palette.setBrush(QPalette::Button, hb); // needed e.g. for Plastique style
410 if (mouseOver && (index != tbar->currentIndex() || tbar->mainMenuVisible())) {
411 // use lower opacity for diplaying hovered tabs
412 origOpacity = painter->opacity();
413 painter->setOpacity(styleName == "qtcurve" ? 0.2 : 0.3);
414 newOpt.state |= State_Selected;
415 }
416 else {
417 if (!specificStyle) {
418 newOpt.palette.setBrush(QPalette::WindowText,
419 newOpt.palette.brush(QPalette::Inactive, QPalette::HighlightedText));
420 newOpt.palette.setBrush(QPalette::ButtonText,
421 newOpt.palette.brush(QPalette::Inactive, QPalette::HighlightedText));
422 }
423 }
424 if (index == tbar->currentIndex() && styleName == "qtcurve") {
425 origOpacity = painter->opacity();
426 painter->setOpacity(0.5);
427 }
428 (newOpt.state |= State_Active) ^= State_Active;
429 QProxyStyle::drawControl(element, &newOpt, painter, widget);
430 if (origOpacity != -1.0) {
431 // restore opacity and draw labels using full this opacity
432 painter->setOpacity(origOpacity);
433 if (index > 0) {
434 QProxyStyle::drawControl(CE_TabBarTabLabel, &newOpt, painter, widget);
435 }
436 }
437 return;
438 }
439 }
440 }
441 else if (element == CE_ToolBar) {
442 return;
443 }
444 QProxyStyle::drawControl(element, option, painter, widget);
445 }
446
drawPrimitive(PrimitiveElement element,const QStyleOption * option,QPainter * painter,const QWidget * widget) const447 void KexiTabbedToolBarStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
448 QPainter *painter, const QWidget *widget) const
449 {
450 const QString styleName(baseStyle()->objectName());
451 if (element == PE_FrameTabWidget) {
452 return;
453 }
454 if (element == PE_FrameTabBarBase) {
455 const QTabBar* tabBar = qobject_cast<const QTabBar*>(widget);
456 KexiTabbedToolBar* tbar = tabBar
457 ? qobject_cast<KexiTabbedToolBar*>(tabBar->parentWidget()) : 0;
458 if (tbar && tbar->mainMenuVisible() && styleName != "bespin") {
459 return;
460 }
461 }
462 if (element == QStyle::PE_PanelToolBar || element == QStyle::PE_FrameMenu) {
463 return;
464 }
465 QProxyStyle::drawPrimitive(element, option, painter, widget);
466 }
467
pixelMetric(PixelMetric metric,const QStyleOption * option,const QWidget * widget) const468 int KexiTabbedToolBarStyle::pixelMetric(PixelMetric metric, const QStyleOption* option,
469 const QWidget* widget) const
470 {
471 if (metric == QStyle::PM_SmallIconSize)
472 return KIconLoader::SizeMedium;
473 return QProxyStyle::pixelMetric(metric, option, widget);
474 }
475
476 // ---
477
KexiTabbedToolBarTabBar(QWidget * parent)478 KexiTabbedToolBarTabBar::KexiTabbedToolBarTabBar(QWidget *parent)
479 : QTabBar(parent)
480 {
481 setObjectName("tabbar");
482 customStyle = new KexiTabbedToolBarStyle(style()->objectName());
483 customStyle->setParent(this);
484 setStyle(customStyle);
485 installEventFilter(parent);
486 QWidget *mainWindow = KexiMainWindowIface::global()->thisWidget();
487 mainWindow->installEventFilter(parent);
488 setAttribute(Qt::WA_Hover, true);
489 }
490
originalTabSizeHint(int index) const491 QSize KexiTabbedToolBarTabBar::originalTabSizeHint(int index) const
492 {
493 return QTabBar::tabSizeHint(index);
494 }
495
tabSizeHint(int index) const496 QSize KexiTabbedToolBarTabBar::tabSizeHint(int index) const
497 {
498 QSize s = QTabBar::tabSizeHint(index);
499 QStyleOptionTab ot;
500 ot.initFrom(this);
501 QFont f(font());
502 f.setBold(true);
503 ot.text = (isSpecificTabStyle(customStyle->baseStyle()->objectName()) ? " " : "") + tabText(index);
504 ot.fontMetrics = QFontMetrics(f);
505 int w = customStyle->pixelMetric(QStyle::PM_TabBarTabHSpace, &ot, this);
506 if (w <= 0) { // needed e.g. for oxygen
507 w = fontMetrics().width(" ");
508 }
509 if (index == 0) {
510 s.setWidth(QFontMetrics(f).width(ot.text) + w * 2);
511 return s;
512 }
513 else if (index == KEXITABBEDTOOLBAR_SPACER_TAB_INDEX) {
514 // fix width of the spacer tab
515 s.setWidth(w);
516 }
517 return s;
518 }
519
toggleMainMenu()520 void KexiTabbedToolBar::Private::toggleMainMenu()
521 {
522 if (q->mainMenuVisible())
523 hideMainMenu();
524 else
525 showMainMenu();
526 }
527
showMainMenu(const char * actionName)528 void KexiTabbedToolBar::Private::showMainMenu(const char* actionName)
529 {
530 QWidget *mainWindow = KexiMainWindowIface::global()->thisWidget();
531 if (!mainMenu) {
532 mainMenu = new KexiMainMenu(q, mainWindow);
533 connect(mainMenu, SIGNAL(contentAreaPressed()), this, SLOT(hideMainMenu()));
534 connect(mainMenu, SIGNAL(hideContentsRequested()), this, SLOT(hideContentsOrMainMenu()));
535 }
536 updateMainMenuGeometry();
537 if (actionName) {
538 q->selectMainMenuItem(actionName);
539 }
540 else {
541 mainMenu->selectFirstItem();
542 }
543 mainMenu->show();
544 mainMenu->setFocus();
545 q->update(); // tab bar could have a line that should be repainted
546 }
547
updateMainMenuGeometry()548 void KexiTabbedToolBar::Private::updateMainMenuGeometry()
549 {
550 if (!mainMenu)
551 return;
552 QWidget *mainWindow = KexiMainWindowIface::global()->thisWidget();
553 KexiTabbedToolBarTabBar *tabBar = static_cast<KexiTabbedToolBarTabBar*>(q->tabBar());
554 QPoint pos = q->mapToGlobal(QPoint(0, tabBar->originalTabSizeHint(0).height() - 1));
555 // qDebug() << "1." << pos;
556 pos = mainWindow->mapFromGlobal(pos);
557 // qDebug() << "2." << pos;
558 // qDebug() << "3." << q->pos();
559
560 QStyleOptionTab ot;
561 ot.initFrom(tabBar);
562 int overlap = tabBar->style()->pixelMetric(QStyle::PM_TabBarBaseOverlap, &ot, tabBar)
563 - tabBar->style()->pixelMetric(QStyle::PM_TabBarBaseHeight, &ot, tabBar);
564 // qDebug() << "4. overlap=" << overlap;
565
566 mainMenu->setGeometry(0, pos.y() - overlap /*- q->y()*/,
567 mainWindow->width(),
568 mainWindow->height() - pos.y() + overlap /*+ q->y()*/);
569 }
570
initSearchLineEdit()571 void KexiTabbedToolBar::Private::initSearchLineEdit()
572 {
573 //! @todo use KexiConfig
574 KConfigGroup mainWindowGroup(KSharedConfig::openConfig()->group("MainWindow"));
575 const bool enabled = mainWindowGroup.readEntry("GlobalSearchBoxEnabled", true);
576 if (enabled && !searchLineEdit) {
577 searchLineEdit = new KexiSearchLineEdit;
578 kexiTester() << KexiTestObject(searchLineEdit, "globalSearch.lineEdit");
579 searchLineEdit->installEventFilter(q);
580 helpLayer->addWidget(searchLineEdit);
581 } else if (!enabled && searchLineEdit) {
582 helpLayer->removeWidget(searchLineEdit);
583 delete searchLineEdit;
584 searchLineEdit = nullptr;
585 }
586 }
587
activateSearchLineEdit()588 void KexiTabbedToolBar::activateSearchLineEdit()
589 {
590 if (!d->searchLineEdit) {
591 return;
592 }
593 d->searchLineEdit->selectAll();
594 d->searchLineEdit->setFocus();
595 }
596
hideMainMenu()597 void KexiTabbedToolBar::Private::hideMainMenu()
598 {
599 if (!mainMenu || !mainMenu->isVisible())
600 return;
601 mainMenu->hide();
602 mainMenu->setContent(0);
603 q->update(); // tab bar could have a line that should be repainted
604 }
605
hideContentsOrMainMenu()606 void KexiTabbedToolBar::Private::hideContentsOrMainMenu()
607 {
608 if (!mainMenu || !mainMenu->isVisible())
609 return;
610 if (mainMenu->contentWidget()) {
611 mainMenu->setContent(0);
612 }
613 else {
614 hideMainMenu();
615 }
616 }
617
createToolBar(const char * name,const QString & caption)618 KToolBar *KexiTabbedToolBar::Private::createToolBar(const char *name, const QString& caption)
619 {
620 KToolBar *tbar = new KToolBar(q, true /*main toolbar*/, false /*read config*/);
621 tbar->setIconDimensions(22); //!< @todo make configurable or system-dependent?
622 // needed e.g. for Windows style to remove the toolbar's frame
623 tbar->setStyle(customTabBar->customStyle);
624 toolbarsForName.insert(name, tbar);
625 tbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
626 tbar->setObjectName(name);
627 toolbarsCaptionForName.insert(name, caption);
628 tabIndex = q->addTab(tbar, caption);
629 toolbarsVisibleForIndex.append(true);
630 toolbarsIndexForName.insert(name, tabIndex);
631 return tbar;
632 }
633
KexiTabbedToolBar(QWidget * parent)634 KexiTabbedToolBar::KexiTabbedToolBar(QWidget *parent)
635 : QTabWidget(parent)
636 , d(new Private(this))
637 {
638 d->customTabBar = new KexiTabbedToolBarTabBar(this);
639 setTabBar(d->customTabBar);
640 setStyle(d->customTabBar->customStyle);
641
642 // from ktabwidget.cpp
643 //! @todo QTabWidget::setTabBar() should be added with this code
644 //! @todo KEXI3 Are these tabBar() connections useful to port?
645 #if 0
646 connect(tabBar(), SIGNAL(contextMenu(int,QPoint)), SLOT(contextMenu(int,QPoint&)));
647 connect(tabBar(), SIGNAL(tabDoubleClicked(int)), SLOT(mouseDoubleClick(int)));
648 connect(tabBar(), SIGNAL(newTabRequest()), this, SIGNAL(mouseDoubleClick())); // #185487
649 connect(tabBar(), SIGNAL(mouseMiddleClick(int)), SLOT(mouseMiddleClick(int)));
650 connect(tabBar(), SIGNAL(initiateDrag( int )), SLOT(initiateDrag( int )));
651 connect(tabBar(), SIGNAL(testCanDecode(QDragMoveEvent*,bool*)), SIGNAL(testCanDecode(QDragMoveEvent*,bool*)));
652 connect(tabBar(), SIGNAL(receivedDropEvent(int,QDropEvent*)), SLOT(receivedDropEvent(int,QDropEvent*)));
653 connect(tabBar(), SIGNAL(moveTab(int,int)), SLOT(moveTab(int,int)));
654 connect(tabBar(), SIGNAL(tabCloseRequested(int)), SLOT(closeRequest(int)));
655 #endif
656
657 setMouseTracking(true); // for mouseMoveEvent()
658 setWhatsThis(xi18n("Task-oriented toolbar. Groups commands using tabs."));
659 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
660 connect(&d->tabRaiseTimer, SIGNAL(timeout()), this, SLOT(slotDelayedTabRaise()));
661 #endif
662 connect(tabBar(), SIGNAL(tabBarDoubleClicked(int)), this, SLOT(slotTabDoubleClicked(int)));
663
664 d->ac = KexiMainWindowIface::global()->actionCollection();
665 QWidget *mainWin = KexiMainWindowIface::global()->thisWidget();
666 const bool userMode = KexiMainWindowIface::global()->userMode();
667 KToolBar *tbar;
668
669 slotSettingsChanged(0);//KGlobalSettings::FontChanged
670 //! @todo KEXI3 port from KGlobalSettings::Private::_k_slotNotifyChange:
671 //! connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(slotSettingsChanged(int)));
672
673 // help area
674 QWidget *helpWidget = new QWidget(this);
675 helpWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
676 d->helpLayer = new QHBoxLayout(helpWidget);
677 d->helpLayer->setContentsMargins(0, 0, 0, 0);
678
679 // * HELP MENU
680 // add help menu actions... (KexiTabbedToolBar depends on them)
681 d->helpMenu = new KHelpMenu(this, KAboutData::applicationData(),
682 true/*showWhatsThis*/);
683 QAction* help_report_bug_action = d->helpMenu->action(KHelpMenu::menuReportBug);
684 d->ac->addAction(help_report_bug_action->objectName(), help_report_bug_action);
685 QObject::disconnect(help_report_bug_action, 0, 0, 0);
686 QObject::connect(help_report_bug_action, SIGNAL(triggered()), mainWin, SLOT(slotReportBug()));
687 help_report_bug_action->setText(xi18nc("Report a bug or wish for Kexi application", "Report a &Bug or Wish..."));
688 help_report_bug_action->setIcon(koIcon("tools-report-bug")); // good icon for toolbar
689 help_report_bug_action->setWhatsThis(xi18n("Files a bug or wish for Kexi application."));
690 QAction* help_whats_this_action = d->helpMenu->action(KHelpMenu::menuWhatsThis);
691 d->ac->addAction(help_whats_this_action->objectName(), help_whats_this_action);
692 help_whats_this_action->setWhatsThis(xi18n("Activates a \"What's This?\" tool."));
693 QAction* help_contents_action = d->helpMenu->action(KHelpMenu::menuHelpContents);
694 d->ac->addAction(help_contents_action->objectName(), help_contents_action);
695 help_contents_action->setText(xi18n("Help"));
696 help_contents_action->setWhatsThis(xi18n("Shows Kexi Handbook."));
697 QAction* help_about_app_action = d->helpMenu->action(KHelpMenu::menuAboutApp);
698 d->ac->addAction(help_about_app_action->objectName(), help_about_app_action);
699 help_about_app_action->setWhatsThis(xi18n("Shows information about Kexi application."));
700 QAction* help_about_kde_action = d->helpMenu->action(KHelpMenu::menuAboutKDE);
701 d->ac->addAction(help_about_kde_action->objectName(), help_about_kde_action);
702 help_about_kde_action->setWhatsThis(xi18n("Shows information about KDE."));
703 QAction* help_switch_language_action = d->helpMenu->action(KHelpMenu::menuSwitchLanguage);
704 if (help_switch_language_action) {
705 d->ac->addAction(help_switch_language_action->objectName(), help_switch_language_action);
706 }
707 // extra action such as help_donate may be confusing for the user or conflicting with existing so hide it
708 QAction *extraAction = d->helpMenu->action(static_cast<KHelpMenu::MenuId>(KHelpMenu::menuSwitchLanguage + 1));
709 if (extraAction) {
710 extraAction->setVisible(false);
711 }
712
713 QAction *action_show_help_menu = d->ac->action("help_show_menu");
714 KexiSmallToolButton *btn = new KexiSmallToolButton(koIcon("help-about"), QString(), helpWidget);
715 btn->setToolButtonStyle(Qt::ToolButtonIconOnly);
716 btn->setPopupMode(QToolButton::InstantPopup);
717 btn->setToolTip(action_show_help_menu->toolTip());
718 btn->setWhatsThis(action_show_help_menu->whatsThis());
719 btn->setFocusPolicy(Qt::NoFocus);
720 QStyleOptionToolButton opt;
721 opt.initFrom(btn);
722 int w = btn->sizeHint().width();
723 int wAdd = btn->style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, btn);
724 if (w <= (2 * (wAdd + 1))) {
725 w += wAdd + 2;
726 }
727 btn->setMinimumWidth(w);
728 connect(action_show_help_menu, SIGNAL(triggered()), btn, SLOT(showMenu()));
729 d->helpLayer->addWidget(btn);
730 btn->setMenu(d->helpMenu->menu());
731 setCornerWidget(helpWidget, Qt::TopRightCorner);
732 d->initSearchLineEdit();
733
734 // needed e.g. for Windows style to remove the toolbar's frame
735 QWidget *dummyWidgetForMainMenu = new QWidget(this);
736 dummyWidgetForMainMenu->setObjectName("kexi");
737 addTab(dummyWidgetForMainMenu, KAboutData::applicationData().displayName());
738 d->toolbarsVisibleForIndex.append(true);
739 addTab(new QWidget(this), QString()); // dummy for spacer
740 d->toolbarsVisibleForIndex.append(true);
741
742 if (!userMode) {
743 d->createWidgetToolBar = d->createToolBar("create", xi18n("Create"));
744 }
745
746 tbar = d->createToolBar("data", xi18n("Data"));
747 addAction(tbar, "edit_cut");
748 addAction(tbar, "edit_copy");
749 addAction(tbar, "edit_paste");
750 if (!userMode)
751 addAction(tbar, "edit_paste_special_data_table");
752 //! @todo move undo/redo to quickbar:
753
754 tbar = d->createToolBar("external", xi18n("External Data"));
755 if (!userMode) {
756 addAction(tbar, "project_import_data_table");
757 addAction(tbar, "tools_import_tables");
758 }
759
760 tbar = d->createToolBar("tools", xi18n("Tools"));
761 addAction(tbar, "tools_compact_database");
762
763 //! @todo move to form plugin
764 tbar = d->createToolBar("form", xi18n("Form Design"));
765
766 //! @todo move to report plugin
767 tbar = d->createToolBar("report", xi18n("Report Design"));
768
769 connect(this, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentChanged(int)));
770 setCurrentWidget(widget(KEXITABBEDTOOLBAR_SPACER_TAB_INDEX + 1)); // the default
771 setFocusPolicy(Qt::NoFocus);
772 }
773
setCurrentTab(const QString & name)774 void KexiTabbedToolBar::Private::setCurrentTab(const QString& name)
775 {
776 q->setCurrentWidget(q->toolBar(name));
777 }
778
hideTab(const QString & name)779 void KexiTabbedToolBar::Private::hideTab(const QString& name)
780 {
781 q->removeTab(q->indexOf(toolbarsForName.value(name)));
782 toolbarsVisibleForIndex[toolbarsIndexForName.value(name)] = false;
783 }
784
isTabVisible(const QString & name) const785 bool KexiTabbedToolBar::Private::isTabVisible(const QString& name) const
786 {
787 return q->indexOf(toolbarsForName.value(name)) != -1
788 && toolbarsVisibleForIndex[toolbarsIndexForName.value(name)];
789 }
790
791 #ifndef NDEBUG
debugToolbars() const792 void KexiTabbedToolBar::Private::debugToolbars() const
793 {
794 qDebug() << "QHash<QString, KToolBar*> toolbarsForName:";
795 for (QHash<QString, KToolBar*>::ConstIterator it(toolbarsForName.constBegin());
796 it!=toolbarsForName.constEnd(); ++it)
797 {
798 qDebug() << it.key() << "->" << it.value();
799 }
800 qDebug() << "QHash<QString, int> toolbarsIndexForName:";
801 for (QHash<QString, int>::ConstIterator it(toolbarsIndexForName.constBegin());
802 it!=toolbarsIndexForName.constEnd(); ++it)
803 {
804 qDebug() << it.key() << "->" << it.value();
805 }
806 qDebug() << "QHash<QString, QString> toolbarsCaptionForName:";
807 for (QHash<QString, QString>::ConstIterator it(toolbarsCaptionForName.constBegin());
808 it!=toolbarsCaptionForName.constEnd(); ++it)
809 {
810 qDebug() << it.key() << "->" << it.value();
811 }
812 qDebug() << "QVector<bool> toolbarsVisibleForIndex:";
813 for (int i = 0; i < toolbarsVisibleForIndex.size(); i++) {
814 qDebug() << i << "->" << toolbarsVisibleForIndex[i];
815 }
816 }
817 #endif
818
showTab(const QString & name)819 void KexiTabbedToolBar::Private::showTab(const QString& name)
820 {
821 // qDebug() << "name:" << name;
822 // qDebug() << "toolbarsForName.value(name):" << toolbarsForName.value(name);
823 // qDebug() << "toolbarsIndexForName.value(name):" << toolbarsIndexForName.value(name);
824 // qDebug() << "q->indexOf(toolbarsForName.value(name))" << q->indexOf(toolbarsForName.value(name));
825 #ifndef NDEBUG
826 //debugToolbars();
827 #endif
828 if (q->indexOf(toolbarsForName.value(name)) == -1) {
829 int h = 0;
830 // count h = invisible tabs before this
831 for (int i = lowestIndex; i < toolbarsIndexForName.value(name); i++) {
832 if (!toolbarsVisibleForIndex.at(i))
833 h++;
834 }
835 q->insertTab(toolbarsIndexForName.value(name) - h,
836 toolbarsForName.value(name), toolbarsCaptionForName.value(name));
837 toolbarsVisibleForIndex[toolbarsIndexForName.value(name)] = true;
838 }
839 }
840
841 // ---
842
~KexiTabbedToolBar()843 KexiTabbedToolBar::~KexiTabbedToolBar()
844 {
845 delete d;
846 }
847
mainMenuVisible() const848 bool KexiTabbedToolBar::mainMenuVisible() const
849 {
850 return d->mainMenu && d->mainMenu->isVisible();
851 }
852
tabRect(int index) const853 QRect KexiTabbedToolBar::tabRect(int index) const
854 {
855 return tabBar()->tabRect(index);
856 }
857
helpMenu() const858 KHelpMenu* KexiTabbedToolBar::helpMenu() const
859 {
860 return d->helpMenu;
861 }
862
slotSettingsChanged(int category)863 void KexiTabbedToolBar::slotSettingsChanged(int category)
864 {
865 Q_UNUSED(category);
866 //! @todo if (category == KGlobalSettings::FontChanged) {
867 //! @todo KEXI3 KGlobalSettings::menuFont() not available (using QFontDatabase::systemFont(QFontDatabase::GeneralFont) for now)
868 //! https://community.kde.org/Frameworks/Porting_Notes#Global_Settings
869 setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); // the toolbar acts like a menu
870 //}
871 }
872
createWidgetToolBar() const873 KToolBar* KexiTabbedToolBar::createWidgetToolBar() const
874 {
875 return d->createWidgetToolBar;
876 }
877
mouseMoveEvent(QMouseEvent * event)878 void KexiTabbedToolBar::mouseMoveEvent(QMouseEvent* event)
879 {
880 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
881 QPoint p = event->pos();
882 int tab = tabBar()->tabAt(p);
883 if (d->tabToRaise != -1 && (tab == -1 || tab == currentIndex())) {
884 d->tabRaiseTimer.stop();
885 d->tabToRaise = -1;
886 } else if (d->tabToRaise != tab) {
887 d->tabRaiseTimer.start();
888
889 d->tabToRaise = tab;
890 }
891 #endif
892 QTabWidget::mouseMoveEvent(event);
893 }
894
leaveEvent(QEvent * event)895 void KexiTabbedToolBar::leaveEvent(QEvent* event)
896 {
897 #ifdef KEXI_AUTORISE_TABBED_TOOLBAR
898 d->tabRaiseTimer.stop();
899 d->tabToRaise = -1;
900 #endif
901 QTabWidget::leaveEvent(event);
902 }
903
eventFilter(QObject * watched,QEvent * event)904 bool KexiTabbedToolBar::eventFilter(QObject* watched, QEvent* event)
905 {
906 switch (event->type()) {
907 case QEvent::MouseButtonPress: {
908 QWidget *mainWin = KexiMainWindowIface::global()->thisWidget();
909 // qDebug() << "MouseButtonPress: watched:" << watched << "window()->focusWidget():" << window()->focusWidget();
910 if (d->searchLineEdit && watched == d->searchLineEdit) {
911 activateSearchLineEdit(); // custom setFocus() for search box, so it's possible to focus
912 // back on Escape key press
913 return false;
914 }
915 else if (watched == tabBar()) {
916 QMouseEvent* me = static_cast<QMouseEvent*>(event);
917 QPoint p = me->pos();
918 KexiTabbedToolBarTabBar *tb = static_cast<KexiTabbedToolBarTabBar*>(tabBar());
919 int index = tb->tabAt(p);
920 if (index == 0) {
921 d->toggleMainMenu();
922 return true;
923 }
924 d->hideMainMenu();
925 if (index == KEXITABBEDTOOLBAR_SPACER_TAB_INDEX) {
926 return true;
927 }
928 }
929 else if (watched == mainWin && d->mainMenu) {
930 QMouseEvent* me = static_cast<QMouseEvent*>(event);
931 if (!QRect(d->mainMenu->mapToGlobal(QPoint(0,0)), d->mainMenu->size())
932 .contains(mainWin->mapToGlobal(me->pos())))
933 {
934 // hide if clicked outside of the menu
935 d->hideMainMenu();
936 }
937 }
938 }
939 break;
940 case QEvent::KeyPress: {
941 QKeyEvent* ke = static_cast<QKeyEvent*>(event);
942 // qDebug() << "**********" << QString::number(ke->key(), 16)
943 // << QKeySequence::mnemonic(tabText(0))[0];
944 if (QKeySequence::mnemonic(tabText(0)) == QKeySequence(ke->key())) {
945 // qDebug() << "eat the &File accel";
946 if (!d->mainMenu || !d->mainMenu->isVisible()) {
947 d->showMainMenu();
948 }
949 /*this could be unexpected:
950 else if (d->mainMenu && d->mainMenu->isVisible()) {
951 d->hideMainMenu();
952 }*/
953 return true;
954 }
955 if (d->mainMenu && d->mainMenu->isVisible() && (ke->key() == Qt::Key_Escape) && ke->modifiers() == Qt::NoModifier) {
956 d->hideContentsOrMainMenu();
957 return true;
958 }
959 break;
960 }
961 case QEvent::Resize:
962 if (watched == KexiMainWindowIface::global()->thisWidget()) {
963 d->updateMainMenuGeometry();
964 }
965 break;
966 case QEvent::Shortcut: {
967 QShortcutEvent *se = static_cast<QShortcutEvent*>(event);
968 if (watched == tabBar() && QKeySequence::mnemonic(tabText(0)) == se->key()) {
969 // qDebug() << "eat the &File accel";
970 if (!d->mainMenu || !d->mainMenu->isVisible()) {
971 d->showMainMenu();
972 return true;
973 }
974 }
975 break;
976 }
977 default:;
978 }
979 return QTabWidget::eventFilter(watched, event);
980 }
981
slotCurrentChanged(int index)982 void KexiTabbedToolBar::slotCurrentChanged(int index)
983 {
984 if (index == indexOf(d->createWidgetToolBar) && index != -1) {
985 if (d->createWidgetToolBar->actions().isEmpty()) {
986 QTimer::singleShot(10, this, SLOT(setupCreateWidgetToolbar()));
987 }
988 }
989 if (d->rolledUp) { // switching the tab rolls down
990 slotTabDoubleClicked(index);
991 }
992 if (index == 0) { // main menu
993 d->showMainMenu();
994 }
995 else {
996 d->hideMainMenu();
997 }
998 }
999
slotTabDoubleClicked(int index)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
tabBarAnimationFinished()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
setupCreateWidgetToolbar()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
slotDelayedTabRaise()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
toolBar(const QString & name) const1077 KToolBar *KexiTabbedToolBar::toolBar(const QString& name) const
1078 {
1079 return d->toolbarsForName[name];
1080 }
1081
addAction(KToolBar * tbar,const char * actionName)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
addAction(const QString & toolBarName,QAction * action)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
addSeparatorAndAction(KToolBar * tbar,const char * actionName)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
appendWidgetToToolbar(const QString & name,QWidget * widget)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
setWidgetVisibleInToolbar(QWidget * widget,bool visible)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
showMainMenu(const char * actionName)1127 void KexiTabbedToolBar::showMainMenu(const char* actionName)
1128 {
1129 d->showMainMenu(actionName);
1130 }
1131
hideMainMenu()1132 void KexiTabbedToolBar::hideMainMenu()
1133 {
1134 d->hideMainMenu();
1135 }
1136
toggleMainMenu()1137 void KexiTabbedToolBar::toggleMainMenu()
1138 {
1139 d->toggleMainMenu();
1140 }
1141
setMainMenuContent(QWidget * w)1142 void KexiTabbedToolBar::setMainMenuContent(QWidget *w)
1143 {
1144 d->mainMenu->setContent(w);
1145 }
1146
mainMenuContent()1147 const QWidget* KexiTabbedToolBar::mainMenuContent()
1148 {
1149 return d->mainMenu->contentWidget();
1150 }
1151
selectMainMenuItem(const char * actionName)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
addSearchableModel(KexiSearchableModel * model)1163 void KexiTabbedToolBar::addSearchableModel(KexiSearchableModel *model)
1164 {
1165 if (!d->searchLineEdit) {
1166 return;
1167 }
1168 d->searchLineEdit->addSearchableModel(model);
1169 }
1170
removeSearchableModel(KexiSearchableModel * model)1171 void KexiTabbedToolBar::removeSearchableModel(KexiSearchableModel *model)
1172 {
1173 if (!d->searchLineEdit) {
1174 return;
1175 }
1176 d->searchLineEdit->removeSearchableModel(model);
1177 }
1178
createToolBar(const char * name,const QString & caption)1179 KToolBar* KexiTabbedToolBar::createToolBar(const char* name, const QString& caption)
1180 {
1181 return d->createToolBar(name, caption);
1182 }
1183
setCurrentTab(const QString & name)1184 void KexiTabbedToolBar::setCurrentTab(const QString& name)
1185 {
1186 //qDebug() << name;
1187 d->setCurrentTab(name);
1188 }
1189
setCurrentTab(int index)1190 void KexiTabbedToolBar::setCurrentTab(int index)
1191 {
1192 setCurrentIndex(d->lowestIndex + index);
1193 }
1194
hideTab(const QString & name)1195 void KexiTabbedToolBar::hideTab(const QString& name)
1196 {
1197 //qDebug() << name;
1198 d->hideTab(name);
1199 }
1200
showTab(const QString & name)1201 void KexiTabbedToolBar::showTab(const QString& name)
1202 {
1203 //qDebug() << name;
1204 d->showTab(name);
1205 }
1206
isTabVisible(const QString & name) const1207 bool KexiTabbedToolBar::isTabVisible(const QString& name) const
1208 {
1209 return d->isTabVisible(name);
1210 }
1211
isRolledUp()1212 bool KexiTabbedToolBar::isRolledUp()
1213 {
1214 return d->rolledUp;
1215 }
1216
toggleRollDown()1217 void KexiTabbedToolBar::toggleRollDown()
1218 {
1219 slotTabDoubleClicked(-1);//use -1 just to rolldown/up the tabbar
1220 }
1221
1222 // ---
1223
KexiMainWidget()1224 KexiMainWidget::KexiMainWidget()
1225 : KMainWindow(0, Qt::Widget)
1226 , m_mainWindow(0)
1227 {
1228 setupCentralWidget();
1229 }
1230
~KexiMainWidget()1231 KexiMainWidget::~KexiMainWidget()
1232 {
1233 }
1234
setParent(KexiMainWindow * mainWindow)1235 void KexiMainWidget::setParent(KexiMainWindow* mainWindow)
1236 {
1237 KMainWindow::setParent(mainWindow);
1238 m_mainWindow = mainWindow;
1239 }
1240
tabWidget() const1241 KexiMainWindowTabWidget* KexiMainWidget::tabWidget() const
1242 {
1243 return m_tabWidget;
1244 }
1245
setupCentralWidget()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
queryClose()1260 bool KexiMainWidget::queryClose()
1261 {
1262 return m_mainWindow ? m_mainWindow->queryClose() : true;
1263 }
1264
slotCurrentTabIndexChanged(int index)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
Private(KexiMainWindow * w)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
~Private()1325 KexiMainWindow::Private::~Private()
1326 {
1327 qDeleteAll(m_openedCustomObjectsForItem);
1328 }
1329
insertWindow(KexiWindow * window)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
windowContainerExistsFor(int identifier) const1339 bool KexiMainWindow::Private::windowContainerExistsFor(int identifier) const
1340 {
1341 return windowContainers.contains(identifier);
1342 }
1343
setWindowContainerExistsFor(int identifier,bool set)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
updateWindowId(KexiWindow * window,int oldItemID)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
removeWindow(int identifier)1364 void KexiMainWindow::Private::removeWindow(int identifier)
1365 {
1366 //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex );
1367 windows.remove(identifier);
1368 }
1369
openedWindowsCount()1370 int KexiMainWindow::Private::openedWindowsCount()
1371 {
1372 //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex );
1373 return windows.count();
1374 }
1375
1376 //! Used in KexiMainWindowe::closeProject()
clearWindows()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
showStartProcessMsg(const QStringList & args)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
updatePropEditorVisibility(Kexi::ViewMode viewMode,KexiPart::Info * info)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
setTabBarVisible(KMultiTabBar::KMultiTabBarPosition position,int id,KexiDockWidget * dockWidget,bool visible)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
setPropertyEditorTabBarVisible(bool visible)1436 void KexiMainWindow::Private::setPropertyEditorTabBarVisible(bool visible)
1437 {
1438 setTabBarVisible(KMultiTabBar::Right, PROPERTY_EDITOR_TABBAR_ID,
1439 propEditorDockWidget, visible);
1440 }
1441
openedCustomObjectsForItem(KexiPart::Item * item,const char * name)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
addOpenedCustomObjectForItem(KexiPart::Item * item,QObject * object,const char * name)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
findDialog()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
updateFindDialogContents(bool createIfDoesNotExist)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
currentViewSupportingAction(const char * actionName) const1499 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
currentViewSupportingSearchAndReplaceInterface() const1512 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
showProjectMigrationWizard(const QString & mimeType,const QString & databaseName,const KDbConnectionData * cdata)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
visibleMainMenuWidgetPage()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
executeActionWhenPendingJobsAreFinished()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
openedWindowFor(const KexiPart::Item * item,PendingJobType & pendingType)1589 KexiWindow *KexiMainWindow::Private::openedWindowFor(const KexiPart::Item* item, PendingJobType &pendingType)
1590 {
1591 return openedWindowFor(item->identifier(), pendingType);
1592 }
1593
openedWindowFor(int identifier,PendingJobType & pendingType)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
addItemToPendingWindows(const KexiPart::Item * item,PendingJobType jobType)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
pendingWindowsExist()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
removePendingWindow(int identifier)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
openedWindowFor(int identifier)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
1637