phonebookui/Phonebook/View/src/CPbkContactEditorDlg.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 13 Oct 2010 14:15:33 +0300
branchRCL_3
changeset 85 38bb213f60ba
parent 68 9da50d567e3c
permissions -rw-r--r--
Revision: 201039 Kit: 201041

/*
* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*           Methods for Phonebook Contact editor dialog.
*
*/


// INCLUDE FILES
#include "CPbkContactEditorDlg.h"

#include <eikspane.h>
#include <avkon.hrh>
#include <akntitle.h>
#include <aknnavi.h>
#include <eikmenub.h>
#include <featmgr.h>
#include <StringLoader.h>
#include <AknQueryDialog.h>
#include <aknnotewrappers.h>
#include <AknsUtils.h>
#include <eikcapc.h>
#include <hlplch.h>   

#include "CPbkThumbnailCmd.h"
#include "CPbkContextPaneIcon.h"
#include "CPbkContactEditorFieldArray.h"
#include "MPbkContactEditorUiBuilder.h"
#include <MPbkContactEditorField.h>
#include <MPbkContactEditorControl.h>
#include "PbkContactEditorStrategyFactory.h"
#include "MPbkContactEditorStrategy.h"
#include "TPbkAddItemManager.h"
#include "CPbkThumbnailPopup.h"
#include <PbkView.hrh>
#include "PbkUID.h"
#include "MPbkFieldEditorVisitor.h"
#include "MPbkFieldEditorControl.h"
#include "MPbkEditorOkToExitCallback.h"

#include <CPbkContactEngine.h>
#include <CPbkContactItem.h>
#include <CPbkFieldsInfo.h>
#include <CPbkConstants.h>
#include <CPbkFieldData.h>
#include <PbkView.rsg>
#include <MPbkContactEditorExtension.h>
#include <MPbkExtensionFactory.h>
#include <CPbkExtGlobals.h>

#include <PbkDebug.h>


/// Unnamed namespace for local definitions
namespace {

// LOCAL CONSTANTS AND MACROS

/// Escape key press event
static const TKeyEvent KEscKeyEvent = { EKeyEscape, 0, 0, 0 };

#ifdef _DEBUG
/// Panic codes for CPbkContactEditorDlg
enum TPanicCode
    {
    EPanicPostCond_Constructor,
    EPanicPreCond_ResetWhenDestroyed,
    EPanicPreCond_CmdFetchThumbnailL,
    EPanicPreCond_CmdRemoveThumbnailL,
    EPanicPreCond_PreLayoutDynInitL,
    EPanicPostCond_PreLayoutDynInitL,
    EPanicPreCond_SetInitialCurrentLine,
    EPanicPreCond_ContactAlreadyExistsL
    };

void Panic(TPanicCode aReason)
    {
    _LIT(KPanicText, "CPbkContactEditorDlg");
    User::Panic(KPanicText, aReason);
    }
#endif  // _DEBUG

void UpdateCbasL( MPbkContactEditorField* aCurrentField,
                  CEikButtonGroupContainer& aCba )
    {
    if ( aCurrentField )
        {
        TPbkFieldEditMode editMode =
            aCurrentField->ContactItemField().FieldInfo().EditMode();
        
        if ( editMode ==  EPbkFieldEditModeSelector )
            {
            if ( !aCba.IsCommandInGroup(EAknSoftkeyChange) )
                {
                aCba.SetCommandSetL( R_PBK_SOFTKEYS_OPTIONS_DONE_CHANGE );    
                aCba.DrawDeferred();
                } 
            }
        else
            {
            if ( !aCba.IsCommandInGroup(EAknSoftkeyContextOptions) )
                {
                aCba.SetCommandSetL( R_PBK_SOFTKEYS_OPTIONS_DONE_CONTEXT );    
                aCba.DrawDeferred();                
                }
            }
        }
    }

}  // namespace


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

/**
 * Extension implementation.
 */
NONSHARABLE_CLASS(CPbkContactEditorDlg::CExtension) :
        public CBase,
        public MPbkContactEditorControl
    {
    public: // Constructor and destructor
        /**
         * Creates a new CExtension.
         * @param aContact currently edited contact
         */
        static CExtension* NewL
            (CPbkContactItem& aContact,
            const CPbkContactEditorDlg& aParent);
        /**
         * Destructor
         */
        ~CExtension();

    public: // From MPbkContactEditorExtension API
        void DynInitMenuPaneL(TInt aResourceId, CEikMenuPane* aMenuPane);
        TBool ProcessCommandL(TInt aCommandId);
        void PreOkToExitL(TInt aButtonId);
        void PostOkToExitL(TInt aButtonId);
        void SetStateModified();

    public: // From MPbkContactEditorControl
        MPbkContactEditorField* FocusedField();
        TInt EditorCount();
        const MPbkContactEditorField& FieldAt(TInt aFieldIndex);

    private: // Implementation
        CExtension(const CPbkContactEditorDlg& aParent);
        void ConstructL(CPbkContactItem& aContact);

    private: // Data
        /// Ref: for creating editor extension
        CPbkExtGlobals* iExtGlobal;
        /// Ref: editor extension
        MPbkContactEditorExtension* iEditorExtension;
        /// Ref: parent editor
        const CPbkContactEditorDlg& iParent;
    };

CPbkContactEditorDlg::CExtension::CExtension
        (const CPbkContactEditorDlg& aParent) :
        iParent(aParent)
    {
    }

void CPbkContactEditorDlg::CExtension::ConstructL
        (CPbkContactItem& aContact)
    {
    iExtGlobal = CPbkExtGlobals::InstanceL();
    iEditorExtension = iExtGlobal->FactoryL().
        CreatePbkContactEditorExtensionL(aContact, iParent.iEngine, *this);
    }

CPbkContactEditorDlg::CExtension* CPbkContactEditorDlg::CExtension::NewL
        (CPbkContactItem& aContact,
        const CPbkContactEditorDlg& aParent)
    {
    CExtension* self = new (ELeave) CExtension(aParent);
    CleanupStack::PushL(self);
    self->ConstructL(aContact);
    CleanupStack::Pop(self);
    return self;
    }

CPbkContactEditorDlg::CExtension::~CExtension()
    {
    Release(iEditorExtension);
    Release(iExtGlobal);
    }

void CPbkContactEditorDlg::CExtension::DynInitMenuPaneL
        (TInt aResourceId, CEikMenuPane* aMenuPane)
    {
    iEditorExtension->DynInitMenuPaneL(aResourceId, aMenuPane);
    }

TBool CPbkContactEditorDlg::CExtension::ProcessCommandL(TInt aCommandId)
    {
    return iEditorExtension->ProcessCommandL(aCommandId);
    }

void CPbkContactEditorDlg::CExtension::PreOkToExitL(TInt aButtonId)
    {
    iEditorExtension->PreOkToExitL(aButtonId);
    }

void CPbkContactEditorDlg::CExtension::PostOkToExitL(TInt aButtonId)
    {
    iEditorExtension->PostOkToExitL(aButtonId);
    }

void CPbkContactEditorDlg::CExtension::SetStateModified()
    {
    iParent.iEditorStrategy->SetStateModified();
    }

MPbkContactEditorField* CPbkContactEditorDlg::CExtension::FocusedField()
    {
    const TInt id = iParent.IdOfFocusControl();
    return iParent.iFieldManager->Find(id);
    }

TInt CPbkContactEditorDlg::CExtension::EditorCount()
    {
    return iParent.iFieldManager->EditorCount();
    }

const MPbkContactEditorField& CPbkContactEditorDlg::CExtension::
        FieldAt(TInt aFieldIndex)
    {
    return iParent.iFieldManager->FieldAt(aFieldIndex);
    }

/**
 * Editor implementation class.
 */
NONSHARABLE_CLASS(CPbkContactEditorDlgImpl) :
        public CPbkContactEditorDlg,
        private MPbkContactEditorUiBuilder
    {
    public: // constructor
        /**
         * Creates a new instance of this class.
         * @param aEngine phonebook engine
         * @param aContactItem contact item
         * @param aIsNewContact defines is this new contact or not
         * @param aFocusIndex field to focus
         * @param AEdited edited flag
         */
        static CPbkContactEditorDlgImpl* NewL(
            CPbkContactEngine& aEngine,
            CPbkContactItem& aContactItem,
            TBool aIsNewContact,
            TInt aFocusIndex = -1,
            TBool aEdited = EFalse);

        /**
         * Destructor.
         */
        ~CPbkContactEditorDlgImpl();

    private: // from MPbkContactEditorUiBuilder
        CCoeControl* CreateLineL(const TDesC& aCaption, TInt aControlId,
                TInt aControlType);
        CEikCaptionedControl* LineControl(TInt aControlId) const;
        void DeleteControl(TInt aControlId);
        void TryChangeFocusL(TInt aControlId);
        void SetCurrentLineCaptionL(const TDesC& aText);
        CCoeControl* Control(TInt aControlId) const;
        void SetEditableL(TBool aState);

    private: // from CAknForm
        void HandleResourceChange(TInt aType);
        
    private: // from CCoeControl
        void FocusChanged(TDrawNow aDrawNow);

    private: // implementation
        CPbkContactEditorDlgImpl
            (CPbkContactEngine& aEngine,
            CPbkContactItem& aContactItem,
            TInt aFocusIndex = -1);
    private:
        class CPbkIconReloaderVisitor;
        CPbkIconReloaderVisitor* iIconReloaderVisitor;
    };

/**
 * Editor icon reloader visitor.
 */
NONSHARABLE_CLASS(CPbkContactEditorDlgImpl::CPbkIconReloaderVisitor) :
        public CBase,
        public MPbkFieldEditorVisitor
    {
    public:
        CPbkIconReloaderVisitor(MPbkContactEditorUiBuilder& aUiBuilder);
        ~CPbkIconReloaderVisitor();

    private: // From MPbkFieldEditorVisitor
        void VisitL(MPbkFieldEditorControl& aThis);

    private: // Data
        MPbkContactEditorUiBuilder& iUiBuilder;
    };

CPbkContactEditorDlgImpl::CPbkIconReloaderVisitor::CPbkIconReloaderVisitor
        (MPbkContactEditorUiBuilder& aUiBuilder) :
    iUiBuilder(aUiBuilder)
    {
    }

CPbkContactEditorDlgImpl::CPbkIconReloaderVisitor::~CPbkIconReloaderVisitor()
    {
    }

void CPbkContactEditorDlgImpl::CPbkIconReloaderVisitor::VisitL
        (MPbkFieldEditorControl& aThis)
    {
    aThis.LoadBitmapToFieldL(iUiBuilder);
    }

inline CPbkContactEditorDlgImpl::CPbkContactEditorDlgImpl(
        CPbkContactEngine& aEngine,
        CPbkContactItem& aContactItem,
        TInt aFocusIndex) :
    CPbkContactEditorDlg(aEngine, aContactItem, aFocusIndex)
    {
    }

CPbkContactEditorDlgImpl* CPbkContactEditorDlgImpl::NewL(
        CPbkContactEngine& aEngine,
        CPbkContactItem& aContactItem,
        TBool aIsNewContact,
        TInt aFocusIndex,
        TBool aEdited)
    {
    // Create the dialog
    CPbkContactEditorDlgImpl* self = new(ELeave) CPbkContactEditorDlgImpl(
            aEngine, aContactItem, aFocusIndex);
    CleanupStack::PushL(self);
    self->BaseConstructL(aIsNewContact, aEdited);

    // create field managing object
    self->iFieldManager = CPbkContactEditorFieldArray::NewL
        (aContactItem, *self);

    self->iIconReloaderVisitor = new(ELeave) CPbkIconReloaderVisitor(*self);
    CleanupStack::Pop(self);
    return self;
    }

CPbkContactEditorDlgImpl::~CPbkContactEditorDlgImpl()
    {
    delete iIconReloaderVisitor;
    }

CCoeControl* CPbkContactEditorDlgImpl::CreateLineL
        (const TDesC& aCaption,
        TInt aControlId,
        TInt aControlType)
    {
    return CreateLineByTypeL(
            aCaption, aControlId, aControlType, NULL);
    }

CEikCaptionedControl* CPbkContactEditorDlgImpl::LineControl
        (TInt aControlId) const
    {
    return Line(aControlId);
    }

void CPbkContactEditorDlgImpl::DeleteControl
        (TInt aControlId)
    {
    DeleteLine(aControlId);
    }

void CPbkContactEditorDlgImpl::TryChangeFocusL
        (TInt aControlId)
    {
    if (ControlOrNull(aControlId))
        {
        TryChangeFocusToL(aControlId);
        }
    }

void CPbkContactEditorDlgImpl::SetCurrentLineCaptionL
        (const TDesC& aText)
    {
    SetControlCaptionL(IdOfFocusControl(), aText);
    }

CCoeControl* CPbkContactEditorDlgImpl::Control
        (TInt aControlId) const
    {
    return ControlOrNull(aControlId);
    }

void CPbkContactEditorDlgImpl::SetEditableL(TBool aState)
    {
    CPbkContactEditorDlg::SetEditableL(aState);
    }

void CPbkContactEditorDlgImpl::HandleResourceChange(TInt aType)
    {
    CPbkContactEditorDlg::HandleResourceChange(aType);

    if (aType == KAknsMessageSkinChange)
        {
        TRAP_IGNORE(iFieldManager->AcceptL(*iIconReloaderVisitor));
        }
    else if (aType == KEikDynamicLayoutVariantSwitch)
        {        
        // It is not bad if thumbnail showing fails
        TRAP_IGNORE( ReloadThumbnailL() );        
        }
    }
    
void CPbkContactEditorDlgImpl::FocusChanged(TDrawNow /*aDrawNow*/)
    {
    // Thumbnail will be set as hidden if the control is not focused.
    // all is done in ReloadThumbnailL -method.
    // No big deal if thumbnail is not shown    
    TRAP_IGNORE(ReloadThumbnailL());
    }


///////////////////////////////////////////////////////////////
/// CPbkContactEditorDlg

inline CPbkContactEditorDlg::CPbkContactEditorDlg
        (CPbkContactEngine& aEngine,
        CPbkContactItem& aContactItem,
        TInt aFocusIndex) :
    iContactItem(aContactItem),
    iEngine(aEngine),
    iFocusIndex(aFocusIndex)
    {
    __ASSERT_DEBUG(!iTitlePane && !iThumbnailHandler && !iStoredTitlePaneText,
        Panic(EPanicPostCond_Constructor));
    }

inline void CPbkContactEditorDlg::StoreTitlePaneTextL()
    {
    CEikStatusPane* statusPane = iEikonEnv->AppUiFactory()->StatusPane();
    if (statusPane && statusPane->PaneCapabilities(TUid::Uid(EEikStatusPaneUidTitle)).IsPresent())
        {
        iTitlePane = static_cast<CAknTitlePane*>
            (statusPane->ControlL(TUid::Uid(EEikStatusPaneUidTitle)));
        if (iTitlePane->Text())
            {
            iStoredTitlePaneText = iTitlePane->Text()->AllocL();
            }
        }
    }

inline void CPbkContactEditorDlg::ConstructContextMenuL()
    {
    CEikMenuBar* newMenuBar = new(ELeave) CEikMenuBar();
    CleanupStack::PushL(newMenuBar);
    newMenuBar->ConstructL(this, NULL, R_PBK_CONTACTEDITOR_CONTEXT_MENUBAR);
    iEikonEnv->EikAppUi()->AddToStackL(newMenuBar, ECoeStackPriorityMenu, ECoeStackFlagRefusesFocus);
    iContextMenuBar = newMenuBar;
    CleanupStack::Pop(newMenuBar);
    }

inline void CPbkContactEditorDlg::ConstructNaviPaneL()
    {
    CEikStatusPane* statusPane = iEikonEnv->AppUiFactory()->StatusPane();
    if (statusPane && statusPane->PaneCapabilities(TUid::Uid(EEikStatusPaneUidNavi)).IsPresent())
        {
        // create a default empty status pane, otherwise sync field will show
        iNaviContainer = static_cast<CAknNavigationControlContainer *>(statusPane->ControlL(
                TUid::Uid(EEikStatusPaneUidNavi)));
        iNaviContainer->PushDefaultL();
        }
    }

inline void CPbkContactEditorDlg::BaseConstructL
        (TBool aIsNewContact, TBool aEdited)
    {
    // set feature manager in TLS
    FeatureManager::InitializeLibL();

    CAknDialog::ConstructL(R_PBK_CONTACTEDITOR_MENUBAR);

    iEditorStrategy = PbkContactEditorStrategyFactory::CreateL
        (iEngine, iContactItem, aIsNewContact);
    if (aEdited)
        {
        iEditorStrategy->SetStateModified();
        }

    // check existance of syncronization field
    if(!iContactItem.FindField(EPbkFieldIdSyncronization))
        {
        CPbkFieldInfo* syncFieldInfo = iEngine.FieldsInfo().Find(
            EPbkFieldIdSyncronization);
        if (syncFieldInfo)
            {
            // add syncronization field to contact - compulsory
            iContactItem.AddFieldL(*syncFieldInfo);
            iEditorStrategy->SetStateModified();
            }
        }
        
    StoreTitlePaneTextL();
    ConstructContextMenuL();
    ConstructNaviPaneL();

    iThumbnailHandler = CPbkThumbnailPopup::NewL(iEngine);
    iThumbnailCmd = CPbkThumbnailCmd::NewL(iEngine, *iThumbnailHandler);
    iExtension = CExtension::NewL(iContactItem, *this);
    }

EXPORT_C CPbkContactEditorDlg* CPbkContactEditorDlg::NewL
        (CPbkContactEngine& aEngine,
        CPbkContactItem& aContactItem,
        TBool aIsNewContact,
        TInt aFocusIndex/*=-1*/,
        TBool aEdited/*=EFalse*/)
    {
    return CPbkContactEditorDlgImpl::NewL(
            aEngine, aContactItem, aIsNewContact, aFocusIndex, aEdited);
    }

EXPORT_C void CPbkContactEditorDlg::ResetWhenDestroyed
        (CPbkContactEditorDlg** aSelfPtr)
    {
    __ASSERT_DEBUG(!aSelfPtr || *aSelfPtr == this,
            Panic(EPanicPreCond_ResetWhenDestroyed));
    iSelfPtr = aSelfPtr;
    }

EXPORT_C void CPbkContactEditorDlg::HideExitCommand()
    {
    iStateFlags.Set(EHideExit);
    }

EXPORT_C void CPbkContactEditorDlg::SetExitCommandId
        (TInt aCommandId)
    {
    iExitCommandId = aCommandId;
    }

CPbkContactEditorDlg::~CPbkContactEditorDlg()
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING("CPbkContactEditorDlg::~CPbkContactEditorDlg"));

    iStateFlags.Set(EUnderDestruction);
    CloseAllPopups();

    // Save unless saved yet. No other way to handle errors than ignore
    // because exiting the dialog must always be possible.
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING("CPbkContactEditorDlg::~CPbkContactEditorDlg(0x%x) saving"), this);
    TRAP_IGNORE(CmdSaveL());
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING("    saved"));

    // delete member data
    delete iThumbnailHandler;
    delete iThumbnailCmd;

    if (iContextMenuBar)
        {
        iEikonEnv->EikAppUi()->RemoveFromStack(iContextMenuBar);
        delete iContextMenuBar;
        }

    delete iFieldManager; // context menu bar's stack removal (above) needs field manager to be alive
    delete iEditorStrategy;
    delete iContextPaneIcon;

    // Restore the titlepane text
    if (iTitlePane && iStoredTitlePaneText)
        {
        // iTitlePane takes ownership of iStoredTitlePaneText
        iTitlePane->SetText(iStoredTitlePaneText);
        }

    // Remove empty status pane
    if (iNaviContainer)
        {
        iNaviContainer->Pop();
        }
    // Reset self pointer
    if (iSelfPtr)
        {
        *iSelfPtr = NULL;
        }
    delete iExtension;

    FeatureManager::UnInitializeLib();
    }

EXPORT_C TContactItemId CPbkContactEditorDlg::ExecuteLD()
    {
    TContactItemId ret = KNullContactId;
    iSavedContactPtr = &ret;

    // Set Phonebook icon on context pane
    if (!iContextPaneIcon)
        {
        iContextPaneIcon = CPbkContextPaneIcon::NewL(*iEikonEnv);
        }

    // Set status pane layout to the Phonebook one
    CEikStatusPane* statusPane = iAvkonAppUi->StatusPane();
    TInt statusPaneLayout = KErrNotFound;
    if (statusPane)
        {
        statusPaneLayout = statusPane->CurrentLayoutResId();
        statusPane->SwitchLayoutL(R_AVKON_STATUS_PANE_LAYOUT_USUAL);
        }

    CAknForm::ExecuteLD(R_PBK_EDIT_MEMORY_ENTRY_DIALOG);

    if (statusPane && (statusPaneLayout != KErrNotFound))
        {
        statusPane->SwitchLayoutL(statusPaneLayout);
        }

    return ret;
    }


EXPORT_C void CPbkContactEditorDlg::SetHelpContext
        (TCoeHelpContext aContext)
    {
    iHelpContext = aContext;
    }


/**
 * Saves dialog's fields to contact fields and saves the contact.
 * Command sent from class destructor.
 */
void CPbkContactEditorDlg::CmdSaveL()
    {
    if (iFieldManager)
        {
        // Save editor fields
        iFieldManager->SaveFieldsL();
        if (iFieldManager->FieldsChanged())
            {
            iEditorStrategy->SetStateModified();
            }

        if (iSavedContactPtr && !iFieldManager->AreAllFieldsEmpty())
            {
            *iSavedContactPtr = iEditorStrategy->SaveContactL();
            }
        }
    }

/**
 * Command sent from OkToExitL.
 */
void CPbkContactEditorDlg::CmdDoneL()
    {
    if (iFieldManager)
        {
        // Save editor fields to contact item
        iFieldManager->SaveFieldsL();
        if (iFieldManager->FieldsChanged())
            {
            iEditorStrategy->SetStateModified();
            }

        if (iFieldManager->AreAllFieldsEmpty())
            {
            iEditorStrategy->DeleteContactL();
            }

        *iSavedContactPtr = iEditorStrategy->SaveContactL();
        }
    }

/**
 * Edits the focused memory items caption.
 */
void CPbkContactEditorDlg::CmdEditItemLabelL()
    {
    MPbkContactEditorField* field = iFieldManager->Find(IdOfFocusControl());
    if (field)
        {
        HBufC* textBuf = HBufC::NewLC(CPbkConstants::FormEditorLength());
        TPtr text = textBuf->Des();
        text.Copy(field->FieldLabel().Left(text.MaxLength()));
        HBufC* prompt = StringLoader::LoadLC(R_QTN_FORM_PRMPT_FIELD_LABEL);

        CAknTextQueryDialog* dlg = CAknTextQueryDialog::NewL(text);
        dlg->SetMaxLength(CPbkConstants::FormEditorLength());
        if (dlg->ExecuteLD(R_PBK_EDIT_FIELD_LABEL, *prompt))
            {
            if (text != field->FieldLabel())
                {
                // Assign new label leave-safely to this form and to the contact field
                HBufC* newLabel = text.AllocLC();
                field->SetFieldLabelL(newLabel); // takes ownership of newLabel
                CleanupStack::Pop(newLabel);
                iEditorStrategy->SetStateModified();
                }
            }
        CleanupStack::PopAndDestroy(2);  // prompt, textBuf
        }
    }

/**
 * Add new memory item menu command.
 */
void CPbkContactEditorDlg::CmdAddItemL()
    {
    TPbkAddItemManager manager(iContactItem, *iFieldManager);

    RArray<TInt> addedFieldsControlIds;
    CleanupClosePushL(addedFieldsControlIds);

    // Query and add field(s) to the contact
    manager.AddFieldsL(iEngine.FieldsInfo(), addedFieldsControlIds);

    if (addedFieldsControlIds.Count() > 0)
        {
        // Activate the first added field
        TInt focusedControlId = addedFieldsControlIds[0];
        TryChangeFocusToL(focusedControlId);
        iEditorStrategy->SetStateModified();
        // must be DrawNow and not DrawDeferred
        // otherwise field label and content of
        // following field will be incorrect
        DrawNow();
        }
    CleanupStack::PopAndDestroy(); //addedFieldsControlIds
    }

/**
 * Deletes focused memory entry item.
 */
void CPbkContactEditorDlg::CmdDeleteItemL()
    {
    // Delete item query
    MPbkContactEditorField* field = iFieldManager->Find(IdOfFocusControl());
    if (field)
        {
        TPtrC label = field->FieldLabel();
        HBufC* prompt = StringLoader::LoadLC(R_QTN_PHOB_QUERY_DELETE_ITEM, label);

        CAknQueryDialog* dlg = CAknQueryDialog::NewL();
        CleanupStack::PushL(dlg);
        dlg->SetPromptL(*prompt);
        CleanupStack::Pop(); // dlg
        if(dlg->ExecuteLD(R_PBK_GENERAL_CONFIRMATION_QUERY_PBKVIEW))
            {
            // Notify about voice tag field deletion (unless it's SIND tag)
            if (iContactItem.VoiceTagField() &&
                iContactItem.VoiceTagField()->IsSame(field->ContactItemField()) &&
                !FeatureManager::FeatureSupported(KFeatureIdSind))
                {
                HBufC* note = iCoeEnv->AllocReadResourceLC(R_QTN_PHOB_NOTE_VOICE_TAG_DEL);
                CAknConfirmationNote* dlg = new(ELeave) CAknConfirmationNote(ETrue);
                dlg->ExecuteLD(*note);
                CleanupStack::PopAndDestroy(); // note
                }

            // Notify about speed dial field deletion
            const TInt fieldIndex = iFieldManager->FindContactItemFieldIndex(
                    field->ContactItemField());
            if (iEngine.IsSpeedDialAssigned(iContactItem, fieldIndex))
                {
                HBufC* note = iCoeEnv->AllocReadResourceLC(R_QTN_PHOB_NOTE_SPEED_DIAL_DEL);
                CAknConfirmationNote* dlg = new(ELeave) CAknConfirmationNote(ETrue);
                dlg->ExecuteLD(*note);
                CleanupStack::PopAndDestroy(); // note
                }

            // Delete the field
            iFieldManager->RemoveField(*field);
            iEditorStrategy->SetStateModified();

            // Redraw using DrawNow, DrawDeferred does not get the job done
            DrawNow();
            }

        CleanupStack::PopAndDestroy(); // prompt
        }
    }

/**
 * Fetches thumbnail image from photoalbum and attaches it to this
 * contact.
 */
void CPbkContactEditorDlg::CmdFetchThumbnailL()
    {
    //PreCond:
    __ASSERT_DEBUG(iThumbnailCmd, Panic(EPanicPreCond_CmdFetchThumbnailL));

    if (iThumbnailCmd->FetchThumbnailL(iContactItem))
        {
        iEditorStrategy->SetStateModified();
        }
    }

/**
 * Removes a thumbnail image attachment from this contact.
 */
void CPbkContactEditorDlg::CmdRemoveThumbnailL()
    {
    //PreCond:
    __ASSERT_DEBUG(iThumbnailCmd, Panic(EPanicPreCond_CmdRemoveThumbnailL));

    if (iThumbnailCmd->RemoveThumbnailL(iContactItem))
        {
        iEditorStrategy->SetStateModified();
        }
    }

/**
 * Updates the application title pane.
 */
void CPbkContactEditorDlg::UpdateTitlePaneL()
    {
    if (iTitlePane)
        {
        CPbkFieldDataArray* titleFields = CPbkFieldDataArray::NewL();
        titleFields->ResetAndDestroyPushL();

        const TInt fieldCount = iFieldManager->EditorCount();
        for (TInt i = 0; i < fieldCount; ++i)
            {
            const MPbkContactEditorField& field = iFieldManager->FieldAt(i);
            const TPbkFieldId fieldId = field.FieldId();
            if (iEngine.IsTitleField(fieldId))
                {
                HBufC* textBuf = field.ControlTextL();
                TPtrC text;
                if (textBuf)
                    {
                    CleanupStack::PushL(textBuf);
                    text.Set(*textBuf);
                    }

                CPbkFieldData* fieldData = CPbkFieldData::NewL(fieldId,text);
                if (textBuf)
                    {
                    CleanupStack::PopAndDestroy(textBuf);
                    }
                CleanupStack::PushL(fieldData);
                titleFields->AppendL(fieldData);
                CleanupStack::Pop(fieldData);
                }
            }

        HBufC* newTitle = iEngine.GetContactTitleOrNullL(*titleFields);
        CleanupStack::PopAndDestroy();  // titleFields

        if (newTitle)
            {
            // Update title only if it doesn't contain ZWS or ZWNJ, which
            // are a sign of incomplete typing in e.g. Hindi
            const TChar KZeroWidthSpace(0x200b);
            const TChar KZeroWidthNonJoiner(0x200c);
            if ((newTitle->Locate(KZeroWidthSpace) == KErrNotFound) &&
                (newTitle->Locate(KZeroWidthNonJoiner) == KErrNotFound))
                {
                iTitlePane->SetText(newTitle);
                }
            }
        else
            {
            iTitlePane->SetTextL(iEditorStrategy->DefaultTitle());
            }
        }
    }

TKeyResponse CPbkContactEditorDlg::OfferKeyEventL
        (const TKeyEvent& aKeyEvent,
        TEventCode aType)
    {
    // display menu bar if the focused control doesnt consume selection keys
    MPbkContactEditorField* field = iFieldManager->Find(IdOfFocusControl());
    if (field && field->ConsumesKeyEvent(aKeyEvent, aType))
        {
        return CAknForm::OfferKeyEventL(aKeyEvent,aType);
        }

    if (aType == EEventKey && aKeyEvent.iCode == EKeyOK)
        {
        iContextMenuBar->TryDisplayMenuBarL();
        return EKeyWasConsumed;
        }

    if (aType == EEventKey && aKeyEvent.iCode == EKeyEscape)
        {
        // See CloseAllMenusAndPopups()
        iStateFlags.Set(EEscKeyReceived);
        }

    if (!iStateFlags.IsSet(EUnderDestruction))
        {
        TKeyResponse ret(EKeyWasNotConsumed);        
        ret = CAknForm::OfferKeyEventL( aKeyEvent, aType );
        if ( !iStateFlags.IsSet(EEscKeyReceived) && 
            ret == EKeyWasConsumed )
            {
            UpdateCbasL( iFieldManager->Find(IdOfFocusControl()), 
                         ButtonGroupContainer() );
            }   
        return ret;
        }
    else
        {
        return EKeyWasConsumed;
        }
    }

void CPbkContactEditorDlg::GetHelpContext
        (TCoeHelpContext& aContext) const
    {
    if (iHelpContext.iMajor.iUid)
        {
        // If external help context has been set, return it..
        aContext = iHelpContext;
        }
    else
        {
        // ..otherwise go with the default context
        aContext.iMajor.iUid = KPbkUID3;
        iEditorStrategy->GetHelpContext(aContext);
        }
    }

void CPbkContactEditorDlg::PreLayoutDynInitL()
    {
    __ASSERT_DEBUG(iFieldManager, Panic(EPanicPreCond_PreLayoutDynInitL));

    SetEditableL(ETrue);

    // create controls for contact items fields
    iFieldManager->CreateFieldsFromContactL();

    __ASSERT_DEBUG(iFieldManager, Panic(EPanicPostCond_PreLayoutDynInitL));
    }

void CPbkContactEditorDlg::PostLayoutDynInitL()
    {
    UpdateTitlePaneL();
    
    if ( iAvkonAppUi->StatusPane()->
        PaneCapabilities(TUid::Uid(EEikStatusPaneUidContext)).IsInCurrentLayout() )
        {
        iThumbnailHandler->Load(iContactItem, NULL );
        }

    // Set state to initialised (enables save from destructor)
    iEditorStrategy->SetStateInitialized();
    }

void CPbkContactEditorDlg::SetInitialCurrentLine()
    {
    //PreCond:
    __ASSERT_DEBUG(iFieldManager,
            Panic(EPanicPreCond_SetInitialCurrentLine));

    CAknForm::SetInitialCurrentLine();
    const TInt fieldCount = iFieldManager->ContactItemFieldCount();
    if (fieldCount > 0)
        {
        if (iFocusIndex < 0 || iFocusIndex >= fieldCount)
            {
            // In any case focus the first line
            iFocusIndex = 0;
            }
        iFieldManager->SetFocusToIndex(iFocusIndex);
        }
    }

void CPbkContactEditorDlg::ProcessCommandL
        (TInt aCommandId)
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING("CPbkContactEditorDlg::ProcessCommandL start"));

    HideMenu();
    iContextMenuBar->StopDisplayingMenuBar();

    if ( iExtension->ProcessCommandL(aCommandId) )
        {
        return; // command processed
        }

    switch (aCommandId)
        {
        case EPbkCmdAddItem:
            {
            CmdAddItemL();
            break;
            }

        case EPbkCmdDeleteItem:
            {
            CmdDeleteItemL();
            break;
            }

        case EPbkCmdEditItemLabel:
            {
            CmdEditItemLabelL();
            break;
            }

        case EPbkCmdFetchThumbnail:
            {
            CmdFetchThumbnailL();
            break;
            }

        case EPbkCmdRemoveThumbnail:
            {
            CmdRemoveThumbnailL();
            break;
            }
        
        case EAknCmdHelp:
            {
            HlpLauncher::LaunchHelpApplicationL(iEikonEnv->WsSession(),
                iEikonEnv->EikAppUi()->AppHelpContextL());
            break;
            }

        case EEikCmdExit:   // FALLTHROUGH
        case EAknCmdExit:   // Exit calling application
            {
            PBK_DEBUG_PRINT(PBK_DEBUG_STRING("CPbkContactEditorDlg::ProcessCommandL exit"));

            // If exit callback returned EFalse, the exit is cancelled
            if (iExitCallback && !iExitCallback->OkToExitL(aCommandId))
                {
                break;
                }
            const TInt exitCommandId = iExitCommandId;
            CEikAppUi* appUi = iEikonEnv->EikAppUi();
            // Close this dialog first
            delete this;
            if (exitCommandId)
                {
                // Exit application
                static_cast<MEikCommandObserver*>(appUi)->ProcessCommandL(exitCommandId);
                }
            else
                {
                // Exit application
                static_cast<MEikCommandObserver*>(appUi)->ProcessCommandL(aCommandId);
                }
            break;
            }

        default:
            {
            // Command not handled here, forward it to the app ui.
            iEikonEnv->EikAppUi()->HandleCommandL(aCommandId);
            break;
            }
        }

    PBK_DEBUG_PRINT(PBK_DEBUG_STRING("CPbkContactEditorDlg::ProcessCommandL end"));
    }

void CPbkContactEditorDlg::DynInitMenuPaneL
        (TInt aResourceId,
        CEikMenuPane* aMenuPane)
    {
    iExtension->DynInitMenuPaneL(aResourceId, aMenuPane);

    iEditorStrategy->DynInitMenuPaneL(aResourceId, aMenuPane);

    // Options menu
    if (aResourceId == R_CONTACTEDITOR_MENUPANE)
        {
        // don't show 'Exit' command if it is disabled
        if (iStateFlags.IsSet(EHideExit))
            {
            aMenuPane->SetItemDimmed(EAknCmdExit,ETrue);
            }

        iThumbnailCmd->DynInitMenuPaneL(aResourceId, *aMenuPane, iContactItem);
        }

    // Common items in Options and context menus
    if (aResourceId == R_CONTACTEDITOR_MENUPANE ||
        aResourceId == R_PBK_CONTACTEDITOR_CONTEXT_MENUPANE)
        {
        // filter delete item away from menu for first and last name fields
        // and from the syncronization field
        MPbkContactEditorField* currentField = iFieldManager->Find(IdOfFocusControl());
        if (currentField)
            {
            const TPbkFieldId fieldId = currentField->FieldId();
            if (fieldId == EPbkFieldIdLastName || fieldId == EPbkFieldIdFirstName ||
                fieldId == EPbkFieldIdSyncronization)
                {
                aMenuPane->SetItemDimmed(EPbkCmdDeleteItem, ETrue);
                }
            }
        }
    }

TBool CPbkContactEditorDlg::OkToExitL(TInt aKeyCode)
    {
    PBK_DEBUG_PRINT(PBK_DEBUG_STRING
        ("CPbkContactEditorDlg::OkToExitL(0x%x,%d)"), this, aKeyCode);

    // editor can't be closed if this leaves
    TRAPD(err, iExtension->PreOkToExitL(aKeyCode));

    TBool ret = EFalse;
    switch (aKeyCode)
        {
        case EAknSoftkeyOptions:
            {
            DisplayMenuL();
            break;
            }
        case EAknSoftkeyDone:
            {
            if (!iThumbnailCmd->OkToExit())
                {
                // ThumbnailCmd state is not ready for exit
                break;
                }
            if(iEngine.Constants()->
               LocallyVariatedFeatureEnabled(EPbkLVCheckDuplicateEntry))
                {
                ret = CheckForDuplicateEntryL();
                }
            else
                {
                if (err != KErrNone)
                    {
                    // cannot affect on editor closing, but let's show
                    // that something was wrong
                    iCoeEnv->HandleError(err);
                    }
                TRAP(err,CmdDoneL());
                ret = ETrue;
                }
            break;
            }
        case EAknSoftkeyChange: // Fallthrough
        case EAknSoftkeyContextOptions:
            {
            // Launch context menu if the control is not popup control
            CEikCaptionedControl* line=CurrentLine();
            TInt type=line->iControlType;
            TBool isPopUp = ( type == EAknCtPopupFieldText ) ||
                ( type == EAknCtPopupField );
            if (isPopUp)
                {
                CAknPopupField* ctrl =
                    static_cast<CAknPopupField*>( line->iControl );
                ctrl->ActivateSelectionListL();
                }
            else
                {
                iContextMenuBar->SetMenuTitleResourceId( R_PBK_CONTACTEDITOR_CONTEXT_MENUBAR );
                iContextMenuBar->SetMenuType( CEikMenuBar::EMenuContext );
                iContextMenuBar->StopDisplayingMenuBar();
                iContextMenuBar->TryDisplayMenuBarL();
                iContextMenuBar->SetMenuTitleResourceId( R_PBK_CONTACTEDITOR_MENUBAR );
                iContextMenuBar->SetMenuType( CEikMenuBar::EMenuOptions );
                }
            break;
            }
        default:
            break;
        }

    if (err == KErrNone)
        {
        TRAP(err, iExtension->PostOkToExitL(aKeyCode));
        }
    if (err != KErrNone)
        {
        // cannot affect on editor closing, but let's show
        // that something was wrong
        iCoeEnv->HandleError(err);
        }
    return ret;
    }

void CPbkContactEditorDlg::HandleControlStateChangeL
        (TInt aControlId)
    {
    MPbkContactEditorField* changedField = iFieldManager->Find(aControlId);
    if (changedField)
        {
        if ( iEngine.IsTitleField(changedField->FieldId()) )
            {
            UpdateTitlePaneL();
            }
        }
    }

/**
 * Closes popups opened from this dialog by sending them Escape key events.
 * @see CPbkContactEditorDlg::OfferKeyEventL
 */
void CPbkContactEditorDlg::CloseAllPopups()
    {
    const TInt KMaxAttempts = 50;
    iStateFlags.Clear(EEscKeyReceived);
    // Send Escape key until this dialog itself receives it
    for (TInt attempt=0;
        attempt < KMaxAttempts && !iStateFlags.IsSet(EEscKeyReceived);
        ++attempt)
        {
        TRAP_IGNORE(iEikonEnv->SimulateKeyEventL(KEscKeyEvent,EEventKey));
        }
    }


/*  This function will check to see if the user entered information, coming out of the
  *  TPbkContactItemFields, matches (exactly) any entries in the contact database, based
  *  on the results of the GetContactTitle function. This function will only return what
  *  is being displayed to the user. For example, a first name of "Eric", no last name, and
  *  a company name of "Nokia" will only display "Eric" and would match an entry with
  *  just the first name of "Eric".
  *
  *  The search has already been performed and ANY entry that matches any of the fields
  *  the user has entered (first name, last name, and/or company name) is passed into
  *  this function in the aIdArray parameter.
  *
  *  We are checking for entries that are separate from the current entry the user is
  *  creating / editing. Therefore, if the user is editing an existing entry, we don't want
  *  to signal a match when we find that specific entry. We can check this based on the stored
  *  variable iContactItem and it's Id() function.
  */
TBool CPbkContactEditorDlg::DuplicateNameMatchL
        (CContactIdArray* aIdArray)
    {
    TBool exactMatch = EFalse;
    // just check the titles of the current item with the titles of each of the
    // potentially matched items

    HBufC* title = iContactItem.GetContactTitleL();
    CleanupStack::PushL(title);

    // iterate through each contact in aIdArray
    for (TInt count = 0; count < aIdArray->Count(); count++)
        {
        // only continue with this check if we aren't checking our own entry. check with ID
        if (iContactItem.Id() != (*aIdArray)[count])
            {
            // read the current contact
            CPbkContactItem* contact = iEngine.ReadContactLC((*aIdArray)[count]);

            // get the title of this current contact to compare
            HBufC* contactTitle = contact->GetContactTitleL();
            CleanupStack::PushL(contactTitle);

            // now just compare the titles of this loop contact and the user-entered contact
            if (!title->CompareF(*contactTitle))
                {
                exactMatch = ETrue;
                CleanupStack::PopAndDestroy(2);     // name, contact
                break;
                }
            CleanupStack::PopAndDestroy(2);         // name, contact
            }
        }

    CleanupStack::PopAndDestroy(title);
    return exactMatch;
    }

 /*
  *  This function pulls user-entered info from the dialog for firstname, lastname and companyname.
  *  We search the contact database on the first of these that is non-empty and not all spaces. If
  *  we come up with any matches, we pass this info to the function DuplicateNameMatch, which checks
  *  that all three fields match the found entries from the database search. If this is the case,
  *  we pass back an ETrue value, EFalse otherwise.
  */
TBool CPbkContactEditorDlg::ContactAlreadyExistsL()
    {
    TBool found = EFalse;
    CContactIdArray* idArray = NULL;

    CPbkFieldIdArray* findFrom = new(ELeave) CPbkFieldIdArray;
    CleanupStack::PushL(findFrom);

    // add all fields, firstname, lastname and companyname to our contact database find parameter
    findFrom->AppendL(EPbkFieldIdFirstName);
    findFrom->AppendL(EPbkFieldIdLastName);
    findFrom->AppendL(EPbkFieldIdCompanyName);

    // get local copies of user-filled-in contact fields' text values
    TPbkContactItemField* firstName = iContactItem.FindField(EPbkFieldIdFirstName);
    TPbkContactItemField* lastName = iContactItem.FindField(EPbkFieldIdLastName);
    TPbkContactItemField* companyName = iContactItem.FindField(EPbkFieldIdCompanyName);

    // this is terribly slow
    if (firstName && !firstName->IsEmptyOrAllSpaces())
        {
        idArray = iEngine.FindLC(firstName->Text(), findFrom);
        }
    else if (lastName && !lastName->IsEmptyOrAllSpaces())
        {
        idArray = iEngine.FindLC(lastName->Text(), findFrom);
        }
    else if (companyName && !companyName->IsEmptyOrAllSpaces())
        {
        idArray = iEngine.FindLC(companyName->Text(), findFrom);
        }
    else
        {
        // fname, lname and cname all empty - check for other "Unnamed" entries
        // Two unnamed contacts are considered duplicates
        idArray = iEngine.FindLC(KNullDesC, findFrom);
        }

    // if we have at least one match, check that all fields match
    if (idArray)
        {
        if (idArray->Count() > 0 && DuplicateNameMatchL(idArray))
            {
            found = ETrue;
            }
        CleanupStack::PopAndDestroy(idArray);
        }

    CleanupStack::PopAndDestroy(findFrom);
    return found;
    }


TBool CPbkContactEditorDlg::CheckForDuplicateEntryL()
    {
    TBool retVal = ETrue;
    TBool exitNormally = ETrue;

    // Check for duplicate entry and give the user the option to
    // save duplicate or return to enter more information.

    // To perform that check, we must save the info the user has
    // just entered (just locally, not in the contact database)
    if (iFieldManager)
        {
        iFieldManager->SaveFieldsL();
        if ( iFieldManager->FieldsChanged() )
            {
            iEditorStrategy->SetStateModified();
            }

        // Totally empty contacts are not checked for duplicates
        if ( !iFieldManager->AreAllFieldsEmpty() )
            {
            // If contact already exists, inform the user and given them
            // the option to add the duplicate, or go back to the screen
            // and enter information
            if ( ContactAlreadyExistsL() )
                {
                // Get contact title to display
                HBufC* name = iContactItem.GetContactTitleL();
                CleanupStack::PushL( name );

                HBufC* prompt = StringLoader::LoadL(R_QTN_PHOB_ADD_DUPLICATE,
                    *name );
                CleanupStack::PushL( prompt );

                CAknQueryDialog* dlg = CAknQueryDialog::NewL();
                CleanupStack::PushL( dlg );
                dlg->SetPromptL( *prompt );
                CleanupStack::Pop(); // dlg
                // If user hits no, we'll put them back to the edit screen
                if ( !dlg->ExecuteLD(R_PBK_GENERAL_CONFIRMATION_QUERY_PBKVIEW) )
                    {
                    exitNormally = EFalse;
                    retVal = EFalse;
                    }
                CleanupStack::PopAndDestroy( 2 ); // prompt, name
                }
            }
        }

    if (exitNormally)
        {
        CmdDoneL();                     // saving occurs here
        retVal = ETrue;
        }

    return retVal;
    }

EXPORT_C void CPbkContactEditorDlg::SetExitCallback
    (MPbkEditorOkToExitCallback* aCallback)
    {
    iExitCallback = aCallback;
    }

void CPbkContactEditorDlg::ReloadThumbnailL()
    {
    if ( iAvkonAppUi->StatusPane()->
        PaneCapabilities(TUid::Uid(EEikStatusPaneUidContext)).IsInCurrentLayout() )
        {
        if (IsFocused())
            {
            if (!iThumbnailHandler)
                {
                iThumbnailHandler = CPbkThumbnailPopup::NewL(iEngine);
                }
            iThumbnailHandler->Load(iContactItem, NULL );
            }
        else
            {
            if (iThumbnailHandler)
                {
                iThumbnailHandler->CancelLoading();
                }
            }   
        }
    }

// End of File