javauis/eswt_akn/org.eclipse.ercp.swt.s60/native/src/swtdisplay.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 13 Oct 2010 14:23:59 +0300
branchRCL_3
changeset 83 26b2b12093af
parent 66 2455ef1f5bbc
permissions -rw-r--r--
Revision: v2.2.17 Kit: 201041

/*******************************************************************************
 * Copyright (c) 2005, 2010 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Nokia Corporation - S60 implementation
 *******************************************************************************/


#include <apgwgnam.h>
#include <swtlaffacade.h>
#include <akntitle.h>
#include "swtdisplay.h"
#include "eswtwidgetscore.h"
#include "methodwrappers.h"
#include "swtevents.h"
#include "swtkeymapper.h"
#include "swttimer.h"
#include "utils.h"
#include "swtdialogbroker.h"
#include "swtserver.h"
#include "swtmidremconobserver.h"
#include "swtuiutils.h"
#include "methodcall.h"
#include "swtfont.h"

// Default name displayed in the tasklist
_LIT(KAppDisplayName, "eSWT");

#define ASSERT_JAVAUITHREAD() ASSERT( IsCurrentThreadJavaUi() )
#define ASSERT_NATIVEUITHREAD() ASSERT( IsCurrentThreadNativeUi() )


// ======== MEMBER FUNCTIONS ========


// Java Ui Thread
CSwtDisplay* CSwtDisplay::NewL(JNIEnv& aJniEnv, jobject aPeer, TInt aDisplayParameter)
{
    jweak globalPeer = aJniEnv.NewWeakGlobalRef(aPeer);
    if (globalPeer == NULL)
        User::Leave(KErrNoMemory);

    CSwtDisplay* self = NULL;
    TRAPD(error, (self=DoNewL(aJniEnv, globalPeer, aDisplayParameter)));
    if (error)
    {
        aJniEnv.DeleteWeakGlobalRef(globalPeer);
        User::Leave(error);
    }

    return self;
}

// Java Ui Thread
void CSwtDisplay::Dispose(JNIEnv& aJniEnv)
{
    iDisposing = ETrue;

    if (iEventQueue)
        iEventQueue->PrepareForDestruction();

    TMethodWrapper0<CSwtDisplay> uiDestructor(*this, &CSwtDisplay::DestroyInNativeUiThread);
    ExecuteInNativeUiThread(uiDestructor);

    jweak globalPeer = static_cast<jweak>(iPeer);
    aJniEnv.DeleteWeakGlobalRef(globalPeer);

    delete iEventQueue;
    iEventQueue = NULL;

    iJavaUiThread.Close();
    iClient.Close();

    // No more Window Server events.
    java::ui::CoreUiAvkonEswt::getInstance().getJavaUiAppUi()->removeChild(this);

    // This will callback in CoreUiAvkonEswtSupportImpl::dispose,
    // the core ui will dissapear and native ui thread will exit.
    java::ui::CoreUiAvkonEswt::getInstance().dispose();
}

// Java Ui Thread
void CSwtDisplay::RequestRunDialogL(TSwtPeer aPeer, TInt aDialogType,
                                    TInt aStyle, const TDesC& aTitle, const TDesC& aText1, const TDesC& aText2,
                                    const TInt aInt1, const TInt aInt2, const TInt aInt3)
{
    TMethodWrapper0<CSwtDisplay> fp(*this, &CSwtDisplay::DialogAboutToOpen);
    ExecuteInNativeUiThread(fp);
    CSwtDialogBroker* dlgBroker(NULL);
    CallMethodL(dlgBroker, this, &CSwtDisplay::CreateDialogBrokerL);
    dlgBroker->SetExtraData(aInt1, aInt2, aInt3);
    CallMethodL(dlgBroker, &CSwtDialogBroker::RequestRunDialogL, aPeer, aDialogType,
                aStyle, aTitle, aText1, aText2);
}

// Native Ui Thread
void CSwtDisplay::OfferWsEventL(const TSwtWsEvent& aEvent, CCoeControl* aDestination /*= NULL*/)
{
    ASSERT_NATIVEUITHREAD();

    switch (aEvent)
    {
    case SwtWsEventEndKeyPressed:
    {
        // At this point the application is already sent to background by Avkon
        // - Send close event to display ( happening before anything has been disposed )
        // - Close event can be modified doIt=false
        // - If doIt==false then we don't do anything
        // - If doIt==true then we notify the runtime of exit
        if (!iCloseEventDispatchTimer)
        {
            iCloseEventDispatchTimer = CPeriodic::NewL(CActive::EPriorityStandard);
        }
        if (!iCloseEventDispatchTimer->IsActive())
        {
            iCloseEventDispatched = EFalse;
            PostCloseEventL(iPeer, iCloseEventDispatched);
            iCloseEventDispatchTimer->Start(5000000, 0, TCallBack(CloseEventDispatchTimerCallback, this));
        }
    }
    break;

    case SwtWsEventShutdown:
    {
        // MIDP's wrapper asks the runtime to exit the process. It is the MIDlet's
        // responsibilty to call Display.dispose inside destroyApp().
        // eRCP's wrapper does not exit the process and does not dispose the display.
        void* env = NULL;
        void* args = NULL;
        if (iVM && iVM->AttachCurrentThread(&env, args) >= 0)
        {
            JNIEnv* jniEnv = reinterpret_cast<JNIEnv*>(env);
            TBool failed;
            CallStaticVoidJavaMethod(failed,
                                     jniEnv,
                                     "org/eclipse/swt/internal/symbian/ExitNotificationWrapper",
                                     "notifyExit",
                                     "(I)V",
                                     iApplicationUid);
            iVM->DetachCurrentThread();
        }
    }
    break;

    case SwtWsEventAppFocusGained:
    case SwtWsEventAppFocusLost:
    {
        TBool focused = aEvent == SwtWsEventAppFocusGained;

        // UiUtils intentionally handled separately although it is an app focus observer also.
        if (iUiUtils)
        {
            iUiUtils->HandleAppFocusChangeL(focused);
        }

        // Inform other observers.
        const TInt count = iAppFocusObservers.Count();
        for (TInt i = 0; i < count; i++)
        {
            iAppFocusObservers[i]->HandleAppFocusChangeL(focused);
        }

        // Inform Java
        PostForegroundEventL(iPeer, focused);
    }
    break;

    case SwtWsEventWindowVisibilityChanged:
    {
        if (iMenuArranger)
        {
            iMenuArranger->HandleWindowVisibilityChangeL(aDestination);
        }
    }
    break;

    default:
        break;
    }

    if (aEvent == CSwtLafFacade::GetConstant(CSwtLafFacade::EBrowserFreeRam)
            ||  aEvent == CSwtLafFacade::GetConstant(CSwtLafFacade::EBrowserMemoryGood))
    {
        if (iUiUtils)
        {
            iUiUtils->HandleFreeRamEventL(aEvent);
        }
    }
}

// Native Ui Thread
void CSwtDisplay::RemoveDialogBroker(CSwtDialogBroker* aBroker)
{
    ASSERT(aBroker);
    for (TInt i = iDialogBrokers.Count()-1; i > -1; i--)
    {
        if (iDialogBrokers[i] == aBroker)
        {
            iDialogBrokers.Remove(i);
        }
    }
    delete aBroker;
    aBroker = NULL;
}

// From java::ui::CoreAppUiChild
// Native Ui Thread
void CSwtDisplay::HandleForegroundEventL(TBool /*aForeground*/)
{
}

// From java::ui::CoreAppUiChild
// Native Ui Thread
void CSwtDisplay::HandleSwitchOnEventL(CCoeControl* /*aDestination*/)
{
}

// From java::ui::CoreAppUiChild
// Native Ui Thread
void CSwtDisplay::HandleApplicationSpecificEventL(TInt /*aType*/, const TWsEvent& /*aEvent*/)
{
}

// From java::ui::CoreAppUiChild
// Native Ui Thread
void CSwtDisplay::HandleCommandL(TInt aCommand)
{
    ASSERT_NATIVEUITHREAD();

    // System request to exit the application ( e.g. close from task list or out-of-memory )
    if (aCommand == EEikCmdExit)
    {
        // Exiting is handled in HandleWsEventL. Let's not do anything here
        // to prevent duplicates.
    }

    if (aCommand == EAknCmdHelp)
    {
        MSwtCommandArranger* commandArranger = CommandArranger();
        if (commandArranger)
        {
            commandArranger->DoHelpOperationL();
        }
    }
}

// From java::ui::CoreAppUiChild
// Native Ui Thread
void CSwtDisplay::HandleResourceChangeL(TInt aType)
{
    if (iDisposing)
    {
        return;
    }

    TBool colorChange(EFalse);
    switch (aType)
    {
        // KAknsMessageSkinChange and KEikColorResourceChange are always sent in tandem.
        // ( See AknsAppSkinInstance.cpp ) Unfortunately we have to pass them both events
        // to the Avkon components as some do not react to them in the same matter.
        // eSWT components must pass both events to their Avkon contained or inherited
        // components and try to handle other jobs ( like font update ) only once.
    case KAknsMessageSkinChange:
    case KEikColorResourceChange:
        colorChange = ETrue;
        // Intentional fall through;
    case KEikDynamicLayoutVariantSwitch:
        if (iMenuArranger)
        {
            iMenuArranger->HandleResolutionChangeL();
        }
        // Intentional fall through;
    case KEikMessageFontChange:
        // Update system font
    {
        if (iSystemFont)
        {
            iSystemFont->RemoveRef();
            iSystemFont = NULL;
        }
        iSystemFont = CSwtFont::NewL(Device(), *AknLayoutUtils::FontFromId(
                                         CSwtLafFacade::GetFontId(CSwtLafFacade::EForm2MidpLabelPaneT1Font, 0)));
        // Send the Settings Event
        // KAknsMessageSkinChange and KEikColorResourceChange are always sent in tandem.
        if (aType != KEikColorResourceChange && iEventQueue)
        {
            iEventQueue->PushL(new(ELeave) CSwtSettingsEvent(iPeer, colorChange));
        }
    }
    break;
    // Not really needed to be handled here.
    case KEikMessageFadeAllWindows:
    case KEikMessageUnfadeWindows:
        break;
    default:
        ASSERT(ETrue);
        break;
    }

    if (iUiUtils)
    {
        iUiUtils->HandleResourceChangedL(aType);
    }

    // Forward HandleResourceChange to observers
    const TInt observerNb = iResourceChangeObservers.Count();
    for (TInt observerIndex=0; observerIndex<observerNb; ++observerIndex)
    {
        if (iResourceChangeObservers[observerIndex])
        {
            iResourceChangeObservers[observerIndex]->OfferResourceChangeL(aType);
        }
    }
}

// From java::ui::CoreAppUiChild
// Native Ui Thread
TBool CSwtDisplay::HandleWsEventL(const TWsEvent& aEvent, CCoeControl* aDestination)
{
    TSwtWsEvent swtEventType = SwtWsEventUnknown;
    TInt eventType = aEvent.Type();

    switch (eventType)
    {
    case KAknUidValueEndKeyCloseEvent:
        // Let CSwtDisplay::OfferWsEventL handle end ( red ) key.
        // CAknAppUi::HandleWsEventL should not handle it.
        swtEventType = SwtWsEventEndKeyPressed;
        break;

    case EEventUser:
        if ((*reinterpret_cast<TApaSystemEvent*>(aEvent.EventData())) == EApaSystemEventShutdown)
        {
            // Oom or exit from task-list:
            // - Send close event to display ( happening before anything has been disposed )
            // - Close event's doIt is ignored
            // - After calling the close event listeners then we notify the runtime of exit
            if (!iCoeEnv->IsSystem())
            {
                swtEventType = SwtWsEventShutdown;
            }
        }
        break;

#ifdef RD_SCALABLE_UI_V2
        // In versions higher than 3.23 different event is sent, when closing
        // midlet using the C-key.
    case KAknShutOrHideApp:
        swtEventType = SwtWsEventShutdown;
        break;
#endif //RD_SCALABLE_UI_V2

    case EEventFocusGained:
        swtEventType = SwtWsEventAppFocusGained;
        break;

    case EEventFocusLost:
        swtEventType = SwtWsEventAppFocusLost;
        break;

    case EEventWindowVisibilityChanged:
        swtEventType = SwtWsEventWindowVisibilityChanged;
        break;

    default:
        break;
    }

    if (swtEventType != SwtWsEventUnknown)
    {
        OfferWsEventL(swtEventType, aDestination);
    }

    // Returning ETrue here means that CAknAppUi will not handle the event.
    return (eventType == KAknUidValueEndKeyCloseEvent);
}

// From MSwtDisplay
// Any thread
#ifdef _DEBUG
TBool CSwtDisplay::IsCurrentThreadNativeUi() const
{
    return (RThread().Id() == iNativeUiThread.Id());
}
#endif

// From MSwtDisplay
// Any thread
TSwtPeer CSwtDisplay::JavaPeer()
{
    return iPeer;
}

// From MSwtDisplay
// Native UI Thread
void CSwtDisplay::SetNameInTaskListL(const TDesC* aNewName)
{
    // This code seems to have no effect on the task list.
    CApaWindowGroupName* wgName = java::ui::CoreUiAvkonEswt::getInstance().getWindowGroupName();
    if (wgName)
    {
        if (aNewName)
        {
            wgName->SetCaptionL(*aNewName);
        }
        else
        {
            wgName->SetCaptionL(KAppDisplayName);
        }
        // Refresh displayed name in tasklist
        wgName->SetWindowGroupName(iCoeEnv->RootWin());
    }
}

// From MSwtDisplay
// Native UI Thread
// For eRCP
void CSwtDisplay::SetUIDInTaskList(TInt aNewUID)
{
    CApaWindowGroupName* wgName = java::ui::CoreUiAvkonEswt::getInstance().getWindowGroupName();
    if (wgName)
    {
        wgName->SetAppUid((TUid::Uid(aNewUID)));
        // Refresh displayed name in tasklist
        wgName->SetWindowGroupName(iCoeEnv->RootWin());
    }
}

// From MSwtDisplay
// Native UI Thread
// For eRCP
void CSwtDisplay::SetAppVisible(TBool aVisible)
{
    CApaWindowGroupName* wgName = java::ui::CoreUiAvkonEswt::getInstance().getWindowGroupName();
    if (wgName)
    {
        wgName->SetHidden(!aVisible);
        // Refresh displayed name in tasklist
        wgName->SetWindowGroupName(iCoeEnv->RootWin());
    }
}

// From MSwtDisplay
// Native UI Thread
void CSwtDisplay::SetUiReady(TBool aFullScreenUi)
{
    if (!iUiReady)
    {
        iUiReady = ETrue;
        java::ui::CoreUiAvkonAppUi* appUi = java::ui::CoreUiAvkonEswt::getInstance().getJavaUiAppUi();
        if (appUi)
        {
            if (!appUi->isStartupCancelled())
            {
                // Bring app to foreground
                RWindowGroup& group = iCoeEnv->RootWin();
                group.SetOrdinalPosition(0);
            }
            appUi->stopStartScreen(aFullScreenUi);
        }
    }
}

// From MSwtDisplay
// Native UI Thread
TBool CSwtDisplay::IsUiReady() const
{
    return iUiReady;
}

// Java Ui Thread
CSwtDisplay* CSwtDisplay::DoNewL(JNIEnv& aJniEnv, TSwtPeer aPeer, TInt aApplicationUid)
{
    CSwtDisplay* self = new(ELeave) CSwtDisplay(aPeer);
    CleanupStack::PushL(self);
    self->iApplicationUid = aApplicationUid;
    self->ConstructInJavaUiThreadL(aJniEnv);
    CleanupStack::Pop(self);
    return self;
}

// Java Ui Thread
void CSwtDisplay::ConstructInJavaUiThreadL(JNIEnv& aJniEnv)
{
    iDisposing = EFalse;

    // Step 1 : Get CAknAppUi
    java::ui::CoreUiAvkonEswt& ui = java::ui::CoreUiAvkonEswt::getInstance();
    ui.ensureInitialized(TUid::Uid(iApplicationUid));
    User::LeaveIfError(iJavaUiThread.Open(RThread().Id()));
#ifdef _DEBUG
    iNativeUiThread = ui.getEswtSupport().thread();
#endif //_DEBUG
    ui.getEswtSupport().setDisplay(this);

    // Step 2: Construct display base in java ui thread
    ASwtDisplayBase::ConstructInJavaUiThreadL();

    // Step 3: Open a session to the function server
    User::LeaveIfError(iClient.Connect());

    // Step 4: Construct in native ui thread
    TMethodWrapper0<CSwtDisplay> uiConstructor(*this, &CSwtDisplay::ConstructInNativeUiThreadL);
    TInt error = ExecuteInNativeUiThread(uiConstructor);
    if (error)   // Cannot use LeaveIfError(), it wouldn't leave on errors >0
        User::Leave(error);

    // Step 5: Store the Jni
    User::LeaveIfError(aJniEnv.GetJavaVM(&iVM));

    // Step 6: Set CoreUi child. Ready to receive Window Server events.
    //         CoreUI takes the ownership of the CSwtDisplay object.
    ui.getJavaUiAppUi()->setEswtChild(this);
}

// Native Ui Thread
void CSwtDisplay::DoExecuteInNativeUiThreadL(const MSwtFunctor* aFunctor)
{
    (*aFunctor)();   // It doesn't look like it but this may leave
}

// Native Ui Thread
TInt CSwtDisplay::CloseEventDispatchTimerCallback(TAny* aThis)
{
    CSwtDisplay* self = static_cast< CSwtDisplay* >(aThis);
    ASSERT(self);

    TRAPD(err, self->HandleCloseEventDispatchTimerCallbackL());
    return err;
}

// Native Ui Thread
CSwtDisplay::~CSwtDisplay()
{
    // Cannot have anything here. Use Dispose().
    // The object will be deleted from CoreUi.
}

// Native Ui Thread
void CSwtDisplay::ConstructInNativeUiThreadL()
{
    ASwtDisplayBase::ConstructInNativeUiThreadL();

    if (java::ui::CoreUiAvkonEswt::getInstance().getJavaUiAppUi()->isForeground())
    {
        iUiUtils->HandleAppFocusChangeL(ETrue);
    }
    else
    {
        iUiUtils->HandleAppFocusChangeL(EFalse);
    }

    // iAppFocusObservers empty at this point
}

// Native Ui Thread
void CSwtDisplay::DestroyInNativeUiThread()
{
    ASSERT_NATIVEUITHREAD();

    ASwtDisplayBase::DestroyInNativeUiThread();

    if (iCloseEventDispatchTimer)
    {
        if (iCloseEventDispatchTimer->IsActive())
        {
            iCloseEventDispatchTimer->Cancel();
        }
        delete iCloseEventDispatchTimer;
        iCloseEventDispatchTimer = NULL;
    }

    CSwtDialogBroker* dlgBroker(NULL);
    for (TInt i = iDialogBrokers.Count() - 1; i > -1; i--)
    {
        dlgBroker = iDialogBrokers[ i ];
        iDialogBrokers.Remove(i);
        delete dlgBroker;
    }
    iDialogBrokers.Close();
}

// Native Ui Thread
CSwtDialogBroker* CSwtDisplay::CreateDialogBrokerL()
{
    CSwtDialogBroker* dlgBroker =  new(ELeave) CSwtDialogBroker(this, CSwtDialogBroker::ENoRequest);
    iDialogBrokers.Append(dlgBroker);
    return dlgBroker;
}

// Native Ui Thread
void CSwtDisplay::HandleCloseEventDispatchTimerCallbackL()
{
    iCloseEventDispatchTimer->Cancel();
    if (iCloseEventDispatched)
    {
        return;
    }

    // The event did not dispatch in 5 seconds. Destroy the UI.
    OfferWsEventL(SwtWsEventShutdown);
}

// Native Ui Thread
void CSwtDisplay::DialogAboutToOpen()
{
    ASSERT_NATIVEUITHREAD();
    if (!IsUiReady())
    {
        SetUiReady(EFalse);
    }
}