<commit_before>/***************************************************************************
**
** Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (<EMAIL>)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at <EMAIL>.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mondisplaychangeevent.h"

#include <MDebug>
#include <mscenewindowevent_p.h>
#include "mapplicationwindow_p.h"
#include "mapplicationwindow.h"
#include "mapplicationwindowstyle.h"
#include "mapplicationpage.h"
#include "mapplicationpage_p.h"
#include "mnavigationbar.h"
#include "mapplicationmenu.h"
#ifndef Q_OS_WIN
#include "mcomponentcache.h"
#endif
#include <mdynamicpropertywatcher.h>
#include "mtoolbar.h"
#include "mtoolbar_p.h"
#include "mdockwidget.h"
#include "mcomponentdata.h"
#include "maction.h"
#include "mhomebuttonpanel.h"
#include "mapplication.h"
#include "mcomponentdata.h"
#include "mscenemanager.h"
#include "mscenemanager_p.h"
#include "mscene.h"
#include "mstatusbar.h"
#include "mdeviceprofile.h"
#include "mcomponentdata.h"

#include <QList>
#include <QEvent>
#include <QRectF>
#include <QActionEvent>

#ifdef Q_WS_X11
# include <QX11Info>
# include <X11/Xatom.h>
# include <X11/Xlib.h>
#endif

#ifdef M_OS_MAEMO5
#include <QDBusConnection>
#include <QDBusMessage>
#endif //M_OS_MAEMO5

namespace {
    // TODO: this may be moved to some header if needed elsewhere
    // Returns the longest length variant of a translated string.
    // If a translated string has many length variants, they are
    // separated with U+009C (STRING TERMINATOR) character, and
    // according to DTD for .ts file format, they should be ordered
    // by decreasing length.
    // This helper function returns the longest length variant
    // by returning the string up to the first U+009C character.
    // If there's only one length variant, the function returns
    // the whole unprocessed string.
    QString longestLengthVariant(const QString &text)
    {
        return text.left(text.indexOf(QChar(0x9c)));
    }
}

MApplicationWindowPrivate::MApplicationWindowPrivate()
    : MWindowPrivate()
    , page(NULL)
    , navigationBar(NULL)
    , toolBar(NULL)
    , dockWidget(new MDockWidget)
    , homeButtonPanel(NULL)
    , menu(new MApplicationMenu)
    , statusBar(NULL)
    , pageAreaMaximized(false)
#ifdef Q_WS_X11
    , isChained(false)
    , chainParentWinId(0)
    , chainTaskTitle()
#endif
#ifdef HAVE_CONTEXTSUBSCRIBER
    , callStatusProperty("Phone.Call")
#endif
    , showingNavigationBar(false)
    , showingDockWidget(false)
    , animateComponentsTransitions(false)
    , styleContainer(0)
{

}

MApplicationWindowPrivate::~MApplicationWindowPrivate()
{
    if (dockWidget) {
        delete dockWidget;
        dockWidget = 0;
    }
    if (toolBar) {
        delete toolBar;
        toolBar = 0;
    }

    if (menu) {
        delete menu;
        menu = 0;
    }

    if (navigationBar) {
        // MNavigationBarView is not destroyed until MNavigationBar destructor chain reaches ~MWidgetController
        // so we have to notify the view earlier that the toolbar is already destroyed.
        navigationBar->undockToolBar();
        delete navigationBar;
        navigationBar = 0;
    }
    if (homeButtonPanel) {
        delete homeButtonPanel;
        homeButtonPanel = 0;
    }
    if (statusBar) {
        delete statusBar;
        statusBar = 0;
    }
    if (styleContainer){
        delete styleContainer;
        styleContainer = 0;
    }
}

void MApplicationWindowPrivate::init()
{
    Q_Q(MApplicationWindow);

    navigationBar = new MNavigationBar;
    toolBar = new MToolBar(navigationBar);
    homeButtonPanel = new MHomeButtonPanel;

    if(MDeviceProfile::instance()->showStatusbar())    {
        statusBar = new MStatusBar;
    }
    else{
        statusBar = NULL;
    }

    q->connect(q, SIGNAL(orientationChanged(M::Orientation)), q, SLOT(_q_updateStyle()));
    q->connect(MTheme::instance(), SIGNAL(themeChangeCompleted()), q, SLOT(_q_updateStyle()));

    q->setOptimizationFlag(QGraphicsView::DontSavePainterState);

#ifdef Q_WS_X11
    if ( !MComponentCache::populating() &&
         !MComponentData::chainDataStackIsEmpty() )
    {
        // init for chainTask here
        updateChainTaskData();
    }

    appendMApplicationWindowTypeProperty();
#endif

#ifdef M_OS_MAEMO5
    q->connect(homeButtonPanel, SIGNAL(buttonClicked()), q, SLOT(_q_exitAppView()));
#else
    q->connect(homeButtonPanel, SIGNAL(buttonClicked()), q, SLOT(closeMenu()));
    q->connect(homeButtonPanel, SIGNAL(buttonClicked()), q, SLOT(showMinimized()));
#endif //M_OS_MAEMO5

    q->connect(navigationBar, SIGNAL(viewmenuTriggered()),
               q, SLOT(openMenu()));

    q->connect(navigationBar, SIGNAL(sceneWindowStateChanged(MSceneWindow::SceneWindowState, MSceneWindow::SceneWindowState)),
               q, SLOT(_q_navigationBarStateChanged(MSceneWindow::SceneWindowState, MSceneWindow::SceneWindowState)));

    q->connect(menu, SIGNAL(appeared()),
               q, SLOT(_q_menuAppeared()));
    q->connect(menu, SIGNAL(disappeared()),
               q, SLOT(_q_menuDisappeared()));
    q->connect(q, SIGNAL(switcherEntered()),
               SLOT(_q_handleInSwitcherVisibilityChange()));

    // We wanna do it later since this navigation bar emptiness might be a momentary state
    // during the setup of a page switch.
    // E.g. The navigation bar gets empty by the detachment of the current page but when the
    // new page is finally connected the nav bar might get populated by a
    // toolbar, menu button, etc and therefore it won't be empty anymore.
    //
    // So doing it later ensures that the navigation bar visibility is updated only once
    // per page switch.
    MDynamicPropertyWatcher *navigationBarIsEmptyPropertyWatcher = new MDynamicPropertyWatcher(q);
    navigationBarIsEmptyPropertyWatcher->watch(navigationBar);
    navigationBarIsEmptyPropertyWatcher->setPropertyName("isEmpty");
    q->connect(navigationBarIsEmptyPropertyWatcher, SIGNAL(propertyChanged()),
               SLOT(_q_scheduleNavigationBarVisibilityUpdate()));

    updateStyleNames();

    // Create the view to get the "isEmpty" property updates.
    navigationBar->view();
    navigationBar->setVisible(false);
    sceneManager->addSceneWindow(navigationBar);

    sceneManager->appearSceneWindowNow(homeButtonPanel);

    // Initialize escape button to close mode.
    navigationBar->setEscapeButtonMode(MNavigationBarModel::EscapeButtonClose);
    QObject::connect(navigationBar, SIGNAL(closeButtonClicked()), q, SLOT(close()));

    if (needsDockWidget()) {
        sceneManager->appearSceneWindowNow(dockWidget);
    }

    if (!MApplication::fullScreen() && statusBar) {
        sceneManager->appearSceneWindowNow(statusBar);
    }

    _q_placeToolBar();

    // Unlike plain MWindows, the full-fledged application window have rounded corners
    // enabled by default.
    q->setRoundedCornersEnabled(true);

    applyWindowBackground();

    initAutoHideComponentsTimer();

#ifdef HAVE_CONTEXTSUBSCRIBER
    q->connect(&callStatusProperty, SIGNAL(valueChanged()), SLOT(_q_updateStatusBarVisibility()));
#endif
    q->connect(q, SIGNAL(orientationAngleChanged(M::OrientationAngle)),
            SLOT(_q_updatePageExposedContentRect()));
    q->connect(MTheme::instance(), SIGNAL(themeChangeCompleted()),
               SLOT(_q_updatePageExposedContentRect()));

    if (statusBar) {
        q->connect(statusBar, SIGNAL(appeared()), SLOT(_q_updatePageExposedContentRect()));
        q->connect(statusBar, SIGNAL(disappeared()), SLOT(_q_updatePageExposedContentRect()));
    }

    navigationBarVisibilityUpdateTimer.setSingleShot(true);
    navigationBarVisibilityUpdateTimer.setInterval(0);
    q->connect(&navigationBarVisibilityUpdateTimer, SIGNAL(timeout()),
               SLOT(_q_updateNavigationBarVisibility()));
}

void MApplicationWindowPrivate::_q_handleInSwitcherVisibilityChange()
{
    closeMenu();
    sceneManager->d_func()->closePopupWindows();
}

#ifdef Q_WS_X11
void MApplicationWindowPrivate::setWindowChainedProperty( const Window &parentWinId, const Window &childWinId )
{
    Atom atomMInvokedBy = XInternAtom(QX11Info::display(), "_MEEGOTOUCH_WM_INVOKED_BY", False);
    Display *display = QX11Info::display();

    // for compositor page transition
    XChangeProperty(display, childWinId,
            atomMInvokedBy, XA_WINDOW,
            32, PropModeReplace,
            (unsigned char *)&parentWinId, 1);

    // for task switcher view stacking
    XSetTransientForHint(display, childWinId, parentWinId);
}

void MApplicationWindowPrivate::appendMApplicationWindowTypeProperty()
{
    Q_Q(MApplicationWindow);

    Atom atomWindowType = XInternAtom(QX11Info::display(), "_MEEGOTOUCH_NET_WM_WINDOW_TYPE_MAPPLICATION", False);
    XChangeProperty(QX11Info::display(), q->effectiveWinId(),
                    XInternAtom(QX11Info::display(), "_NET_WM_WINDOW_TYPE", False),
                    XA_ATOM, 32, PropModeAppend, (unsigned char*) &atomWindowType, 1);
}
#endif

void  MApplicationWindowPrivate::initAutoHideComponentsTimer()
{
    autoHideComponentsTimer.setInterval(style()->autoHideTimeout());
    autoHideComponentsTimer.setSingleShot(true);
}

void MApplicationWindowPrivate::windowStateChangeEvent(QWindowStateChangeEvent *event)
{
    Q_Q(MApplicationWindow);
    if (!statusBar)
        return;

    if (q->isFullScreen() != event->oldState().testFlag(Qt::WindowFullScreen)) {
        _q_updateStatusBarVisibility();
    }
}

void MApplicationWindowPrivate::maximizePageArea()
{
    pageAreaMaximized = true;

    // When maximized, the window is in control of these components.
    setComponentDisplayMode(homeButtonPanel, MApplicationPageModel::Hide);

    if (navigationBar->focusItem()) {
        // Always show focused navigation bar.
        setComponentDisplayMode(navigationBar, MApplicationPageModel::Show);
    } else {
        setComponentDisplayMode(navigationBar, MApplicationPageModel::Hide);
    }

    if (needsDockWidget()) {
        if (dockWidget->focusItem()) {
            // Always show focused dock widget.
            setComponentDisplayMode(dockWidget, MApplicationPageModel::Show);
        } else {
            setComponentDisplayMode(dockWidget, MApplicationPageModel::Hide);
        }
    }


    // Now that we've set pageAreaMaximized, this update tries to hide status bar.
    _q_updateStatusBarVisibility();
}

void MApplicationWindowPrivate::restorePageArea()
{
    if (!pageAreaMaximized) {
        return;
    }

    pageAreaMaximized = false;

    if (page) {
        setComponentDisplayMode(homeButtonPanel, page->model()->homeButtonDisplayMode());
        setComponentDisplayMode(navigationBar, page->model()->navigationBarDisplayMode());

        if (needsDockWidget()) {
            // Dock widget follows navigation bar display mode.
            setComponentDisplayMode(dockWidget, page->model()->navigationBarDisplayMode());
        }
    }

    // Show status bar
    _q_updateStatusBarVisibility();
}

void MApplicationWindowPrivate::_q_updateStatusBarVisibility()
{
    Q_Q(MApplicationWindow);
    if (!statusBar)
        return;

#ifdef HAVE_CONTEXTSUBSCRIBER
    // Status bar should always be appeared while a phone call is ongoing.
    if (callStatusProperty.value().toString() == "active") {
        statusBar->appear(q);
        return;
    }
#endif

    if (q->isFullScreen() || pageAreaMaximized) {
        statusBar->disappear();
    } else {
        statusBar->appear(q);
    }
}

void MApplicationWindowPrivate::_q_actionUpdated(QActionEvent *event)
{
    QAction *action = event->action();

    if (event->type() == QEvent::ActionAdded) {
        QAction *before = event->before();
        distributeAction(action, before);
    } else if (event->type() == QEvent::ActionRemoved) {
        menu->removeAction(action);
        toolBar->removeAction(action);
    }
    refreshArrowIconVisibility();

    if (page) {
        // Dock widget follows navigation bar display mode.
        setComponentDisplayMode(dockWidget,
                                page->model()->navigationBarDisplayMode());
    }
}

void MApplicationWindowPrivate::_q_handlePageModelModifications(const QList<const char *> &modifications)
{
    Q_Q(MApplicationWindow);
    const char *member;

    foreach(member, modifications) {
        if (!pageAreaMaximized) {
            if (member == MApplicationPageModel::HomeButtonDisplayMode) {
                setComponentDisplayMode(homeButtonPanel,
                                        page->model()->homeButtonDisplayMode());

            } else if (member == MApplicationPageModel::NavigationBarDisplayMode) {
                if (page->model()->navigationBarDisplayMode() == MApplicationPageModel::AutoHide)
                    addComponentToAutoHide(navigationBar);
                else
                    removeComponentFromAutoHide(navigationBar);

                _q_scheduleNavigationBarVisibilityUpdate();

                // Dock widget follows navigation bar display mode.
                setComponentDisplayMode(dockWidget,
                                        page->model()->navigationBarDisplayMode());

                // Display mode can be changed between "auto-hide" and "show" which does not
                // affect appearance state. Need to update exposed rectangle explicitly.
                _q_updatePageExposedContentRect();
            } else if (member == MApplicationPageModel::EscapeButtonDisplayMode) {
                _q_scheduleNavigationBarVisibilityUpdate();
            }
        }

        if (member == MApplicationPageModel::ProgressIndicatorVisible) {
            navigationBar->setProgressIndicatorVisible(page->model()->progressIndicatorVisible());

        } else if (member == MApplicationPageModel::EscapeMode) {
            setupPageEscape();

        } else if (member == MApplicationPageModel::Title) {
            if (page->isVisible()) {
                navigationBar->setViewMenuDescription(page->model()->title());
                q->setWindowTitle(longestLengthVariant(page->model()->title()));
            }
        }
    }
}

void MApplicationWindowPrivate::_q_placeToolBar()
{
    // set position of toolbar
    if (needsDockWidget()) {
        // take toolbar from navigation bar
        navigationBar->undockToolBar();
        // dock it in lower dock widget
        dockWidget->dockToolBar(toolBar);
        // show dockwidget
        if (page) {
            // Dock widget follows navigation bar display mode.
            setComponentDisplayMode(dockWidget,
                                    page->model()->navigationBarDisplayMode());
        }
    } else {
        // hide dockwidget
        dockWidget->disappear();
        // undock toolbar
        dockWidget->undockToolBar();
        // redock to navigation bar
        navigationBar->dockToolBar(toolBar);
    }
}

void MApplicationWindowPrivate::_q_updatePageExposedContentRect()
{
    if (!page) {
        return;
    }

    qreal topCoverage = 0.0f;
    qreal bottomCoverage = 0.0f;

    QRectF pageExposedContentRect;

    // Interpretation of whether the navigation bar is covering the page
    // changes from "auto-hide" to "show". On "auto-hide" the navigation
    // bar doesn't count as covering the page since it does so only momentarily.
    const bool navBarIsCovering = showingNavigationBar &&
            page->componentDisplayMode(MApplicationPage::NavigationBar) != MApplicationPageModel::AutoHide;

    if (navBarIsCovering) {
        if (navigationBar->alignment().testFlag(Qt::AlignTop))
            topCoverage += navigationBar->size().height();
        else if (navigationBar->alignment().testFlag(Qt::AlignBottom))
            bottomCoverage += navigationBar->size().height();
    }


    if (showingDockWidget) {
        if (dockWidget->alignment().testFlag(Qt::AlignTop))
            topCoverage += dockWidget->size().height();
        else if (dockWidget->alignment().testFlag(Qt::AlignBottom))
            bottomCoverage += dockWidget->size().height();
    }

    pageExposedContentRect.setY(topCoverage);
    pageExposedContentRect.setWidth(page->geometry().width());
    pageExposedContentRect.setHeight(page->geometry().height()
                              - pageExposedContentRect.y() - bottomCoverage);

    page->d_func()->setExposedContentRect(pageExposedContentRect);
}

void MApplicationWindowPrivate::openMenu()
{
    Q_Q(MApplicationWindow);
    if (navigationBar->isArrowIconVisible() &&
        (navigationBar->sceneWindowState() != MSceneWindow::Disappearing)) {
        menu->appear(q);
        navigationBar->setEscapeButtonEnabled(false);

        // Simply calling setEnabled(false) would uncheck the currently
        // checked item in the toolbar if tab view is being used.
        toolBar->setProperty(_M_IsEnabledPreservingSelection, QVariant(false));
    }
}

void MApplicationWindowPrivate::closeMenu()
{
    if (menu->sceneWindowState() == MSceneWindow::Appeared || menu->sceneWindowState() == MSceneWindow::Appearing)
        menu->disappear();
}

bool MApplicationWindowPrivate::isMenuOpen() const
{
    return (menu->sceneWindowState() == MSceneWindow::Appeared);
}

void MApplicationWindowPrivate::_q_menuAppeared()
{
    Q_Q(MApplicationWindow);
    QObject::disconnect(navigationBar, SIGNAL(viewmenuTriggered()),
                        q, SLOT(openMenu()));
    QObject::connect(navigationBar, SIGNAL(viewmenuTriggered()),
                     q, SLOT(closeMenu()));
    autoHideComponentsTimer.stop();
}

void MApplicationWindowPrivate::_q_menuDisappeared()
{
    Q_Q(MApplicationWindow);
    QObject::disconnect(navigationBar, SIGNAL(viewmenuTriggered()),
                        q, SLOT(closeMenu()));
    QObject::connect(navigationBar, SIGNAL(viewmenuTriggered()),
                     q, SLOT(openMenu()));

    navigationBar->setEscapeButtonEnabled(true);
    toolBar->setProperty(_M_IsEnabledPreservingSelection, QVariant(true));<commit_msg>Changes: According to new menu design there is no need to disable tool bar items as they are already behind the dimming effect layer when the menu is open.<commit_after>/***************************************************************************
**
** Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (<EMAIL>)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at <EMAIL>.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mondisplaychangeevent.h"

#include <MDebug>
#include <mscenewindowevent_p.h>
#include "mapplicationwindow_p.h"
#include "mapplicationwindow.h"
#include "mapplicationwindowstyle.h"
#include "mapplicationpage.h"
#include "mapplicationpage_p.h"
#include "mnavigationbar.h"
#include "mapplicationmenu.h"
#ifndef Q_OS_WIN
#include "mcomponentcache.h"
#endif
#include <mdynamicpropertywatcher.h>
#include "mtoolbar.h"
#include "mtoolbar_p.h"
#include "mdockwidget.h"
#include "mcomponentdata.h"
#include "maction.h"
#include "mhomebuttonpanel.h"
#include "mapplication.h"
#include "mcomponentdata.h"
#include "mscenemanager.h"
#include "mscenemanager_p.h"
#include "mscene.h"
#include "mstatusbar.h"
#include "mdeviceprofile.h"
#include "mcomponentdata.h"

#include <QList>
#include <QEvent>
#include <QRectF>
#include <QActionEvent>

#ifdef Q_WS_X11
# include <QX11Info>
# include <X11/Xatom.h>
# include <X11/Xlib.h>
#endif

#ifdef M_OS_MAEMO5
#include <QDBusConnection>
#include <QDBusMessage>
#endif //M_OS_MAEMO5

namespace {
    // TODO: this may be moved to some header if needed elsewhere
    // Returns the longest length variant of a translated string.
    // If a translated string has many length variants, they are
    // separated with U+009C (STRING TERMINATOR) character, and
    // according to DTD for .ts file format, they should be ordered
    // by decreasing length.
    // This helper function returns the longest length variant
    // by returning the string up to the first U+009C character.
    // If there's only one length variant, the function returns
    // the whole unprocessed string.
    QString longestLengthVariant(const QString &text)
    {
        return text.left(text.indexOf(QChar(0x9c)));
    }
}

MApplicationWindowPrivate::MApplicationWindowPrivate()
    : MWindowPrivate()
    , page(NULL)
    , navigationBar(NULL)
    , toolBar(NULL)
    , dockWidget(new MDockWidget)
    , homeButtonPanel(NULL)
    , menu(new MApplicationMenu)
    , statusBar(NULL)
    , pageAreaMaximized(false)
#ifdef Q_WS_X11
    , isChained(false)
    , chainParentWinId(0)
    , chainTaskTitle()
#endif
#ifdef HAVE_CONTEXTSUBSCRIBER
    , callStatusProperty("Phone.Call")
#endif
    , showingNavigationBar(false)
    , showingDockWidget(false)
    , animateComponentsTransitions(false)
    , styleContainer(0)
{

}

MApplicationWindowPrivate::~MApplicationWindowPrivate()
{
    if (dockWidget) {
        delete dockWidget;
        dockWidget = 0;
    }
    if (toolBar) {
        delete toolBar;
        toolBar = 0;
    }

    if (menu) {
        delete menu;
        menu = 0;
    }

    if (navigationBar) {
        // MNavigationBarView is not destroyed until MNavigationBar destructor chain reaches ~MWidgetController
        // so we have to notify the view earlier that the toolbar is already destroyed.
        navigationBar->undockToolBar();
        delete navigationBar;
        navigationBar = 0;
    }
    if (homeButtonPanel) {
        delete homeButtonPanel;
        homeButtonPanel = 0;
    }
    if (statusBar) {
        delete statusBar;
        statusBar = 0;
    }
    if (styleContainer){
        delete styleContainer;
        styleContainer = 0;
    }
}

void MApplicationWindowPrivate::init()
{
    Q_Q(MApplicationWindow);

    navigationBar = new MNavigationBar;
    toolBar = new MToolBar(navigationBar);
    homeButtonPanel = new MHomeButtonPanel;

    if(MDeviceProfile::instance()->showStatusbar())    {
        statusBar = new MStatusBar;
    }
    else{
        statusBar = NULL;
    }

    q->connect(q, SIGNAL(orientationChanged(M::Orientation)), q, SLOT(_q_updateStyle()));
    q->connect(MTheme::instance(), SIGNAL(themeChangeCompleted()), q, SLOT(_q_updateStyle()));

    q->setOptimizationFlag(QGraphicsView::DontSavePainterState);

#ifdef Q_WS_X11
    if ( !MComponentCache::populating() &&
         !MComponentData::chainDataStackIsEmpty() )
    {
        // init for chainTask here
        updateChainTaskData();
    }

    appendMApplicationWindowTypeProperty();
#endif

#ifdef M_OS_MAEMO5
    q->connect(homeButtonPanel, SIGNAL(buttonClicked()), q, SLOT(_q_exitAppView()));
#else
    q->connect(homeButtonPanel, SIGNAL(buttonClicked()), q, SLOT(closeMenu()));
    q->connect(homeButtonPanel, SIGNAL(buttonClicked()), q, SLOT(showMinimized()));
#endif //M_OS_MAEMO5

    q->connect(navigationBar, SIGNAL(viewmenuTriggered()),
               q, SLOT(openMenu()));

    q->connect(navigationBar, SIGNAL(sceneWindowStateChanged(MSceneWindow::SceneWindowState, MSceneWindow::SceneWindowState)),
               q, SLOT(_q_navigationBarStateChanged(MSceneWindow::SceneWindowState, MSceneWindow::SceneWindowState)));

    q->connect(menu, SIGNAL(appeared()),
               q, SLOT(_q_menuAppeared()));
    q->connect(menu, SIGNAL(disappeared()),
               q, SLOT(_q_menuDisappeared()));
    q->connect(q, SIGNAL(switcherEntered()),
               SLOT(_q_handleInSwitcherVisibilityChange()));

    // We wanna do it later since this navigation bar emptiness might be a momentary state
    // during the setup of a page switch.
    // E.g. The navigation bar gets empty by the detachment of the current page but when the
    // new page is finally connected the nav bar might get populated by a
    // toolbar, menu button, etc and therefore it won't be empty anymore.
    //
    // So doing it later ensures that the navigation bar visibility is updated only once
    // per page switch.
    MDynamicPropertyWatcher *navigationBarIsEmptyPropertyWatcher = new MDynamicPropertyWatcher(q);
    navigationBarIsEmptyPropertyWatcher->watch(navigationBar);
    navigationBarIsEmptyPropertyWatcher->setPropertyName("isEmpty");
    q->connect(navigationBarIsEmptyPropertyWatcher, SIGNAL(propertyChanged()),
               SLOT(_q_scheduleNavigationBarVisibilityUpdate()));

    updateStyleNames();

    // Create the view to get the "isEmpty" property updates.
    navigationBar->view();
    navigationBar->setVisible(false);
    sceneManager->addSceneWindow(navigationBar);

    sceneManager->appearSceneWindowNow(homeButtonPanel);

    // Initialize escape button to close mode.
    navigationBar->setEscapeButtonMode(MNavigationBarModel::EscapeButtonClose);
    QObject::connect(navigationBar, SIGNAL(closeButtonClicked()), q, SLOT(close()));

    if (needsDockWidget()) {
        sceneManager->appearSceneWindowNow(dockWidget);
    }

    if (!MApplication::fullScreen() && statusBar) {
        sceneManager->appearSceneWindowNow(statusBar);
    }

    _q_placeToolBar();

    // Unlike plain MWindows, the full-fledged application window have rounded corners
    // enabled by default.
    q->setRoundedCornersEnabled(true);

    applyWindowBackground();

    initAutoHideComponentsTimer();

#ifdef HAVE_CONTEXTSUBSCRIBER
    q->connect(&callStatusProperty, SIGNAL(valueChanged()), SLOT(_q_updateStatusBarVisibility()));
#endif
    q->connect(q, SIGNAL(orientationAngleChanged(M::OrientationAngle)),
            SLOT(_q_updatePageExposedContentRect()));
    q->connect(MTheme::instance(), SIGNAL(themeChangeCompleted()),
               SLOT(_q_updatePageExposedContentRect()));

    if (statusBar) {
        q->connect(statusBar, SIGNAL(appeared()), SLOT(_q_updatePageExposedContentRect()));
        q->connect(statusBar, SIGNAL(disappeared()), SLOT(_q_updatePageExposedContentRect()));
    }

    navigationBarVisibilityUpdateTimer.setSingleShot(true);
    navigationBarVisibilityUpdateTimer.setInterval(0);
    q->connect(&navigationBarVisibilityUpdateTimer, SIGNAL(timeout()),
               SLOT(_q_updateNavigationBarVisibility()));
}

void MApplicationWindowPrivate::_q_handleInSwitcherVisibilityChange()
{
    closeMenu();
    sceneManager->d_func()->closePopupWindows();
}

#ifdef Q_WS_X11
void MApplicationWindowPrivate::setWindowChainedProperty( const Window &parentWinId, const Window &childWinId )
{
    Atom atomMInvokedBy = XInternAtom(QX11Info::display(), "_MEEGOTOUCH_WM_INVOKED_BY", False);
    Display *display = QX11Info::display();

    // for compositor page transition
    XChangeProperty(display, childWinId,
            atomMInvokedBy, XA_WINDOW,
            32, PropModeReplace,
            (unsigned char *)&parentWinId, 1);

    // for task switcher view stacking
    XSetTransientForHint(display, childWinId, parentWinId);
}

void MApplicationWindowPrivate::appendMApplicationWindowTypeProperty()
{
    Q_Q(MApplicationWindow);

    Atom atomWindowType = XInternAtom(QX11Info::display(), "_MEEGOTOUCH_NET_WM_WINDOW_TYPE_MAPPLICATION", False);
    XChangeProperty(QX11Info::display(), q->effectiveWinId(),
                    XInternAtom(QX11Info::display(), "_NET_WM_WINDOW_TYPE", False),
                    XA_ATOM, 32, PropModeAppend, (unsigned char*) &atomWindowType, 1);
}
#endif

void  MApplicationWindowPrivate::initAutoHideComponentsTimer()
{
    autoHideComponentsTimer.setInterval(style()->autoHideTimeout());
    autoHideComponentsTimer.setSingleShot(true);
}

void MApplicationWindowPrivate::windowStateChangeEvent(QWindowStateChangeEvent *event)
{
    Q_Q(MApplicationWindow);
    if (!statusBar)
        return;

    if (q->isFullScreen() != event->oldState().testFlag(Qt::WindowFullScreen)) {
        _q_updateStatusBarVisibility();
    }
}

void MApplicationWindowPrivate::maximizePageArea()
{
    pageAreaMaximized = true;

    // When maximized, the window is in control of these components.
    setComponentDisplayMode(homeButtonPanel, MApplicationPageModel::Hide);

    if (navigationBar->focusItem()) {
        // Always show focused navigation bar.
        setComponentDisplayMode(navigationBar, MApplicationPageModel::Show);
    } else {
        setComponentDisplayMode(navigationBar, MApplicationPageModel::Hide);
    }

    if (needsDockWidget()) {
        if (dockWidget->focusItem()) {
            // Always show focused dock widget.
            setComponentDisplayMode(dockWidget, MApplicationPageModel::Show);
        } else {
            setComponentDisplayMode(dockWidget, MApplicationPageModel::Hide);
        }
    }


    // Now that we've set pageAreaMaximized, this update tries to hide status bar.
    _q_updateStatusBarVisibility();
}

void MApplicationWindowPrivate::restorePageArea()
{
    if (!pageAreaMaximized) {
        return;
    }

    pageAreaMaximized = false;

    if (page) {
        setComponentDisplayMode(homeButtonPanel, page->model()->homeButtonDisplayMode());
        setComponentDisplayMode(navigationBar, page->model()->navigationBarDisplayMode());

        if (needsDockWidget()) {
            // Dock widget follows navigation bar display mode.
            setComponentDisplayMode(dockWidget, page->model()->navigationBarDisplayMode());
        }
    }

    // Show status bar
    _q_updateStatusBarVisibility();
}

void MApplicationWindowPrivate::_q_updateStatusBarVisibility()
{
    Q_Q(MApplicationWindow);
    if (!statusBar)
        return;

#ifdef HAVE_CONTEXTSUBSCRIBER
    // Status bar should always be appeared while a phone call is ongoing.
    if (callStatusProperty.value().toString() == "active") {
        statusBar->appear(q);
        return;
    }
#endif

    if (q->isFullScreen() || pageAreaMaximized) {
        statusBar->disappear();
    } else {
        statusBar->appear(q);
    }
}

void MApplicationWindowPrivate::_q_actionUpdated(QActionEvent *event)
{
    QAction *action = event->action();

    if (event->type() == QEvent::ActionAdded) {
        QAction *before = event->before();
        distributeAction(action, before);
    } else if (event->type() == QEvent::ActionRemoved) {
        menu->removeAction(action);
        toolBar->removeAction(action);
    }
    refreshArrowIconVisibility();

    if (page) {
        // Dock widget follows navigation bar display mode.
        setComponentDisplayMode(dockWidget,
                                page->model()->navigationBarDisplayMode());
    }
}

void MApplicationWindowPrivate::_q_handlePageModelModifications(const QList<const char *> &modifications)
{
    Q_Q(MApplicationWindow);
    const char *member;

    foreach(member, modifications) {
        if (!pageAreaMaximized) {
            if (member == MApplicationPageModel::HomeButtonDisplayMode) {
                setComponentDisplayMode(homeButtonPanel,
                                        page->model()->homeButtonDisplayMode());

            } else if (member == MApplicationPageModel::NavigationBarDisplayMode) {
                if (page->model()->navigationBarDisplayMode() == MApplicationPageModel::AutoHide)
                    addComponentToAutoHide(navigationBar);
                else
                    removeComponentFromAutoHide(navigationBar);

                _q_scheduleNavigationBarVisibilityUpdate();

                // Dock widget follows navigation bar display mode.
                setComponentDisplayMode(dockWidget,
                                        page->model()->navigationBarDisplayMode());

                // Display mode can be changed between "auto-hide" and "show" which does not
                // affect appearance state. Need to update exposed rectangle explicitly.
                _q_updatePageExposedContentRect();
            } else if (member == MApplicationPageModel::EscapeButtonDisplayMode) {
                _q_scheduleNavigationBarVisibilityUpdate();
            }
        }

        if (member == MApplicationPageModel::ProgressIndicatorVisible) {
            navigationBar->setProgressIndicatorVisible(page->model()->progressIndicatorVisible());

        } else if (member == MApplicationPageModel::EscapeMode) {
            setupPageEscape();

        } else if (member == MApplicationPageModel::Title) {
            if (page->isVisible()) {
                navigationBar->setViewMenuDescription(page->model()->title());
                q->setWindowTitle(longestLengthVariant(page->model()->title()));
            }
        }
    }
}

void MApplicationWindowPrivate::_q_placeToolBar()
{
    // set position of toolbar
    if (needsDockWidget()) {
        // take toolbar from navigation bar
        navigationBar->undockToolBar();
        // dock it in lower dock widget
        dockWidget->dockToolBar(toolBar);
        // show dockwidget
        if (page) {
            // Dock widget follows navigation bar display mode.
            setComponentDisplayMode(dockWidget,
                                    page->model()->navigationBarDisplayMode());
        }
    } else {
        // hide dockwidget
        dockWidget->disappear();
        // undock toolbar
        dockWidget->undockToolBar();
        // redock to navigation bar
        navigationBar->dockToolBar(toolBar);
    }
}

void MApplicationWindowPrivate::_q_updatePageExposedContentRect()
{
    if (!page) {
        return;
    }

    qreal topCoverage = 0.0f;
    qreal bottomCoverage = 0.0f;

    QRectF pageExposedContentRect;

    // Interpretation of whether the navigation bar is covering the page
    // changes from "auto-hide" to "show". On "auto-hide" the navigation
    // bar doesn't count as covering the page since it does so only momentarily.
    const bool navBarIsCovering = showingNavigationBar &&
            page->componentDisplayMode(MApplicationPage::NavigationBar) != MApplicationPageModel::AutoHide;

    if (navBarIsCovering) {
        if (navigationBar->alignment().testFlag(Qt::AlignTop))
            topCoverage += navigationBar->size().height();
        else if (navigationBar->alignment().testFlag(Qt::AlignBottom))
            bottomCoverage += navigationBar->size().height();
    }


    if (showingDockWidget) {
        if (dockWidget->alignment().testFlag(Qt::AlignTop))
            topCoverage += dockWidget->size().height();
        else if (dockWidget->alignment().testFlag(Qt::AlignBottom))
            bottomCoverage += dockWidget->size().height();
    }

    pageExposedContentRect.setY(topCoverage);
    pageExposedContentRect.setWidth(page->geometry().width());
    pageExposedContentRect.setHeight(page->geometry().height()
                              - pageExposedContentRect.y() - bottomCoverage);

    page->d_func()->setExposedContentRect(pageExposedContentRect);
}

void MApplicationWindowPrivate::openMenu()
{
    Q_Q(MApplicationWindow);
    if (navigationBar->isArrowIconVisible() &&
        (navigationBar->sceneWindowState() != MSceneWindow::Disappearing)) {
        menu->appear(q);
        navigationBar->setEscapeButtonEnabled(false);
    }
}

void MApplicationWindowPrivate::closeMenu()
{
    if (menu->sceneWindowState() == MSceneWindow::Appeared || menu->sceneWindowState() == MSceneWindow::Appearing)
        menu->disappear();
}

bool MApplicationWindowPrivate::isMenuOpen() const
{
    return (menu->sceneWindowState() == MSceneWindow::Appeared);
}

void MApplicationWindowPrivate::_q_menuAppeared()
{
    Q_Q(MApplicationWindow);
    QObject::disconnect(navigationBar, SIGNAL(viewmenuTriggered()),
                        q, SLOT(openMenu()));
    QObject::connect(navigationBar, SIGNAL(viewmenuTriggered()),
                     q, SLOT(closeMenu()));
    autoHideComponentsTimer.stop();
}

void MApplicationWindowPrivate::_q_menuDisappeared()
{
    Q_Q(MApplicationWindow);
    QObject::disconnect(navigationBar, SIGNAL(viewmenuTriggered()),
                        q, SLOT(closeMenu()));
    QObject::connect(navigationBar, SIGNAL(viewmenuTriggered()),
                     q, SLOT(openMenu()));

    navigationBar->setEscapeButtonEnabled(true);
{'fraction_non_alphanumeric': 0.11746316533463817, 'fraction_numerical': 0.003588321644103735, 'mean_word_length': 3.4557897286821704, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 237, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '401837', 'n_tokens_mistral': 10459, 'n_tokens_neox': 10058, 'n_words': 2183}