
/*
 *@@sourcefile xcenter.c:
 *      This file contains SOM code for the following XWorkplace classes:
 *
 *      --  XCenter: a WarpCenter replacement.
 *
 *      This class interface does not contain a whole lot of
 *      code but is rather responsible for making the XCenter
 *      behave like a decent abstract object. The key entry
 *      point is XCenter::wpOpen, which goes into the mess
 *      of code in src\xcenter, which has all the important
 *      stuff.
 *
 *      See src\shared\center.c for an introduction.
 *
 *      Installation of this class is completely optional.
 *
 *@@added V0.9.4 (2000-06-11) [umoeller]
 *@@somclass XCenter xctr_
 *@@somclass M_XCenter xctrM_
 */

/*
 *      Copyright (C) 2000-2007 Ulrich M”ller.
 *      This file is part of the XWorkplace source package.
 *      XWorkplace is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the XWorkplace main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

/*
 *  This file was generated by the SOM Compiler and Emitter Framework.
 *  Generated using:
 *      SOM Emitter emitctm: 2.41
 */

#ifndef SOM_Module_xcenter_Source
#define SOM_Module_xcenter_Source
#endif
#define XCenter_Class_Source
#define M_XCenter_Class_Source

#pragma strings(readonly)

/*
 *  Suggested #include order:
 *  1)  os2.h
 *  2)  C library headers
 *  3)  setup.h (code generation and debugging options)
 *  4)  headers in helpers\
 *  5)  at least one SOM implementation header (*.ih)
 *  6)  dlgids.h, headers in shared\ (as needed)
 *  7)  headers in implementation dirs (e.g. filesys\, as needed)
 *  8)  #pragma hdrstop and then more SOM headers which crash with precompiled headers
 */

#define INCL_DOSPROCESS
#define INCL_DOSEXCEPTIONS
#define INCL_DOSSEMAPHORES
#define INCL_DOSERRORS

#define INCL_WINFRAMEMGR
#define INCL_WINWINDOWMGR
#define INCL_WINMESSAGEMGR
#define INCL_WINMENUS           // for menu helpers
#define INCL_WINDIALOGS
#define INCL_WINBUTTONS         // for button/check box helpers
#define INCL_WINSHELLDATA
#include <os2.h>

// C library headers
#include <stdio.h>              // needed for except.h
#include <setjmp.h>

// generic headers
#include "setup.h"                      // code generation and debugging options

// headers in /helpers
#include "helpers\dosh.h"               // Control Program helper routines
#include "helpers\except.h"             // exception handling
#include "helpers\linklist.h"           // linked list helper routines
#include "helpers\winh.h"               // PM helper routines

// SOM headers which don't crash with prec. header files
#include "xcenter.ih"

// XWorkplace implementation headers
#include "dlgids.h"                     // all the IDs that are shared with NLS
#include "shared\common.h"              // the majestic XWorkplace include file
#include "shared\errors.h"              // private XWorkplace error codes
#include "shared\helppanels.h"          // all XWorkplace help panel IDs
#include "shared\kernel.h"              // XWorkplace Kernel
#include "shared\notebook.h"            // generic XWorkplace notebook handling

#include "shared\center.h"              // public XCenter interfaces

#include "xcenter\centerp.h"            // private XCenter implementation

// other SOM headers

#pragma hdrstop

/* ******************************************************************
 *
 *   Declarations
 *
 ********************************************************************/

/*
 *@@ WPSAVELONGITEM:
 *      key/PULONG pair to be saved in a loop in
 *      wpSaveState.
 *
 *@@added V0.9.7 (2001-01-18) [umoeller]
 */

typedef struct _WPSAVELONGITEM
{
    ULONG   ulSaveKey;
    PULONG  pul;
} WPSAVELONGITEM, *PWPSAVELONGITEM;

/* ******************************************************************
 *
 *   Global variables
 *
 ********************************************************************/

/* ******************************************************************
 *
 *   XCenter instance methods
 *
 ********************************************************************/

/*
 *@@ xwpAddXCenterPages:
 *
 *@@changed V0.9.9 (2001-03-09) [umoeller]: made second "view" page major "style" page
 */

SOM_Scope ULONG  SOMLINK xctr_xwpAddXCenterPages(XCenter *somSelf,
                                                 HWND hwndNotebook)
{
    INSERTNOTEBOOKPAGE inbp;

    /* XCenterData *somThis = XCenterGetData(somSelf); */
    XCenterMethodDebug("XCenter","xctr_xwpAddXCenterPages");

    // add the "Widgets" page
    memset(&inbp, 0, sizeof(INSERTNOTEBOOKPAGE));
    inbp.somSelf = somSelf;
    inbp.hwndNotebook = hwndNotebook;
    inbp.hmod = cmnQueryNLSModuleHandle(FALSE);
    inbp.usPageStyleFlags = BKA_MAJOR;
    inbp.pcszName = cmnGetString(ID_XSSI_WIDGETSPAGE);  // pszWidgetsPage
    inbp.ulDlgID = ID_XFD_CONTAINERPAGE;
    inbp.ulDefaultHelpPanel  = ID_XSH_XCENTER_WIDGETS;
    inbp.ulPageID = SP_XCENTER_WIDGETS;
    inbp.pampControlFlags = G_pampGenericCnrPage;
    inbp.cControlFlags = G_cGenericCnrPage;
    inbp.pfncbInitPage    = ctrpWidgetsInitPage;
    inbp.pfncbItemChanged = ctrpWidgetsItemChanged;
    ntbInsertPage(&inbp);

    // add the "Classes" page
    memset(&inbp, 0, sizeof(INSERTNOTEBOOKPAGE));
    inbp.somSelf = somSelf;
    inbp.hwndNotebook = hwndNotebook;
    inbp.hmod = cmnQueryNLSModuleHandle(FALSE);
    inbp.usPageStyleFlags = BKA_MAJOR;
    inbp.pcszName = cmnGetString(ID_XSSI_CLASSESPAGE);  // pszClassesPage
    inbp.ulDlgID = ID_XFD_CONTAINERPAGE;
    inbp.ulDefaultHelpPanel  = ID_XSH_XCENTER_CLASSES;
    inbp.ulPageID = SP_XCENTER_CLASSES;
    inbp.pampControlFlags = G_pampGenericCnrPage;
    inbp.cControlFlags = G_cGenericCnrPage;
    inbp.pfncbInitPage    = ctrpClassesInitPage;
    inbp.pfncbItemChanged = ctrpClassesItemChanged;
    ntbInsertPage(&inbp);

    // add the "Style" page on top
    memset(&inbp, 0, sizeof(INSERTNOTEBOOKPAGE));
    inbp.somSelf = somSelf;
    inbp.hwndNotebook = hwndNotebook;
    inbp.hmod = cmnQueryNLSModuleHandle(FALSE);
    inbp.usPageStyleFlags = BKA_MAJOR;
    inbp.fEnumerate = TRUE;
    inbp.pcszName = cmnGetString(ID_XSSI_STYLEPAGE);  // pszStylePage
    inbp.ulDlgID = ID_XFD_EMPTYDLG;           // V0.9.16 (2001-09-29) [umoeller] ID_CRD_SETTINGS_VIEW2;
    inbp.ulDefaultHelpPanel  = ID_XSH_XCENTER_VIEW2;
    inbp.ulPageID = SP_XCENTER_VIEW2;
    inbp.pfncbInitPage    = ctrpView2InitPage;
    inbp.pfncbItemChanged = ctrpView2ItemChanged;
    ntbInsertPage(&inbp);

    // add the "View" page on top
    memset(&inbp, 0, sizeof(INSERTNOTEBOOKPAGE));
    inbp.somSelf = somSelf;
    inbp.hwndNotebook = hwndNotebook;
    inbp.hmod = cmnQueryNLSModuleHandle(FALSE);
    inbp.usPageStyleFlags = BKA_MAJOR;
    inbp.fEnumerate = TRUE;
    inbp.pcszName = cmnGetString(ID_XSSI_VIEWPAGE);  // pszViewPage
    inbp.ulDlgID = ID_XFD_EMPTYDLG; // ID_CRD_SETTINGS_VIEW; V0.9.19 (2002-05-07) [umoeller]
    inbp.ulDefaultHelpPanel  = ID_XSH_XCENTER_VIEW1;
    inbp.ulPageID = SP_XCENTER_VIEW1;
    inbp.pfncbInitPage    = ctrpView1InitPage;
    inbp.pfncbItemChanged = ctrpView1ItemChanged;
    return ntbInsertPage(&inbp);
}

/*
 *@@ xwpQueryWidgets:
 *      returns an array of XCENTERWIDGETSETTING structures
 *      describing the widgets in this XCenter instance.
 *
 *      *pulCount receives the array item count (_not_
 *      the array size). Use XCenter::xwpFreeWidgetsBuf
 *      to free the memory allocated by this method.
 *
 *      If no widgets are present, NULL is returned, and
 *      *pulCount is set to 0.
 *
 *@@added V0.9.7 (2000-12-08) [umoeller]
 */

SOM_Scope PVOID  SOMLINK xctr_xwpQueryWidgets(XCenter *somSelf,
                                              PULONG pulCount)
{
    PXCENTERWIDGETSETTING paSettings = NULL;
    BOOL    fLocked = FALSE;

    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_xwpQueryWidgets");

    TRY_LOUD(excpt1)
    {
        if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
        {
            PLINKLIST pllSettings = ctrpQuerySettingsList(somSelf);
            PLISTNODE pNode = lstQueryFirstNode(pllSettings);
            ULONG cSettings = lstCountItems(pllSettings);
            if (paSettings = malloc(sizeof(XCENTERWIDGETSETTING) * cSettings))
            {
                ULONG ul = 0;
                for (;
                     ul < cSettings;
                     ul++)
                {
                    PPRIVATEWIDGETSETTING pSource = (PPRIVATEWIDGETSETTING)pNode->pItemData;
                    paSettings[ul].pszWidgetClass = strdup(pSource->Public.pszWidgetClass);
                    if (pSource->Public.pszSetupString)
                        paSettings[ul].pszSetupString = strdup(pSource->Public.pszSetupString);
                    else
                        paSettings[ul].pszSetupString = NULL;

                    pNode = pNode->pNext;
                }
            }
        }
    }
    CATCH(excpt1)
    {
        if (paSettings)
        {
            free(paSettings);
            paSettings = NULL;
        }
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    return paSettings;
}

/*
 *@@ xwpFreeWidgetsBuf:
 *      frees the widget buffer allocated by
 *      XCenter::xwpQueryWidgets. pBuf must
 *      be the value returned from there
 *      and ulCount must be the value of
 *      *pulCount from there.
 *
 *@@added V0.9.7 (2000-12-08) [umoeller]
 */

SOM_Scope void  SOMLINK xctr_xwpFreeWidgetsBuf(XCenter *somSelf,
                                               PVOID pBuf,
                                               ULONG ulCount)
{
    // no semaphore needed here, we created a copy above
    ULONG ul = 0;
    PXCENTERWIDGETSETTING paSettings = (PXCENTERWIDGETSETTING)pBuf;

    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_xwpFreeWidgetsBuf");

    for (;
         ul < ulCount;
         ul++)
    {
        PXCENTERWIDGETSETTING pThis = &paSettings[ul];
        if (pThis->pszWidgetClass)
            free(pThis->pszWidgetClass);
        if (pThis->pszSetupString)
            free(pThis->pszSetupString);
    }

    free(paSettings);
}

/*
 *@@ xwpCreateWidget:
 *      creates a new widget into an XCenter at the
 *      specified index.
 *
 *      This operates in two modes:
 *
 *      --  If ulTrayWidgetIndex and ulTrayIndex are both
 *          -1, this creates a root widget before
 *          ulWidgetIndex. If ulWidgetIndex is -1 also,
 *          the root widget is created at the end of the
 *          list, i.e. at the very right of the XCenter bar.
 *
 *      --  If ulTrayWidget and ulTrayIndex are not -1, they
 *          specify the index of an existing tray widget and
 *          of an existing tray in that widget. This then
 *          creates a new widget before ulWidgetIndex, which
 *          is considered a widget index inside the tray.
 *          If ulWidgetIndex is -1, again, this creates a new
 *          widget at the very right of the tray.
 *
 *      If the XCenter is currently open, its view is
 *      refreshed if necessary.
 *
 *      Returns:
 *
 *      --  NO_ERROR
 *
 *      --  ERROR_INVALID_PARAMETER
 *
 *      --  ERROR_NOT_ENOUGH_MEMORY
 *
 *      --  ERROR_PROTECTION_VIOLATION
 *
 *      --  XCERR_INVALID_ROOT_WIDGET_INDEX: ulWidgetIndex
 *          does not exist.
 *
 *      --  XCERR_ROOT_WIDGET_INDEX_IS_NO_TRAY: in tray
 *          mode, ulWidgetIndex is not a tray widget.
 *
 *      --  XCERR_INVALID_TRAY_INDEX: ulTrayIndex is
 *          invalid.
 *
 *      --  XCERR_INVALID_SUBWIDGET_INDEX: ulTrayWidgetIndex
 *          is invalid.
 *
 *      This method replaces xwpInsertWidget (V0.9.19) and
 *      can now correctly handle all cases with trays and
 *      subwidgets. As a result, the ugly hack messages to
 *      the tray widget are no longer needed.
 *
 *@@added V0.9.19 (2002-04-25) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xctr_xwpCreateWidget(XCenter *somSelf,
                                              PSZ pszWidgetClass,
                                              PSZ pszSetupString,
                                              XCenter_PWIDGETPOSITION pPosition)
{
    APIRET      arc = NO_ERROR;
    BOOL        fLocked = FALSE;

    XCenterMethodDebug("XCenter","xctr_xwpInsertWidget");

    if (!pPosition || !pszWidgetClass)
        return ERROR_INVALID_PARAMETER;

    TRY_LOUD(excpt1)
    {
        if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
        {
            XCenterData *somThis = XCenterGetData(somSelf);
            PPRIVATEWIDGETSETTING   pNewSetting;
            ULONG                   ulNewItemCount = 0,
                                    ulNewWidgetIndex = 0;
            PTRAYSETTING            pTraySetting = NULL;
            PPRIVATEWIDGETSETTING   pTrayWidgetSetting = NULL;
            PXCENTERWIDGET          pTrayWidgetView = NULL;

            ctrpLoadClasses();

            if (pPosition->ulTrayWidgetIndex != -1)
            {
                // create subwidget in tray:
                // find the specified tray then
                arc = ctrpFindTraySetting(somSelf,
                                          pPosition->ulTrayWidgetIndex,
                                          pPosition->ulTrayIndex,
                                          &pTrayWidgetSetting,
                                                // receives tray widget setting always
                                          &pTraySetting,
                                                // receives tray setting always
                                          &pTrayWidgetView);
                                                // receives tray widget view only
                                                // if specified tray is currently
                                                // switched to, so then we need
                                                // to refresh the tray widget below
            }

            if (    (!arc)
                 && (!(arc = ctrpCreateWidgetSetting(somSelf,
                                                     pTraySetting,   // can be NULL for root
                                                     pszWidgetClass,
                                                     pszSetupString,
                                                     pPosition->ulWidgetIndex,
                                                     &pNewSetting,
                                                     &ulNewItemCount,
                                                     &ulNewWidgetIndex)))
               )
            {
                // added successfully:

                // do not hold the mutex while sending
                _wpReleaseObjectMutexSem(somSelf);
                fLocked = FALSE;

                // if this is a tray widget, add a default empty tray
                // V0.9.19 (2002-04-25) [umoeller]
                if (!strcmp(pszWidgetClass, TRAY_WIDGET_CLASS_NAME))
                {
                    PTRAYSETTING pTray;
                    CHAR sz[200];
                    pNewSetting->pllTraySettings = lstCreate(FALSE);
                    // create a default tray
                    sprintf(sz,
                            cmnGetString(ID_CRSI_TRAY),     // tray %d
                            1);
                    pTray = ctrpCreateTraySetting(pNewSetting,
                                                  sz,
                                                  NULL);
                }

                if (_pvOpenView)
                {
                    // XCenter view currently open:
                    PXCENTERWINDATA pXCenterData = (PXCENTERWINDATA)_pvOpenView;
                    PXCENTERGLOBALS pGlobals = &pXCenterData->Globals;
                    HWND            hwndRefresh = NULLHANDLE;

                    // we must send msg instead of calling ctrpCreateWidgetWindow
                    // directly, because that func may only run on the
                    // XCenter GUI thread

                    if (!pTraySetting)
                    {
                        // new root widget:
                        hwndRefresh = pGlobals->hwndClient;
                    }
                    else
                    {
                        // new subwidget in tray:
                        // refresh the tray widget if the tray
                        // that we created the widget in is currently
                        // switched to (ptr set above by ctrpFindTraySetting)
                        if (pTrayWidgetView)
                            hwndRefresh = pTrayWidgetView->hwndWidget;
                    }

                    if (hwndRefresh)
                    {
                        // either client or tray widget needs refresh:
                        WinSendMsg(hwndRefresh,
                                   XCM_CREATEWIDGETWINDOW,
                                   (MPARAM)pNewSetting,
                                   (MPARAM)ulNewWidgetIndex);
                                // new widget is invisible, and at a random position
                        WinPostMsg(hwndRefresh,
                                   XCM_REFORMAT,
                                   (MPARAM)(XFMF_RECALCHEIGHT
                                            | XFMF_REPOSITIONWIDGETS
                                            | XFMF_SHOWWIDGETS),
                                   0);
                    }
                }

                // save instance data (with that linked list)
                _wpSaveDeferred(somSelf);

                // update the "widgets" notebook page, if open
                ntbUpdateVisiblePage(somSelf, SP_XCENTER_WIDGETS);
            }

            ctrpFreeClasses();
        }
    }
    CATCH(excpt1)
    {
        arc = ERROR_PROTECTION_VIOLATION;
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    // update the "widgets" notebook page, if open
    ntbUpdateVisiblePage(somSelf, SP_XCENTER_WIDGETS);

    return arc;
}

/*
 *@@ xwpCreateTray:
 *      creates a new tray in the given tray widget.
 *
 *      Returns:
 *
 *      --  NO_ERROR
 *
 *      --  ERROR_INVALID_PARAMETER
 *
 *      --  ERROR_NOT_ENOUGH_MEMORY
 *
 *      --  ERROR_PROTECTION_VIOLATION
 *
 *      --  XCERR_INVALID_ROOT_WIDGET_INDEX: ulTrayWidgetIndex
 *          does not exist.
 *
 *      --  XCERR_ROOT_WIDGET_INDEX_IS_NO_TRAY: ulTrayWidgetIndex
 *          is valid, but does not point to a tray widget.
 *
 *@@added V0.9.19 (2002-04-25) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xctr_xwpCreateTray(XCenter *somSelf,
                                            ULONG ulTrayWidgetIndex,
                                            PSZ pszTrayName)
{
    APIRET      arc = NO_ERROR;
    BOOL        fLocked = FALSE;

    XCenterMethodDebug("XCenter","xctr_xwpCreateTray");

    if (!pszTrayName)
        return ERROR_INVALID_PARAMETER;

    TRY_LOUD(excpt1)
    {
        if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
        {
            XCenterData *somThis = XCenterGetData(somSelf);
            PPRIVATEWIDGETSETTING pTrayWidgetSetting;
            PXCENTERWIDGET pTrayWidget;

            WIDGETPOSITION pos;
            pos.ulTrayWidgetIndex = -1;
            pos.ulTrayIndex = -1;
            pos.ulWidgetIndex = ulTrayWidgetIndex;

            if (!(arc = ctrpFindWidgetSetting(somSelf,
                                              &pos,
                                              &pTrayWidgetSetting,
                                              &pTrayWidget)))
            {
                if (!(pTrayWidgetSetting->pllTraySettings))
                    arc = XCERR_ROOT_WIDGET_INDEX_IS_NO_TRAY;
                else
                {
                    PTRAYSETTING pNewTray;
                    ULONG ulNewIndex;
                    if (!(pNewTray = ctrpCreateTraySetting(pTrayWidgetSetting,
                                                           pszTrayName,
                                                           &ulNewIndex)))
                        arc = ERROR_NOT_ENOUGH_MEMORY;
                    else
                    {
                        // do not hold the mutex while sending
                        _wpReleaseObjectMutexSem(somSelf);
                        fLocked = FALSE;

                        if (pTrayWidget)
                        {
                            WinSendMsg(pTrayWidget->hwndWidget,
                                       XCM_TRAYSCHANGED,
                                       0,
                                       0);
                        }

                        // update the "widgets" notebook page, if open
                        ntbUpdateVisiblePage(somSelf, SP_XCENTER_WIDGETS);
                    }
                }
            }
        }
    }
    CATCH(excpt1)
    {
        arc = ERROR_PROTECTION_VIOLATION;
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    return arc;
}

/*
 *@@ xwpRenameTray:
 *      gives the specified tray a new name.
 *
 *      ulTrayWidgetIndex must specify the index of the
 *      tray widget, and ulTrayIndex the index of the
 *      tray to rename in that widget.
 *
 *      Returns:
 *
 *      --  NO_ERROR
 *
 *      --  ERROR_INVALID_PARAMETER
 *
 *      --  ERROR_NOT_ENOUGH_MEMORY
 *
 *      --  ERROR_PROTECTION_VIOLATION
 *
 *      --  XCERR_INVALID_ROOT_WIDGET_INDEX: ulTrayWidgetIndex
 *          does not exist.
 *
 *      --  XCERR_ROOT_WIDGET_INDEX_IS_NO_TRAY: ulTrayWidgetIndex
 *          is valid, but does not point to a tray widget.
 *
 *      --  XCERR_INVALID_TRAY_INDEX: ulTrayIndex is
 *          invalid.
 *
 *@@added V0.9.19 (2002-04-25) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xctr_xwpRenameTray(XCenter *somSelf,
                                            ULONG ulTrayWidgetIndex,
                                            ULONG ulTrayIndex,
                                            PSZ pszNewTrayName)
{
    APIRET      arc = NO_ERROR;
    BOOL        fLocked = FALSE;

    XCenterMethodDebug("XCenter","xctr_xwpRenameTray");

    if (!pszNewTrayName)
        return ERROR_INVALID_PARAMETER;

    TRY_LOUD(excpt1)
    {
        if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
        {
            XCenterData *somThis = XCenterGetData(somSelf);
            PPRIVATEWIDGETSETTING pTrayWidgetSetting;
            PTRAYSETTING pTraySetting;
            PXCENTERWIDGET pTrayWidget = NULL;
            if (!(arc = ctrpFindTraySetting(somSelf,
                                            ulTrayWidgetIndex,
                                            ulTrayIndex,
                                            &pTrayWidgetSetting,
                                            &pTraySetting,
                                            &pTrayWidget)))
            {
                if (pTraySetting->pszTrayName)
                    free(pTraySetting->pszTrayName);
                pTraySetting->pszTrayName = strdup(pszNewTrayName);

                if (pTrayWidget)
                {
                    // we have an open view:
                    _wpReleaseObjectMutexSem(somSelf);
                    fLocked = FALSE;

                    WinSendMsg(pTrayWidget->hwndWidget,
                               XCM_TRAYSCHANGED,
                               0,
                               0);
                }
            }
        }
    }
    CATCH(excpt1)
    {
        arc = ERROR_PROTECTION_VIOLATION;
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    return arc;
}

/*
 *@@ xwpDeleteWidget:
 *      removes the widget at the given position.
 *
 *      This operates in two modes:
 *
 *      --  If ulTrayWidgetIndex and ulTrayIndex are both
 *          -1, this removes the root widget at ulWidgetIndex,
 *          where 0 specifies the leftmost widget. If this
 *          is a tray widget, all trays and subwidgets are
 *          removed as well.
 *
 *      --  If ulTrayWidget and ulTrayIndex are not -1, they
 *          specify the index of an existing tray widget and
 *          of an existing tray in that widget. This then
 *          removes the subwidget at ulWidgetIndex in the
 *          given tray widget and tray, where, again, 0
 *          specifies the leftmost subwidget.
 *
 *      If the XCenter is currently open, its view is
 *      refreshed if necessary.
 *
 *      Returns:
 *
 *      --  NO_ERROR
 *
 *      --  ERROR_INVALID_PARAMETER
 *
 *      --  ERROR_NOT_ENOUGH_MEMORY
 *
 *      --  ERROR_PROTECTION_VIOLATION
 *
 *      --  XCERR_INVALID_ROOT_WIDGET_INDEX: ulWidgetIndex
 *          does not exist.
 *
 *      --  XCERR_ROOT_WIDGET_INDEX_IS_NO_TRAY: in tray
 *          mode, ulWidgetIndex is not a tray widget.
 *
 *      --  XCERR_INVALID_TRAY_INDEX: ulTrayIndex is
 *          invalid.
 *
 *      --  XCERR_INVALID_SUBWIDGET_INDEX: ulTrayWidgetIndex
 *          is invalid.
 *
 *@@added V0.9.7 (2000-12-02) [umoeller]
 *@@changed V0.9.19 (2002-05-04) [umoeller]: prototype changed, rewritten to support trays
 */

SOM_Scope ULONG SOMLINK xctr_xwpDeleteWidget(XCenter *somSelf,
                                             XCenter_PWIDGETPOSITION pPosition)
{
    APIRET arc = NO_ERROR;
    BOOL        fLocked = FALSE;

    if (!pPosition)
        return ERROR_INVALID_PARAMETER;

    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_xwpDeleteWidget");

    TRY_LOUD(excpt1)
    {
        if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
        {
            XCenterData *somThis = XCenterGetData(somSelf);
            PPRIVATEWIDGETSETTING   pSetting = NULL;
            PXCENTERWIDGET          pViewData = NULL;
            HWND                    hwndClient = NULLHANDLE;

            if (!(arc = ctrpFindWidgetSetting(somSelf,
                                              pPosition,
                                              &pSetting,
                                              &pViewData)))
            {
                // widget or subwidget found:
                PXCENTERWINDATA pXCenterData;

                // we must _first_ refresh the view before nuking
                // the widget setting, so check if we found one

                if (    (pViewData)
                     && (pXCenterData = (PXCENTERWINDATA)_pvOpenView)
                   )
                {
                    // mark for refresh below:
                    if (pSetting->pOwningTray)
                    {
                        // this is a subwidget in a tray and it is
                        // currently displaying:
                        PPRIVATEWIDGETVIEW pOwningTrayWidget;
                        if (pOwningTrayWidget = ((PPRIVATEWIDGETVIEW)pViewData)->pOwningTrayWidget)
                            // this widget resides in a tray:
                            hwndClient = pOwningTrayWidget->Widget.hwndWidget;
                    }
                    else
                        // root widget:
                        hwndClient = pXCenterData->Globals.hwndClient;
                }

                // do not hold the mutex while sending
                _wpReleaseObjectMutexSem(somSelf);
                fLocked = FALSE;

                if (hwndClient)
                {
                    // we must send a msg instead of doing WinDestroyWindow
                    // directly because only the XCenter GUI thread can
                    // destroy the window

                    WinSendMsg(hwndClient,
                               XCM_DESTROYWIDGETWINDOW,
                               (MPARAM)pViewData->hwndWidget,
                               0);
                    // msg behavior depends on whether this is sent
                    // to client or tray widget
                }

                if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
                {
                    // now we can kill the widget setting
                    if (!(arc = ctrpRemoveWidgetSetting(somSelf,
                                                        &pSetting)))
                        _wpSaveDeferred(somSelf);

                    if (hwndClient)
                        // we found the open view above:
                        WinPostMsg(hwndClient,
                                   XCM_REFORMAT,
                                   (MPARAM)(XFMF_RECALCHEIGHT | XFMF_REPOSITIONWIDGETS),
                                   0);

                    // do not hold the mutex while sending
                    _wpReleaseObjectMutexSem(somSelf);
                    fLocked = FALSE;

                    // update the "widgets" notebook page, if open
                    ntbUpdateVisiblePage(somSelf, SP_XCENTER_WIDGETS);
                }
            }

            /*
            PLISTNODE   pSettingsNode = lstNodeFromIndex(pllWidgetSettings, ulIndex);
            HWND        hwndXCenterClient = NULLHANDLE;

            if (!pSettingsNode)
                cmnLog(__FILE__, __LINE__, __FUNCTION__,
                       "Invalid widget index 0x%lX.", ulIndex);
            else
            {
                // widget exists:

                PPRIVATEWIDGETSETTING pSetting = (PPRIVATEWIDGETSETTING)pSettingsNode->pItemData;

                if (_pvOpenView)
                {
                    // XCenter view currently open:
                    PPRIVATEWIDGETVIEW pView;
                    PXCENTERWINDATA pXCenterData = (PXCENTERWINDATA)_pvOpenView;
                    PXCENTERGLOBALS pGlobals = &pXCenterData->Globals;

                    // store XCenter client so we can update the client
                    // at the bottom
                    hwndXCenterClient = pGlobals->hwndClient;

                    if (pView = lstItemFromIndex(&pXCenterData->llWidgets,
                                                 ulIndex))
                    {
                    }
                } // end if (_hwndOpenView)

                brc = lstRemoveNode(pllWidgetSettings, pSettingsNode);

                // no auto-free...
                // clear all the setting members, including
                // tray and subwidget stuff
                ctrpFreeSettingData(&pSetting);      // V0.9.13 (2001-06-21) [umoeller]

                if (brc)
                    // save instance data (with that linked list)
                    _wpSaveDeferred(somSelf);

                // update the "widgets" notebook page, if open
                ntbUpdateVisiblePage(somSelf, SP_XCENTER_WIDGETS);

            } // end if (pSettingsNode)

            if (hwndXCenterClient)
            {
                // we found the open view above:
                WinPostMsg(hwndXCenterClient,
                           XCM_REFORMAT,
                           (MPARAM)(XFMF_RECALCHEIGHT | XFMF_REPOSITIONWIDGETS),
                           0);
            }

            */
        }
    }
    CATCH(excpt1)
    {
        arc = ERROR_PROTECTION_VIOLATION;
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    return arc;
}

/*
 *@@ xwpMoveWidget:
 *      moves a widget to a new position within the
 *      same XCenter.
 *
 *      ulIndex2Move specifies the widget to be moved
 *      (with 0 being the leftmost widget).
 *
 *      ulBeforeIndex specifies the position to which
 *      the widget should be moved. 0 means leftmost,
 *      1 means before first widget, etc.
 *      If ulBeforeIndex is -1 or larger than the
 *      no. of widgets in the XCenter, we insert the
 *      new widget as the last widget.
 *
 *@@added V0.9.7 (2000-12-10) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xctr_xwpMoveWidget(XCenter *somSelf,
                                           ULONG ulIndex2Move,
                                           ULONG ulBeforeIndex)
{
    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_xwpMoveWidget");

    return ctrpMoveWidget(somSelf, ulIndex2Move, ulBeforeIndex);
}

/*
 *@@ xwpSetPriority:
 *      sets a new priority for the XCenter view.
 *      lDelta is used as with DosSetPriority and
 *      specifies the priority within the "regular"
 *      class.
 *
 *      An open XCenter thread is updated.
 *
 *@@added V0.9.7 (2001-01-03) [umoeller]
 *@@changed V0.9.19 (2002-05-04) [umoeller]: moved impl. back here, fixed missing save
 *@@changed V0.9.20 (2002-08-08) [umoeller]: removed class parameter
 */

SOM_Scope BOOL  SOMLINK xctr_xwpSetPriority(XCenter *somSelf,
                                            long lDelta)
{
    BOOL    brc = FALSE;
    BOOL    fLocked = FALSE;

    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_xwpSetPriority");

    TRY_LOUD(excpt1)
    {
        if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
        {
            XCenterData *somThis = XCenterGetData(somSelf);
            _lPriorityDelta = lDelta;

            if (_pvOpenView)
            {
                DosSetPriority(PRTYS_THREAD,
                               PRTYC_REGULAR,
                               lDelta,
                               _tidRunning);   // tid of XCenter GUI thread
            }

            _wpSaveDeferred(somSelf); // V0.9.19 (2002-05-04) [umoeller]
        }
        else
            brc = FALSE;
    }
    CATCH(excpt1) {} END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    return brc;
}

/*
 *@@ xwpQuerySetup2:
 *      this XFldObject method is overridden to support
 *      setup strings for folders.
 *
 *      See XFldObject::xwpQuerySetup2 for details.
 *
 *@@added V0.9.7 (2000-12-09) [umoeller]
 *@@changed V0.9.16 (2001-10-11) [umoeller]: adjusted to new implementation
 */

SOM_Scope BOOL  SOMLINK xctr_xwpQuerySetup2(XCenter *somSelf,
                                            PVOID pstrSetup)
{
    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_xwpQuerySetup2");

    // call implementation
    return ctrpQuerySetup(somSelf, pstrSetup);
}

/*
 *@@ xwpHotkeyOrBorderAction:
 *      this new XFldObject method gets called
 *      from the thread-1 object window whenever
 *      the XWPDaemon notifies it that an object
 *      should be opened, either because a hotkey
 *      was pressed or because a screen border was
 *      touched with the mouse.
 *
 *      This method gets resolved by name from
 *      T1M_OpenObjectFromHandle. This allows WPS
 *      classes to override what happens in this case.
 *
 *      Parameters:
 *
 *      --  hab is the anchor block of WPS thread 1.
 *
 *      --  ulCorner is 0 if this is really from a
 *          hotkey. In that case, the "hotkey"
 *          system sound has already been played.
 *          It is > 0 if this resulted from a
 *          screen border mouse action.
 *
 *      We override this for the XCenter to be able
 *      to open the first widget which can handle
 *      this event.
 *
 *@@added V0.9.19 (2002-04-17) [umoeller]
 */

SOM_Scope HWND  SOMLINK xctr_xwpHotkeyOrBorderAction(XCenter *somSelf,
                                                     ULONG ulView,
                                                     ULONG ulCorner)
{
    PXCENTERWINDATA pXCenterData;
    XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_xwpHotkeyOrBorderAction");

    if (    (_tidRunning)
         && (pXCenterData = (PXCENTERWINDATA)_pvOpenView)
       )
    {
        // XCenter view is open:
        WinPostMsg(pXCenterData->Globals.hwndClient,
                   XCM_REFORMAT,
                   (MPARAM)((ulCorner == 0)
                                // if object hotkey, open first widget
                                ? XFMF_RESURFACE | XFMF_FOCUS2FIRSTWIDGET
                                // if screen corner, resurface only
                                : XFMF_RESURFACE),
                   0);

        return pXCenterData->Globals.hwndFrame;
    }

    return _wpViewObject(somSelf,
                         NULLHANDLE,
                         ulView, // OPEN_DEFAULT,
                         0);
}

/*
 *@@ xwpDeleteWidgetClass:
 *      this deletes all widgets of a named class.
 *
 *      Parameters:
 *
 *      --  pszWidgetClass is the class of the widget to delete.
 *
 *@@added V1.0.7 (2006-12-31) [pr]
 */

SOM_Scope ULONG  SOMLINK xctr_xwpDeleteWidgetClass(XCenter *somSelf,
                                                   PSZ pszWidgetClass)
{
    APIRET arc = NO_ERROR;
    BOOL   fLocked = FALSE, fFound;

    XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_xwpDeleteWidgetClass");

    TRY_LOUD(excpt1)
    {
        do
        {
            fFound = FALSE;
            if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
            {
                PLINKLIST   pllWidgets = ctrpQuerySettingsList(somSelf);
                PLISTNODE   pNode = lstQueryFirstNode(pllWidgets);
                ULONG       ulIndex = 0;

                while (pNode && !fFound)
                {
                    PPRIVATEWIDGETSETTING pSetting = (PPRIVATEWIDGETSETTING)pNode->pItemData;
                    WIDGETPOSITION Pos;

                    Pos.ulTrayWidgetIndex = -1;
                    Pos.ulTrayIndex = -1;
                    Pos.ulWidgetIndex = ulIndex;
                    if (!stricmp(pSetting->Public.pszWidgetClass, pszWidgetClass))
                    {
                        // widget of the correct class found:
                        fFound = TRUE;
                        _wpReleaseObjectMutexSem(somSelf);
                        fLocked = FALSE;
                        arc = _xwpDeleteWidget(somSelf, &Pos);
                        break;
                    }

                    if (pSetting->pllTraySettings)
                    {
                        PLISTNODE pTrayNode = lstQueryFirstNode(pSetting->pllTraySettings);

                        Pos.ulTrayWidgetIndex = ulIndex;
                        Pos.ulTrayIndex = 0;
                        while (pTrayNode && !fFound)
                        {
                            PTRAYSETTING pTray = (PTRAYSETTING)pTrayNode->pItemData;
                            PLISTNODE pSubwidgetNode = lstQueryFirstNode(&pTray->llSubwidgetSettings);

                            Pos.ulWidgetIndex = 0;
                            while (pSubwidgetNode)
                            {
                                PPRIVATEWIDGETSETTING pSubwidget = (PPRIVATEWIDGETSETTING)pSubwidgetNode->pItemData;

                                if (!stricmp(pSubwidget->Public.pszWidgetClass, pszWidgetClass))
                                {
                                    // subwidget of the correct class found:
                                    fFound = TRUE;
                                    _wpReleaseObjectMutexSem(somSelf);
                                    fLocked = FALSE;
                                    arc = _xwpDeleteWidget(somSelf, &Pos);
                                    break;
                                }

                                (Pos.ulWidgetIndex)++;
                                pSubwidgetNode = pSubwidgetNode->pNext;
                            }

                            pTrayNode = pTrayNode->pNext;
                            (Pos.ulTrayIndex)++;
                        }
                    }

                    pNode = pNode->pNext;
                    ulIndex++;
                }

                if (fLocked)
                {
                    _wpReleaseObjectMutexSem(somSelf);
                    fLocked = FALSE;
                }
            }
        }
        while (fFound && !arc);
    }
    CATCH(excpt1)
    {
        arc = ERROR_PROTECTION_VIOLATION;
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    return arc;
}

/*
 *@@ wpInitData:
 *      this WPObject instance method gets called when the
 *      object is being initialized (on wake-up or creation).
 *      We initialize our additional instance data here.
 *      Always call the parent first.
 */

SOM_Scope void  SOMLINK xctr_wpInitData(XCenter *somSelf)
{
    XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpInitData");

    XCenter_parent_WPAbstract_wpInitData(somSelf);

    // initialize defaults from the table for those
    // variables which are in there...
    ctrpInitData(somSelf);

    _pvOpenView = NULL;
    _tidRunning = 0;

    _fShowingOpenViewMenu = FALSE;

    _pszPackedWidgetSettings = NULL;
    _cbPackedWidgetSettings = 0;

    _pllAllWidgetSettings = NULL;
}

/*
 * wpObjectReady: override;     removed V0.9.9 (2001-03-13) [umoeller]
 */

/*
 *@@ wpUnInitData:
 *      this WPObject instance method is called when the object
 *      is destroyed as a SOM object, either because it's being
 *      made dormant or being deleted. All allocated resources
 *      should be freed here.
 *      The parent method must always be called last.
 */

SOM_Scope void  SOMLINK xctr_wpUnInitData(XCenter *somSelf)
{
    XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpUnInitData");

    ctrpFreeWidgets(somSelf);

    if (_pszPackedWidgetSettings)
    {
        free(_pszPackedWidgetSettings);
        _pszPackedWidgetSettings = NULL;
    }

    XCenter_parent_WPAbstract_wpUnInitData(somSelf);
}

/*
 *@@ wpSetup:
 *      this WPObject instance method is called to allow an
 *      object to set itself up according to setup strings.
 *      As opposed to wpSetupOnce, this gets called any time
 *      a setup string is invoked.
 *
 *      We scan for the XCenter setup here. The implementation
 *      is in ctrpSetup.
 *
 *@@added V0.9.7 (2001-01-25) [umoeller]
 *@@changed V0.9.19 (2002-04-25) [umoeller]: this never returned FALSE, fixed
 */

SOM_Scope BOOL  SOMLINK xctr_wpSetup(XCenter *somSelf, PSZ pszSetupString)
{
    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpSetup");

    if (XCenter_parent_WPAbstract_wpSetup(somSelf, pszSetupString))
        return ctrpSetup(somSelf,
                         pszSetupString);
            // V0.9.19 (2002-04-25) [umoeller]

    return FALSE;
}

/*
 *@@ wpSetupOnce:
 *      this WPObject method allows special object handling
 *      based on a creation setup string after an object has
 *      been fully created.
 *      As opposed to WPObject::wpSetup, this method _only_
 *      gets called during object creation. The WPObject
 *      implementation calls wpSetup in turn.
 *      If FALSE is returned, object creation is aborted.
 *
 *      After having parsed the setup string, we check if
 *      we have any widgets in the XCenter already. If not,
 *      this probably means that no initial WIDGETS setup
 *      string was given with the object, and we create
 *      some standard XCenter widgets.
 *
 *@@added V0.9.16 (2001-10-15) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xctr_wpSetupOnce(XCenter *somSelf, PSZ pszSetupString)
{
    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpSetupOnce");

    if (XCenter_parent_WPAbstract_wpSetupOnce(somSelf, pszSetupString))
        return ctrpSetupOnce(somSelf, pszSetupString);

    return FALSE;
}

/*
 *@@ wpSaveState:
 *      this WPObject instance method saves an object's state
 *      persistently so that it can later be re-initialized
 *      with wpRestoreState. This gets called during wpClose,
 *      wpSaveImmediate or wpSaveDeferred processing.
 *      All persistent instance variables should be stored here.
 *
 *@@added V0.9.7 (2000-12-02) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xctr_wpSaveState(XCenter *somSelf)
{
    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpSaveState");

    if (XCenter_parent_WPAbstract_wpSaveState(somSelf))
        return ctrpSaveState(somSelf);

    return FALSE;
}

/*
 *@@ wpRestoreState:
 *      this WPObject instance method gets called during object
 *      initialization (after wpInitData) to restore the data
 *      which was stored with wpSaveState.
 *
 *@@added V0.9.7 (2000-12-02) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xctr_wpRestoreState(XCenter *somSelf,
                                            ULONG ulReserved)
{
    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpRestoreState");

    if (XCenter_parent_WPAbstract_wpRestoreState(somSelf, ulReserved))
        return ctrpRestoreState(somSelf);

    return FALSE;
}

/*
 *@@ wpModifyPopupMenu:
 *      this WPObject instance methods gets called by the WPS
 *      when a context menu needs to be built for the object
 *      and allows the object to manipulate its context menu.
 *      This gets called _after_ wpFilterPopupMenu.
 *
 *      We override this to add the "Widgets" submenu to an
 *      open XCenter's popup menu. Note that we have not
 *      overridden wpMenuItemSelected, because WM_COMMAND
 *      is intercepted in the XCenter windows directly.
 *
 *      This last comment is not entirely correct. We do need
 *      to override wpMenuItemSelected for "Open as, XCenter"
 *      to work.
 *
 *@@added V0.9.7 (2000-12-02) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xctr_wpModifyPopupMenu(XCenter *somSelf,
                                               HWND hwndMenu,
                                               HWND hwndCnr,
                                               ULONG iPosition)
{
    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpModifyPopupMenu");

    if (XCenter_parent_WPAbstract_wpModifyPopupMenu(somSelf,
                                                    hwndMenu,
                                                    hwndCnr,
                                                    iPosition))
        // add "Add widget" submenu etc.
        return ctrpModifyPopupMenu(somSelf, hwndMenu);

    return FALSE;
}

/*
 *@@ wpMenuItemSelected:
 *      this WPObject method processes menu selections.
 *      This must be overridden to support new menu
 *      items which have been added in wpModifyPopupMenu.
 *
 *      See XFldObject::wpMenuItemSelected for additional
 *      remarks.
 *
 *      We need to support "open XCenter view".
 *
 *@@added V1.0.6 (2006-11-13) [pr]: @@fixes 889
 */

SOM_Scope BOOL  SOMLINK xctr_wpMenuItemSelected(XCenter *somSelf,
                                                HWND hwndFrame,
                                                ULONG ulMenuId)
{
    BOOL brc = TRUE;

    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpMenuItemSelected");

    if (ulMenuId == *G_pulVarMenuOfs + ID_XFMI_OFS_XWPVIEW)
    {
        // "Open" --> "XCenter":
        // wpViewObject will call wpOpen if a new view is necessary
        _wpViewObject(somSelf,
                      NULLHANDLE,   // hwndCnr; "WPS-internal use only", IBM says
                      ulMenuId,     // ulView; must be the same as menu item
                      0);           // parameter passed to wpOpen
    }
    else
        // not our menu item (maybe settings): call parent
        brc = XCenter_parent_WPAbstract_wpMenuItemSelected(somSelf,
                                                           hwndFrame,
                                                           ulMenuId);

    return brc;
}

/*
 *@@ wpQueryDefaultView:
 *      this WPObject method returns the default view of an object,
 *      that is, which view is opened if the program file is
 *      double-clicked upon. This is also used to mark
 *      the default view in the "Open" context submenu.
 *
 *      This must be overridden for direct WPAbstract subclasses,
 *      because otherwise double-clicks on the object won't
 *      work.
 */

SOM_Scope ULONG  SOMLINK xctr_wpQueryDefaultView(XCenter *somSelf)
{
    /* XCenterData *somThis = XCenterGetData(somSelf); */
    XCenterMethodDebug("XCenter","xctr_wpQueryDefaultView");

    return *G_pulVarMenuOfs + ID_XFMI_OFS_XWPVIEW;
}

/*
 *@@ wpOpen:
 *      this WPObject instance method gets called when
 *      a new view needs to be opened. Normally, this
 *      gets called after wpViewObject has scanned the
 *      object's USEITEMs and has determined that a new
 *      view is needed.
 *
 *      This _normally_ runs on thread 1 of the WPS, but
 *      this is not always the case. If this gets called
 *      in response to a menu selection from the "Open"
 *      submenu or a double-click in the folder, this runs
 *      on the thread of the folder (which _normally_ is
 *      thread 1). However, if this results from WinOpenObject
 *      or an OPEN setup string, this will not be on thread 1.
 *
 *      We open an XCenter view here by calling
 *      ctrpCreateXCenterView or redirect other views to
 *      thread 1.
 *
 *@@changed V0.9.9 (2001-02-06) [umoeller]: now redirecting settings to thread-1
 *@@changed V1.0.1 (2003-02-02) [umoeller]: made introductory help a global flag
 */

SOM_Scope HWND  SOMLINK xctr_wpOpen(XCenter *somSelf,
                                    HWND hwndCnr,
                                    ULONG ulView,
                                    ULONG param)
{
    HWND    hwndNewView = NULLHANDLE;
    XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpOpen");

    if (ulView == *G_pulVarMenuOfs + ID_XFMI_OFS_XWPVIEW)
    {
        if (!_tidRunning)       // V0.9.12 (2001-05-20) [umoeller]
        {
            // no open view yet (just make sure!)
            HWND hwnd = (hwndCnr) ? hwndCnr : cmnQueryActiveDesktopHWND();
            ULONG   fl;

            hwndNewView = ctrpCreateXCenterView(somSelf,
                                                WinQueryAnchorBlock(hwnd),
                                                ulView,
                                                // store in instance data
                                                &_pvOpenView);
            // V1.0.1 (2003-02-02) [umoeller]
            fl = cmnQuerySetting(sflIntroHelpShown);
            if (!(fl & HLPS_NOSHOWXCENTER))
            {
                ULONG ulPanel = 0;
                CHAR szHelp[CCHMAXPATH];

                cmnSetSetting(sflIntroHelpShown, fl | HLPS_NOSHOWXCENTER);

                _wpQueryDefaultHelp(somSelf,
                                    &ulPanel,
                                    szHelp);
                // help not displayed yet:
                _wpDisplayHelp(somSelf,
                               ulPanel,
                               szHelp);
            }
        }
    }
    else
    {
        // other view (probably settings):

        // make sure we don't open the other views on the XCenter
        // view thread... otherwise the views get closed when
        // the thread terminates
        if (    (_tidRunning)
             && (doshMyTID() == _tidRunning)
           )
        {
            // we're on the XCenter thread here:
            // redirect to thread 1
            hwndNewView = (HWND)krnSendThread1ObjectMsg(T1M_OPENOBJECTFROMPTR,
                                                        (MPARAM)somSelf,
                                                        (MPARAM)ulView);
        }
        else
            hwndNewView = XCenter_parent_WPAbstract_wpOpen(somSelf,
                                                           hwndCnr,
                                                           ulView,
                                                           param);
    }

    return hwndNewView;
}

/*
 *@@ wpSwitchTo:
 *      this WPObject method is called to give focus
 *      to an already open view. This gets called
 *      from wpViewObject instead of wpOpen if a view
 *      already exists, but can be called separately
 *      as well.
 *
 *      For the XCenter, we'll give focus to the
 *      XCenter view... however, if auto-hide is
 *      enabled, we'll also have to re-show the
 *      frame.
 *
 *@@added V0.9.7 (2000-12-04) [umoeller]
 *@@changed V0.9.16 (2001-12-31) [umoeller]: this never worked; now sending message to client properly
 */

SOM_Scope BOOL  SOMLINK xctr_wpSwitchTo(XCenter *somSelf, ULONG View)
{
    BOOL brc = FALSE;
    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpSwitchTo");

    // check if we should switch to the existing XCenter view
    if (View == *G_pulVarMenuOfs + ID_XFMI_OFS_XWPVIEW)
    {
        // yes:
        PUSEITEM    pUseItem = NULL;
        for (pUseItem = _wpFindUseItem(somSelf, USAGE_OPENVIEW, NULL);
             pUseItem;
             pUseItem = _wpFindUseItem(somSelf, USAGE_OPENVIEW, pUseItem))
        {
            PVIEWITEM pViewItem = (PVIEWITEM)(pUseItem + 1);
            if (pViewItem->view == View)
            {
                HWND hwndClient;
                // yes, it's an XCenter view:
                // instead of activating the view (which is what
                // the WPS normally does), show the frame on top
                // and restart the update timer
                // DO NOT GIVE FOCUS, DO NOT ACTIVATE

                    // duh, this must be posted to the client, not the
                    // frame... V0.9.16 (2001-12-31) [umoeller]
                if (hwndClient = WinWindowFromID(pViewItem->handle, FID_CLIENT))
                {
                    WinPostMsg(hwndClient,
                               XCM_REFORMAT,
                               (MPARAM)XFMF_RESURFACE,
                               0);
                    brc = TRUE;
                }

                break;
            }
        }
    }
    else
        // view other than XCenter view (probably settings):
        brc = XCenter_parent_WPAbstract_wpSwitchTo(somSelf, View);

    return brc;
}

/*
 *@@ wpClose:
 *      this WPObject method goes through the USAGE_OPENVIEW
 *      useitems of the object and sends (!) WM_CLOSE to
 *      each of them.
 *
 *      This is also used by XShutdown to close objects.
 *
 *      For the XCenter, we call the parent to have this
 *      job done. Howver, in addition, we MUST wait for
 *      the XCenter thread to terminate or otherwise we
 *      might hang on Desktop workarea resize...
 *
 *      The problem is that once WM_CLOSE is received by the
 *      XCenter frame, it will destroy all child windows
 *      and then exit the XCenter thread a bit later. During
 *      exit, if "resize desktop" is enabled, the desktop is
 *      resized. If XShutdown closes the XCenter and the
 *      desktop next, the WPS apparently cannot handle this
 *      if this isn't properly serialized.
 *
 *@@added V0.9.9 (2001-04-04) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xctr_wpClose(XCenter *somSelf)
{
    // XCenterData *somThis = XCenterGetData(somSelf);
    XCenterMethodDebug("XCenter","xctr_wpClose");

    if (XCenter_parent_WPAbstract_wpClose(somSelf))
    {
        /* while (_tid)
            winhSleep(100); */

        return TRUE;
    }

    return FALSE;
}

/*
 *@@ wpAddObjectWindowPage:
 *      this WPObject instance method normally adds the
 *      "Standard Options" page to the settings notebook
 *      (that's what the WPS reference calls it; it's actually
 *      the "Window" page).
 */

SOM_Scope ULONG  SOMLINK xctr_wpAddObjectWindowPage(XCenter *somSelf,
                                                    HWND hwndNotebook)
{
    /* XCenterData *somThis = XCenterGetData(somSelf); */
    XCenterMethodDebug("XCenter","xctr_wpAddObjectWindowPage");

    return SETTINGS_PAGE_REMOVED;
}

/*
 *@@ wpAddSettingsPages:
 *      this WPObject instance method gets called by the WPS
 *      when the Settings view is opened to have all the
 *      settings page inserted into hwndNotebook.
 *
 *      We call XCenter::xwpAddXXenterPages to have the
 *      "XCenter" pages inserted on top.
 */

SOM_Scope BOOL  SOMLINK xctr_wpAddSettingsPages(XCenter *somSelf,
                                                HWND hwndNotebook)
{
    /* XCenterData *somThis = XCenterGetData(somSelf); */
    XCenterMethodDebug("XCenter","xctr_wpAddSettingsPages");

    if (XCenter_parent_WPAbstract_wpAddSettingsPages(somSelf,
                                                     hwndNotebook))
    {
        _xwpAddXCenterPages(somSelf, hwndNotebook);
        return TRUE;
    }

    return FALSE;
}

/* ******************************************************************
 *
 *   XCenter class methods
 *
 ********************************************************************/

/*
 *@@ xwpclsAutoOpen:
 *      this is invoked by the XWP startup thread immediately
 *      before it processes XWPStartup folders if it finds the
 *      XCENTERAUTOOPEN key in os2.ini. It will not be called
 *      if the user disabled startup folder processing in the
 *      Panic dialog.
 *
 *      If any of the handles in the auto-open list are invalid
 *      or can't be opened (e.g. because they refer to deleted
 *      objects), they will be removed and the ini entry will be
 *      updated after the remaining XCenters are opened.
 */

SOM_Scope void  SOMLINK xctrM_xwpclsAutoOpen(M_XCenter *somSelf)
{
    M_XCenterData *somThis = M_XCenterGetData(somSelf);
    BOOL    fErr = FALSE;
    ULONG   ctr;

    M_XCenterMethodDebug("M_XCenter","xctrM_xwpclsAutoOpen");

    for (ctr = 0; ctr < XCTR_MAX_AUTO && _aulAutoOpen[ctr]; ctr++)
    {
        XCenter *obj = _wpclsObjectFromHandle(somSelf, _aulAutoOpen[ctr]);

        // if the handle can't be instantiated or it isn't
        // an XCenter, set it to be removed from the list
        if (!obj || !_somIsA(obj, somSelf)) {
            _aulAutoOpen[ctr] = (ULONG)-1;
            fErr = TRUE;
            continue;
        }

        // don't open templates
        if (_wpQueryStyle(obj) & OBJSTYLE_TEMPLATE)
            continue;

        // if the object can't be opened, set it to be removed from the list
        if (!_wpOpen(obj, 0, (*G_pulVarMenuOfs + ID_XFMI_OFS_XWPVIEW), 0)) {
            _aulAutoOpen[ctr] = (ULONG)-1;
            fErr = TRUE;
        }
    }

    // remove invalid entries and save result
    if (fErr)
        _xwpclsSaveAutoOpen(somSelf);
}

/*
 *@@ xwpclsSetAutoOpen:
 *      this is called by the View1 page of XCenter's notebook
 *      when that page is initialized, and again if the user toggles
 *      the auto-open option. It is also invoked when this XCenter
 *      is created and when a setup string containing "AUTOOPEN=YES|NO"
 *      is processed.
 *
 *      It returns the current auto-open state for the object whose
 *      handle was passed in, regardless of whether any action was
 *      required to set that state.
 */

SOM_Scope BOOL  SOMLINK xctrM_xwpclsSetAutoOpen(M_XCenter *somSelf,
                                                ULONG ulHandle,
                                                BOOL fAutoOpen)
{
    M_XCenterData *somThis = M_XCenterGetData(somSelf);
    ULONG   ctr;
    ULONG   found;

    M_XCenterMethodDebug("M_XCenter","xctrM_xwpclsSetAutoOpen");

    if (!ulHandle)
        return FALSE;

    // loop until we reach the first empty slot
    found = XCTR_MAX_AUTO;
    for (ctr = 0; ctr < XCTR_MAX_AUTO && _aulAutoOpen[ctr]; ctr++)
    {
        if (_aulAutoOpen[ctr] == ulHandle)
            found = ctr;
    }

    if (fAutoOpen) {
        // already present
        if (found < XCTR_MAX_AUTO)
            return TRUE;

        // no room at the inn...
        if (ctr >= XCTR_MAX_AUTO)
            return FALSE;

        // put the handle in the first empty slot
        _aulAutoOpen[ctr] = ulHandle;
    }
    else {
        // not found
        if (found == XCTR_MAX_AUTO)
            return FALSE;

        // move the last non-zero item into the slot
        // occupied by the item to be removed
        // (if this is the only item, the one moved is already zero)
        _aulAutoOpen[found] = _aulAutoOpen[--ctr];
        _aulAutoOpen[ctr] = 0;
    }

    // only real changes reach this point
    if (!_xwpclsSaveAutoOpen(somSelf))
        fAutoOpen = !fAutoOpen;

    return fAutoOpen;
}

/*
 *@@ xwpclsSaveAutoOpen:
 *      this is called by the other auto-open class methods to
 *      save the current list. Those methods change invalid handles
 *      to (-1) to signal that the entry should be removed before
 *      the list is saved.
 */

SOM_Scope BOOL  SOMLINK xctrM_xwpclsSaveAutoOpen(M_XCenter *somSelf)
{
    M_XCenterData *somThis = M_XCenterGetData(somSelf);
    ULONG   i, j;
    char    *ptr;
    char    buf[XCTR_MAX_AUTO * 8]; // somewhat larger than needed

    M_XCenterMethodDebug("M_XCenter","xctrM_xwpclsSaveAutoOpen");

    ptr = buf;
    for (i = 0; i < XCTR_MAX_AUTO && _aulAutoOpen[i]; i++)
    {
        // remove entries other methods marked as invalid
        if (_aulAutoOpen[i] == (ULONG)-1) {
            for (j = i + 1; j < XCTR_MAX_AUTO && _aulAutoOpen[j]; j++);

            _aulAutoOpen[i] = _aulAutoOpen[--j];
            _aulAutoOpen[j] = 0;
            i--;
            continue;
        }

        // add this valid entry to the list
        ptr += sprintf(ptr, "%X ", _aulAutoOpen[i]);
    }

    // save the list if non-null; delete the key if the list is empty
    return PrfWriteProfileString(HINI_USERPROFILE, (PSZ)INIAPP_XWORKPLACE,
                                 (PSZ)INIKEY_XCENTERAUTOOPEN,
                                 (ptr == buf ? NULL : buf));
}

/*
 *@@ wpclsInitData:
 *      this WPObject class method gets called when a class
 *      is loaded by the WPS (probably from within a
 *      somFindClass call) and allows the class to initialize
 *      itself.
 *
 *      To support XCenter auto-open, it initializes the array
 *      of auto-open handles. If any of them appear to be
 *      malformed, it has xwpclsSaveAutoOpen() rewrite the
 *      list stored in os2.ini.
 */

SOM_Scope void  SOMLINK xctrM_wpclsInitData(M_XCenter *somSelf)
{
    M_XCenterData *somThis = M_XCenterGetData(somSelf);
    APIRET  arc = NO_ERROR;
    ULONG   ctr;
    ULONG   handle;
    char    *ptr;
    char    buf[XCTR_MAX_AUTO * 8]; // larger than needed, just in case

    M_XCenterMethodDebug("M_XCenter","xctrM_wpclsInitData");

    M_XCenter_parent_M_WPAbstract_wpclsInitData(somSelf);

    krnClassInitialized(G_pcszXCenterReal);

    // resolve WinSetDesktopWorkArea etc. (ctr_engine.c)
    arc = ctrpDesktopWorkareaSupported();

    // register XCenter plugins category (ctr_model.c)
    arc = ctrpRegisterCategory();

    // process the auto-open list of handles, if any
    memset(_aulAutoOpen, 0, sizeof(_aulAutoOpen));
    ctr = sizeof(buf);

    if (PrfQueryProfileData(HINI_USERPROFILE, (PSZ)INIAPP_XWORKPLACE,
                             (PSZ)INIKEY_XCENTERAUTOOPEN, buf, &ctr))
    {
        arc = FALSE;

        // the list is formatted as "[handle][space]...nul"
        for (ctr = 0, ptr = strtok(buf, " ");
             ctr < XCTR_MAX_AUTO && ptr;
             ctr++, ptr = strtok(NULL, " "))
        {
            handle = strtoul(ptr, &ptr, 16);
            if (handle >= 0x20000 && handle < 0x30000 && !*ptr)
                _aulAutoOpen[ctr] = handle;
            else
                arc = TRUE;
        }

        if (arc)
            _xwpclsSaveAutoOpen(somSelf);
    }
}

/*
 *@@ wpclsQueryStyle:
 *      prevent print.
 */

SOM_Scope ULONG  SOMLINK xctrM_wpclsQueryStyle(M_XCenter *somSelf)
{
    /* M_XCenterData *somThis = M_XCenterGetData(somSelf); */
    M_XCenterMethodDebug("M_XCenter","xctrM_wpclsQueryStyle");

    return (// M_XCenter_parent_M_WPAbstract_wpclsQueryStyle(somSelf)
            CLSSTYLE_NEVERPRINT);
                    // but allow templates
}

/*
 *@@ wpclsQueryTitle:
 *      this WPObject class method tells the WPS the clear
 *      name of a class, which is shown in the third column
 *      of a Details view and also used as the default title
 *      for new objects of a class.
 */

SOM_Scope PSZ  SOMLINK xctrM_wpclsQueryTitle(M_XCenter *somSelf)
{
    /* M_XCenterData *somThis = M_XCenterGetData(somSelf); */
    M_XCenterMethodDebug("M_XCenter","xctrM_wpclsQueryTitle");

    return ((PSZ)ENTITY_XCENTER);
}

/*
 *@@ wpclsQueryDefaultHelp:
 *      this WPObject class method returns the default help
 *      panel for objects of this class. This gets called
 *      from WPObject::wpQueryDefaultHelp if no instance
 *      help settings (HELPLIBRARY, HELPPANEL) have been
 *      set for an individual object. It is thus recommended
 *      to override this method instead of the instance
 *      method to change the default help panel for a class
 *      in order not to break instance help settings (fixed
 *      with 0.9.20).
 *
 *      We return the XCenter default help here.
 *
 *@@added V0.9.20 (2002-07-12) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xctrM_wpclsQueryDefaultHelp(M_XCenter *somSelf,
                                                    PULONG pHelpPanelId,
                                                    PSZ pszHelpLibrary)
{
    /* M_XCenterData *somThis = M_XCenterGetData(somSelf); */
    M_XCenterMethodDebug("M_XCenter","xctrM_wpclsQueryDefaultHelp");

    strcpy(pszHelpLibrary, cmnQueryHelpLibrary());
    *pHelpPanelId = ID_XSH_XCENTER_MAIN;
    return TRUE;
}

/*
 *@@ wpclsCreateDefaultTemplates:
 *      this WPObject class method is called by the
 *      Templates folder to allow a class to
 *      create its default templates.
 *
 *      The default WPS behavior is to create new templates
 *      if the class default title is different from the
 *      existing templates.
 *
 *@@added V0.9.7 (2001-01-11) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xctrM_wpclsCreateDefaultTemplates(M_XCenter *somSelf,
                                                          WPObject* Folder)
{
    /* M_XCenterData *somThis = M_XCenterGetData(somSelf); */
    M_XCenterMethodDebug("M_XCenter","xctrM_wpclsCreateDefaultTemplates");

    // pretend we've created the templates
    return TRUE;
}

/*
 *@@ wpclsQueryIconData:
 *      this WPObject class method must return information
 *      about how to build the default icon for objects
 *      of a class. This gets called from various other
 *      methods whenever a class default icon is needed;
 *      most importantly, M_WPObject::wpclsQueryIcon
 *      calls this to build a class default icon, which
 *      is then cached in the class's instance data.
 *      If a subclass wants to change a class default icon,
 *      it should always override _this_ method instead of
 *      wpclsQueryIcon.
 *
 *      Note that the default WPS implementation does not
 *      allow for specifying the ICON_FILE format here,
 *      which is why we have overridden
 *      M_XFldObject::wpclsQueryIcon too. This allows us
 *      to return icon _files_ for theming too. For details
 *      about the WPS's crappy icon management, refer to
 *      src\filesys\icons.c.
 *
 *      We override this to give XCenter object a new
 *      icon (src\shared\xcenter.ico).
 */

SOM_Scope ULONG  SOMLINK xctrM_wpclsQueryIconData(M_XCenter *somSelf,
                                                  PICONINFO pIconInfo)
{
    /* M_XCenterData *somThis = M_XCenterGetData(somSelf); */
    M_XCenterMethodDebug("M_XCenter","xctrM_wpclsQueryIconData");

    if (pIconInfo)
    {
        pIconInfo->fFormat = ICON_RESOURCE;
        pIconInfo->resid   = ID_ICONXCENTER;
        pIconInfo->hmod    = cmnQueryMainResModuleHandle();
    }

    return sizeof(ICONINFO);
}


