/* pane.c - This file contains the multi-pane handling routines.
 *
 * Copyright (c) 1991-, Microsoft Corporation.
 * All rights reserved.
 */


#include "packager.h"
#include <shellapi.h>
#include "dialogs.h"


//#define  OLESVR_SUPPORT           /* enable support for OLE server files */


#define DRAG_EMBED  2                   // Ctrl + Drag
#define DRAG_LINK   6                   // Ctrl + Shift + Drag


static HBRUSH hbrBlack;                 // Black brush
static HCURSOR hcurSplit;
static HWND hwndDesc;
static HWND hwndInsertIcon = NULL;
static HWND hwndView = NULL;
static INT cxBorder;                    // WS_BORDER border width
static INT cyBorder;
static INT cxFudge = 0;                 // Fudge factors for good appearance
static INT cyFudge = 0;
static INT cxMinWidth;
static INT cxView;
static INT cxSplit;                     // Splitter bar width
static INT cxPict;
static INT cxDesc;
static INT cxInsertIcon;
static INT cxMin[CCHILDREN];
static INT cyHeight;
static INT xSplit = 0;
static CHAR szButton[] = "button";
static CHAR szStatic[] = "static";
static CHAR szPaneClass[] = "PaneClass";
static CHAR szSubtitleClass[] = "SubTitleClass";
static CHAR szDescription[CBMESSAGEMAX];
static CHAR szView[CBMESSAGEMAX];
static CHAR szPicture[CBMESSAGEMAX];
static CHAR szInsertIcon[CBMESSAGEMAX];
static CHAR szDropFile[CBPATHMAX];
static BOOL fHScrollEnable = FALSE;
static BOOL fVScrollEnable = FALSE;


static BOOL MakeWindows(VOID);
static INT GetTextLen(HDC hdc, LPSTR lpstr);
static VOID RecalibrateScroll(INT iPane, DWORD lParam);
static VOID Undo(INT iPane);
static VOID CalcWindows(BOOL fFirst);
static INT Constrain(INT x, INT right);
static VOID CopyOther(VOID);


/* InitPaneClasses() - Do application "global" initialization.
 *
 * This function registers the window classes used by the application.
 * Returns:  TRUE if successful.
 */
BOOL
InitPaneClasses(
    VOID
    )
{
    WNDCLASS  wc;

    wc.style            = 0;
    wc.lpfnWndProc      = SubtitleWndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = 0;
    wc.hInstance        = ghInst;
    wc.hIcon            = LoadIcon(ghInst, MAKEINTRESOURCE(ID_APPLICATION));
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)(COLOR_3DFACE + 1);
    wc.lpszMenuName     = MAKEINTRESOURCE(ID_APPLICATION);
    wc.lpszClassName    = szSubtitleClass;

    if (!RegisterClass(&wc))
        return FALSE;

    wc.style            = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;
    wc.lpfnWndProc      = PaneWndProc;
    wc.cbClsExtra       = 0;
    // Reserve space for the item specific data handle
    wc.cbWndExtra       = sizeof(LPVOID);
    wc.hInstance        = ghInst;
    wc.hIcon            = NULL;
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)(COLOR_3DFACE + 1);
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = szPaneClass;

    if (!RegisterClass(&wc))
        return FALSE;

    return TRUE;
}



/* InitPanes() - Handles the instance-specific initialization.
 *
 * This function creates the main application window.
 * Returns:  TRUE if successful.
 */
BOOL
InitPanes(
    VOID
    )
{
    LOGFONT lf;
    CHARSETINFO csinfo;
    LCID lcid = GetThreadLocale();
    DWORD dwCp = GetACP();

    hbrBlack = GetStockObject(BLACK_BRUSH);
    hcurSplit = LoadCursor(ghInst, MAKEINTRESOURCE(SPLIT));
    gcxIcon = GetSystemMetrics(SM_CXICON);
    gcyIcon = GetSystemMetrics(SM_CYICON);

    SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
    SystemParametersInfo(SPI_ICONHORIZONTALSPACING, 0, &gcxArrange, FALSE);
    SystemParametersInfo(SPI_ICONVERTICALSPACING, 0, &gcyArrange, FALSE);

    // ANSI app needs to make sure it has the right charset for text rendering
    if (TranslateCharsetInfo(&dwCp, &csinfo, TCI_SRCCODEPAGE))
        lf.lfCharSet = (BYTE) csinfo.ciCharset;

    // Lock down font size to 8 point size since we won't adjust window size 
    lf.lfHeight = -MulDiv(8, giYppli, 72);
    lf.lfWidth = 0;

    ghfontTitle = CreateFontIndirect(&lf);
    if (PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_CHINESE ||
        PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_JAPANESE)
        lf.lfWeight = FW_NORMAL;
    else
        lf.lfWeight = FW_BOLD;
    ghfontChild = CreateFontIndirect(&lf);


    if (!(ghfontTitle || ghfontChild))
    {
        if (ghfontTitle)
            DeleteObject(ghfontTitle);

        return FALSE;
    }

    LoadString(ghInst, IDS_CONTENT, gszCaption[CONTENT], CBMESSAGEMAX);
    LoadString(ghInst, IDS_VIEW, szView, CBMESSAGEMAX);
    LoadString(ghInst, IDS_DESCRIPTION, szDescription, CBMESSAGEMAX);
    LoadString(ghInst, IDS_PICTURE, szPicture, CBMESSAGEMAX);
    LoadString(ghInst, IDS_APPEARANCE, gszCaption[APPEARANCE], CBMESSAGEMAX);
    LoadString(ghInst, IDS_INSERTICON, szInsertIcon, CBMESSAGEMAX);

    // Create the window panes
    if (!MakeWindows())
        return FALSE;

    CalcWindows(TRUE);

    // Give the focus to the content pane
    PostMessage(ghwndPane[CONTENT], WM_LBUTTONDOWN, 0, 0L);

    return TRUE;
}



/* EndPaneInstance() - Instance-specific termination code.
 */
VOID
EndPanes(
    VOID
    )
{
    if (ghfontTitle)
        DeleteObject(ghfontTitle);

    if (ghfontChild)
        DeleteObject(ghfontChild);
}



/* MakeWindows() - Make the window panes.
 */
static BOOL
MakeWindows(
    VOID
    )
{
    if (ghwndBar[CONTENT] =
        CreateWindow(szSubtitleClass, gszCaption[CONTENT], WS_CHILD | WS_VISIBLE,
        0, 0, 0, 0, ghwndFrame, NULL, ghInst, NULL))
    {

        hwndView = CreateWindow(szStatic, szView,
            WS_CHILD | WS_VISIBLE | SS_LEFT | SS_CENTERIMAGE,
            0, 0, 0, 0, ghwndBar[CONTENT], NULL, ghInst, NULL);

        hwndDesc = CreateWindow(szButton, szDescription,
            WS_CHILD | BS_AUTORADIOBUTTON | WS_VISIBLE | WS_GROUP,
            0, 0, 0, 0, ghwndBar[CONTENT], (HMENU)IDM_DESC, ghInst, NULL);

        ghwndPict = CreateWindow(szButton, szPicture,
            WS_CHILD | BS_AUTORADIOBUTTON | WS_VISIBLE,
            0, 0, 0, 0, ghwndBar[CONTENT], (HMENU)IDM_PICT, ghInst, NULL);


        if (hwndView && hwndDesc && ghwndPict)
        {
            // Use the appropriate dialog font
            SendMessage(ghwndBar[CONTENT], WM_SETFONT, (WPARAM)ghfontChild, TRUE);
            SendMessage(hwndView, WM_SETFONT, (WPARAM)ghfontChild, TRUE);
            SendMessage(hwndDesc, WM_SETFONT, (WPARAM)ghfontChild, TRUE);
            SendMessage(ghwndPict, WM_SETFONT, (WPARAM)ghfontChild, TRUE);
            CheckRadioButton(ghwndBar[CONTENT], IDM_PICT, IDM_DESC, IDM_DESC);
            EnableWindow(ghwndPict, FALSE);
        }
        else
        {
            goto Error;
        }
    }
    else
    {
        goto Error;
    }

    if (ghwndBar[APPEARANCE] =
        CreateWindow(szSubtitleClass, gszCaption[APPEARANCE],
        WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, ghwndFrame, NULL, ghInst, NULL))
    {
        hwndInsertIcon =
            CreateWindow(szButton, szInsertIcon,
            WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE,
            0, 0, 0, 0, ghwndBar[APPEARANCE], (HMENU)IDM_INSERTICON,
            ghInst, NULL);

        if (hwndInsertIcon)
        {
            SendMessage(ghwndBar[APPEARANCE], WM_SETFONT, (WPARAM)ghfontChild,
                 TRUE);
            SendMessage(hwndInsertIcon, WM_SETFONT, (WPARAM)ghfontChild, TRUE);
        }
        else
        {
            goto Error;
        }
    }
    else
    {
        goto Error;
    }

    ghwndPane[APPEARANCE] =
        CreateWindowEx(WS_EX_CLIENTEDGE, szPaneClass, NULL,
        WS_BORDER | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL,
        0, 0, 0, 0, ghwndFrame, NULL, ghInst, NULL);

    ghwndPane[CONTENT] =
        CreateWindowEx(WS_EX_CLIENTEDGE, szPaneClass, NULL,
        WS_BORDER | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL,
        0, 0, 0, 0, ghwndFrame, NULL, ghInst, NULL);

    if (!ghwndPane[APPEARANCE] || !ghwndPane[CONTENT])
        goto Error;

    EnableScrollBar(ghwndPane[APPEARANCE], SB_HORZ, ESB_DISABLE_BOTH);
    EnableScrollBar(ghwndPane[APPEARANCE], SB_VERT, ESB_DISABLE_BOTH);
    EnableScrollBar(ghwndPane[CONTENT], SB_HORZ, ESB_DISABLE_BOTH);
    EnableScrollBar(ghwndPane[CONTENT], SB_VERT, ESB_DISABLE_BOTH);

    DragAcceptFiles(ghwndPane[CONTENT], TRUE);

    return TRUE;

Error:
    if (ghwndBar[CONTENT])
    {
        if (hwndView)
            DestroyWindow(hwndView);

        if (hwndDesc)
            DestroyWindow(hwndDesc);

        if (ghwndPict)
            DestroyWindow(ghwndPict);

        DestroyWindow(ghwndBar[CONTENT]);
    }

    if (ghwndBar[APPEARANCE])
    {
        if (hwndInsertIcon)
            DestroyWindow(hwndInsertIcon);

        DestroyWindow(ghwndBar[APPEARANCE]);
    }

    if (ghwndPane[APPEARANCE])
        DestroyWindow(ghwndPane[APPEARANCE]);

    if (ghwndPane[CONTENT])
        DestroyWindow(ghwndPane[CONTENT]);

    return FALSE;
}



/* SubtitleWndProc() - "Appearance" and "Content" bar window procedure.
 */
LRESULT CALLBACK
SubtitleWndProc(
    HWND hWnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    PAINTSTRUCT ps;
    RECT rcCaption;
    INT iPane;

    iPane = (hWnd == ghwndBar[CONTENT]);

    switch (msg)
    {
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDM_INSERTICON:
                    Raise(APPEARANCE);
                    DeletePane(APPEARANCE, FALSE);

                    if (gptyUndo[APPEARANCE] != ICON)
                        glpobj[APPEARANCE] = IconCreateFromFile("");
                    else
                        glpobj[APPEARANCE] = IconClone(glpobjUndo[APPEARANCE]);

                    if (glpobj[APPEARANCE])
                        gpty[APPEARANCE] = ICON;

                    if (glpobj[APPEARANCE] && IconDialog(glpobj[APPEARANCE]))
                    {
                        InvalidateRect(ghwndPane[APPEARANCE], NULL, TRUE);
                        Dirty();
                    }
                    else
                    {
                        IconDelete(glpobj[APPEARANCE]);
                        gpty[APPEARANCE] = NOTHING;
                        glpobj[APPEARANCE] = NULL;
                        SendMessage(ghwndPane[APPEARANCE], WM_COMMAND,
                            IDM_UNDO, 0);
                    }

                    break;

                case IDM_DESC:
                    if (!IsDlgButtonChecked(ghwndBar[CONTENT], IDM_DESC))
                        CheckRadioButton(ghwndBar[CONTENT], IDM_PICT,
                            IDM_DESC, IDM_DESC);

                    if (fHScrollEnable)
                        EnableScrollBar(ghwndPane[iPane], SB_HORZ,
                            ESB_DISABLE_BOTH);

                    if (fVScrollEnable)
                        EnableScrollBar(ghwndPane[iPane], SB_VERT,
                            ESB_DISABLE_BOTH);

                    InvalidateRect(ghwndPane[CONTENT], NULL, TRUE);

                    goto defProcess;

                case IDM_PICT:
                    if (!IsDlgButtonChecked(ghwndBar[CONTENT], IDM_PICT)
                        && IsWindowEnabled(GetDlgItem(ghwndBar[CONTENT],
                        IDM_PICT)))
                        CheckRadioButton(ghwndBar[CONTENT], IDM_PICT,
                            IDM_DESC, IDM_PICT);

                    if (fHScrollEnable)
                        EnableScrollBar(ghwndPane[iPane], SB_HORZ,
                            ESB_ENABLE_BOTH);

                    if (fVScrollEnable)
                        EnableScrollBar(ghwndPane[iPane], SB_VERT,
                            ESB_ENABLE_BOTH);

                    InvalidateRect(ghwndPane[CONTENT], NULL, TRUE);
                    // Fall through

                default:
defProcess:
                    if (GetTopWindow(ghwndFrame) != ghwndPane[iPane])
                    {
                        if (gbDBCS)
                        {
                            /* 4-Oct-93 #2701 v-katsuy */
                             //win31#1203: 12/26/92:fixing Focus Line Scroll
                             //delete Focus Rect on another pane
                            InvalidateRect(ghwndPane[APPEARANCE], NULL, TRUE);
                        }
                        BringWindowToTop(ghwndPane[iPane]);
                        InvalidateRect(ghwndBar[APPEARANCE], NULL, TRUE);
                        InvalidateRect(ghwndBar[CONTENT], NULL, TRUE);
                        if (LOWORD(wParam) == IDM_PICT
                            || LOWORD(wParam) == IDM_DESC)
                            UpdateWindow(ghwndPane[CONTENT]);
                    }
            }

            break;

        case WM_LBUTTONDOWN:
            if (GetTopWindow(ghwndFrame) != ghwndPane[iPane])
                SendMessage(ghwndPane[iPane], WM_LBUTTONDOWN, 0, 0);

            break;

        case WM_PAINT:
            {
                HFONT hfontOld;

                GetClientRect(hWnd, &rcCaption);
                BeginPaint(hWnd, &ps);

                if (GetTopWindow(ghwndFrame) == ghwndPane[iPane])
                {
                    SetTextColor(ps.hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
                    SetBkColor(ps.hdc, GetSysColor(COLOR_HIGHLIGHT));
                }
                else
                {
                    SetTextColor(ps.hdc, GetSysColor(COLOR_WINDOWTEXT));
                    SetBkColor(ps.hdc, GetSysColor(COLOR_WINDOW));
                }

                hfontOld = SelectObject(ps.hdc, ghfontChild);
                rcCaption.left += cxFudge;
                DrawText(ps.hdc, gszCaption[iPane], -1, &rcCaption,
                    DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP);
                SelectObject(ps.hdc, hfontOld);

                EndPaint(hWnd, &ps);
            }

            break;

        case WM_SIZE:
            if (iPane == APPEARANCE)
            {
                if (hwndInsertIcon)
                {
                    GetClientRect(hWnd, &rcCaption);

                    SetWindowPos(hwndInsertIcon, 0,
                        rcCaption.right - cxInsertIcon, cyFudge, 0, 0,
                        SWP_NOSIZE | SWP_NOZORDER);

                    InvalidateRect(ghwndBar[APPEARANCE], NULL, TRUE);
                }
            }
            else
            {
                if (hwndView)
                {
                    BOOL bChinese = PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) == LANG_CHINESE;
                    GetClientRect(hWnd, &rcCaption);
                    SetWindowPos(hwndView, 0,
                                 bChinese ?
                                   rcCaption.right - cxDesc - cxPict - cxView - 15 :
                                   rcCaption.right - cxDesc - cxPict - cxView,
                                   0, 0, 0, SWP_NOSIZE | SWP_NOZORDER);

                    SetWindowPos(hwndDesc, 0,
                                 rcCaption.right - cxDesc - cxPict,
                                 cyFudge, 0, 0,
                                 SWP_NOSIZE | SWP_NOZORDER);

                    SetWindowPos(ghwndPict, 0,
                                 rcCaption.right - cxPict,
                                 cyFudge, 0, 0,
                                 SWP_NOSIZE | SWP_NOZORDER);

                    InvalidateRect(ghwndBar[CONTENT], NULL, TRUE);
                }
            }

            break;

        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }

    return 0L;
}



static INT
GetTextLen(
    HDC hdc,
    LPSTR lpstr
    )
{
    SIZE Size;

    GetTextExtentPoint32(hdc, lpstr, lstrlen(lpstr), &Size);

    return Size.cx + (cxFudge * 2);
}



LRESULT CALLBACK
PaneWndProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    BOOL fFocus;
    LPVOID lpobjTemp;
    PAINTSTRUCT ps;
    RECT rc;
    CHAR szFile[CBPATHMAX];
    INT iOld;
    INT iPane;
    INT iPos;
    INT Max;
    INT Min;
    INT nBar;

    iPane = (hwnd == ghwndPane[CONTENT]);

    switch (msg)
    {
        case WM_HSCROLL:
        case WM_VSCROLL:
            // If not the content pane in picture mode, break
            if (gpty[iPane] == PICTURE
                && iPane == CONTENT
                && !IsDlgButtonChecked(ghwndBar[CONTENT], IDM_PICT))
                break;

            // Can't scroll anything but cmd line and picture
            if (gpty[iPane] != PICTURE && gpty[iPane] != CMDLINK)
                break;

            nBar = (msg == WM_HSCROLL ? SB_HORZ : SB_VERT);
            iOld = iPos = GetScrollPos(hwnd, nBar);

            switch (LOWORD(wParam))
            {
                case SB_LINEUP:
                    iPos--;
                    break;

                case SB_LINEDOWN:
                    iPos++;
                    break;

                case SB_PAGEUP:
                case SB_PAGEDOWN:
                    GetClientRect(hwnd, &rc);
                    if (LOWORD(wParam) == SB_PAGEUP)
                        iPos -= (rc.bottom - rc.top + 1);
                    else
                        iPos += (rc.bottom - rc.top + 1);

                    break;

                case SB_THUMBPOSITION:
                    iPos = (INT)HIWORD(wParam);
                    break;
            }

            // Make sure that iPos is in the range
            GetScrollRange(hwnd, nBar, &Min, &Max);

            if (iPos < Min)
                iPos = Min;
            if (iPos > Max)
                iPos = Max;

            SetScrollPos(hwnd, nBar, iPos, TRUE);

            if (msg == WM_HSCROLL)
                ScrollWindow(hwnd, iOld - iPos, 0, NULL, NULL);
            else
                ScrollWindow(hwnd, 0, iOld - iPos, NULL, NULL);

            UpdateWindow(hwnd);
            break;

        case WM_LBUTTONDOWN:
            if (GetTopWindow(ghwndFrame) != hwnd)
            {
                BringWindowToTop(hwnd);
                InvalidateRect(ghwndBar[APPEARANCE], NULL, TRUE);
                InvalidateRect(ghwndBar[CONTENT], NULL, TRUE);
                InvalidateRect(ghwndPane[APPEARANCE], NULL, TRUE);
                InvalidateRect(ghwndPane[CONTENT], NULL, TRUE);
            }

            break;

        case WM_PAINT:
            GetClientRect(hwnd, &rc);
            BeginPaint(hwnd, &ps);
            if (fFocus = (ghwndPane[iPane] == GetTopWindow(ghwndFrame)))
            {
                SetTextColor(ps.hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
                SetBkColor(ps.hdc, GetSysColor(COLOR_HIGHLIGHT));
            }
            else
            {
                SetTextColor(ps.hdc, GetSysColor(COLOR_WINDOWTEXT));
                SetBkColor(ps.hdc, GetSysColor(COLOR_WINDOW));
            }

            switch (gpty[iPane])
            {
                case CMDLINK:
                    CmlDraw(glpobj[iPane], ps.hdc, &rc,
                        GetScrollPos(hwnd, SB_HORZ), fFocus);
                    break;

                case PEMBED:
                    EmbDraw(glpobj[iPane], ps.hdc, &rc, fFocus);
                    break;

                case ICON:
                    IconDraw(glpobj[iPane], ps.hdc, &rc, fFocus, 0, 0);
                    break;

                case PICTURE:
                    PicDraw(glpobj[iPane], ps.hdc, &rc,
                        GetScrollPos(hwnd, SB_HORZ),
                        GetScrollPos(hwnd, SB_VERT),
                        hwnd == ghwndPane[APPEARANCE] ||
                        IsDlgButtonChecked(ghwndBar[CONTENT], IDM_PICT), fFocus);
                    break;

                default:
                    FillRect(ps.hdc, &rc, ghbrBackground);
                    break;
            }

            EndPaint(hwnd, &ps);
            break;

        case WM_FIXSCROLL:
            GetClientRect(hwnd, &rc);
            lParam = ((DWORD)rc.bottom << 16) | (DWORD)rc.right;

            // Fall through

        case WM_SIZE:
            if (gpty[iPane] == PICTURE || gpty[iPane] == CMDLINK)
                RecalibrateScroll(iPane, (DWORD)lParam);

            break;

        case WM_DESTROY:
            DeletePane(iPane, TRUE);
            break;

        case WM_DROPFILES:
            {
                BYTE bKeyState = 0;

                // Retrieve the file name
                DragQueryFile((HANDLE)wParam, 0, szDropFile, CBPATHMAX);

                DragFinish((HANDLE)wParam);

                // We got dropped on, so bring ourselves to the top
                BringWindowToTop(ghwndFrame);
                BringWindowToTop(hwnd);

                // See what the user wants us to do
                bKeyState = ((GetKeyState(VK_SHIFT) < 0) << 2)
                    | ((GetKeyState(VK_CONTROL) < 0) << 1)
                    | ((GetKeyState(VK_MENU) < 0));

                switch (bKeyState)
                {
                    case DRAG_LINK:
                        PostMessage(hwnd, WM_COMMAND, IDM_LINKFILE, 0L);
                        break;

                    case DRAG_EMBED:
                    default:
                        PostMessage(hwnd, WM_COMMAND, IDM_EMBEDFILE, 0L);
                        break;
                }

                break;
            }

        case WM_LBUTTONDBLCLK:
            // Alt + Double Click = Properties
            if (gpty[iPane] == PICTURE && GetKeyState(VK_MENU) < 0)
            {
                wParam = IDM_LINKS;
            }
            else
            {
                if (gpty[iPane] == PEMBED)
                {
                    //
                    // If the server is a OLE server, we want to activate in
                    // OLE fashion. But from users perspective it should not
                    // look like an object. So for non-objects double-click
                    // implies show the server. We should try to do the same
                    // thing while editing ole server files.
                    //
                    wParam = IDD_EDIT;
                }
                else
                {
                    wParam  = IDD_PLAY;
                }
            }

            msg = WM_COMMAND;
            lParam = 0;

            // Fall through

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDM_COPY:
                case IDM_CUT:
                    switch (gpty[iPane])
                    {
                        case PICTURE:
                            PicCopy(glpobj[iPane]);

                        default:
                            if (iPane == APPEARANCE)
                                CopyOther();
                            break;
                    }

                    if (LOWORD(wParam) == IDM_COPY)
                        break;

                    // Fall through to delete the selection

                case IDM_CLEAR:
                    DeletePane(iPane, FALSE);
                    break;

                case IDM_LINKS:
                    {
                        LONG objtype;

                        OleQueryType(((LPPICT)glpobj[iPane])->lpObject, &objtype);
                        if (objtype == OT_LINK)
                            DialogBoxAfterBlock(MAKEINTRESOURCE(DTPROP),
                                ghwndPane[iPane], fnProperties);

                        break;
                    }
                case IDM_LINKFILE:
                    if(SUCCEEDED(StringCchCopy(szFile, ARRAYSIZE(szFile), szDropFile)))
                    {
                        wParam = IDM_PASTELINK;
                        goto CreateFromFile;
                    }
                 
                    break;

                case IDM_EMBEDFILE:
                    if(SUCCEEDED(StringCchCopy(szFile, ARRAYSIZE(szFile), szDropFile)))
                    {
                        wParam = IDM_PASTE;
                        goto CreateFromFile;
                    }
                    break;

                case IDM_PASTE:
                case IDM_PASTELINK:
                    // Try to paste a file name from the File Manager
                    if (iPane == CONTENT)
                    {
                        HANDLE hdata;
                        LPSTR lpstrFile;

                        if (IsClipboardFormatAvailable(gcfFileName))
                        {
                            if (!OpenClipboard(ghwndFrame))
                                break;

                            if (!(hdata = GetClipboardData(gcfFileName)) || !(lpstrFile =
                                GlobalLock(hdata)))
                            {
                                CloseClipboard();
                                break;
                            }

                            StringCchCopy(szFile, ARRAYSIZE(szFile), lpstrFile);
                            GlobalUnlock(hdata);
                            CloseClipboard();

CreateFromFile:

#ifdef OLESVR_SUPPORT
                            if (IsOleServerDoc (szFile))
                            {
                                lpobjTemp = PicFromFile((wParam == IDM_PASTE),
                                    szFile);
                                if (!lpobjTemp)
                                {
                                    ErrorMessage(E_GET_FROM_CLIPBOARD_FAILED);
                                    break;
                                }

                                goto StuffNewObject;
                            }
                            else
                            {
#endif
                                DeletePane(CONTENT, FALSE);
                                if (wParam == IDM_PASTE)
                                {
                                    if (glpobj[CONTENT] = EmbCreate(szFile))
                                        gpty[CONTENT] = PEMBED;
                                }
                                else
                                {
                                    if (glpobj[CONTENT] =
                                           CmlCreateFromFilename(szFile, TRUE))
                                        gpty[CONTENT] = CMDLINK;
                                }
#ifdef OLESVR_SUPPORT
                            }
#endif
                            InvalidateRect(ghwndPane[CONTENT], NULL, TRUE);
                            Dirty();

                            if (!gpty[APPEARANCE])
                            {
                                if (glpobj[APPEARANCE] =
                                    IconCreateFromFile(szFile))
                                {
                                    gpty[APPEARANCE] = ICON;
                                    InvalidateRect(ghwndPane[APPEARANCE],
                                        NULL, TRUE);
                                }
                            }

                            break;
                        }
                    }

                    // Not a file name, try to paste an OLE object
                    if (!(lpobjTemp = PicPaste(LOWORD(wParam) == IDM_PASTE,
                                                gszCaption[iPane])))
                    {
                        ErrorMessage(E_GET_FROM_CLIPBOARD_FAILED);
                        break;
                    }
#ifdef OLESVR_SUPPORT

StuffNewObject:

#endif
                    DeletePane(iPane, FALSE);
                    glpobj[iPane] = lpobjTemp;
                    gpty[iPane] = PICTURE;
                    SendMessage(ghwndPane[iPane], WM_FIXSCROLL, 0, 0L);
                    InvalidateRect(ghwndPane[iPane], NULL, TRUE);
                    Dirty();

                    if (iPane == CONTENT)
                    {
                        EnableWindow(ghwndPict, TRUE);

                        if (!gpty[APPEARANCE])
                        {
                            if (glpobj[APPEARANCE] = IconCreateFromObject(
                                ((LPPICT)glpobj[iPane])->lpObject))
                            {
                                gpty[APPEARANCE] = ICON;
                                InvalidateRect(ghwndPane[APPEARANCE], NULL, TRUE);
                            }
                        }
                    }

                    Dirty();
                    break;

                case IDD_EDIT:          /* Edit the icon form */
                case IDD_PLAY:
                    switch (gpty[iPane])
                    {
                        case CMDLINK:
                            CmlActivate(glpobj[iPane]);
                            break;

                        case PEMBED:
                            EmbActivate(glpobj[iPane], LOWORD(wParam));
                            break;

                        case PICTURE:
                            PicActivate(glpobj[iPane], LOWORD(wParam));
                            break;

                        default:
                            break;
                    }

                    break;

                case IDD_UPDATE:        /* Update the (link) object */
                    if (gpty[iPane] == PICTURE)
                        PicUpdate(glpobj[iPane]);

                    break;

                case IDD_FREEZE:        /* Make the object static */
                    if (gpty[iPane] == PICTURE)
                        PicFreeze(glpobj[iPane]);

                    break;

                case IDD_CHANGE:
                    if (gpty[iPane] == PICTURE)
                        PicChangeLink(glpobj[iPane]);

                    break;

                case IDM_UNDO:
                    Undo(iPane);
                    break;

                case IDD_AUTO:          /* Change the (link) update options */
                case IDD_MANUAL:
                    if (gpty[iPane] == PICTURE
                        && !PicSetUpdateOptions(glpobj[iPane], LOWORD(wParam)))
                        break;

                case IDM_LINKDONE:      /* The link update has completed */
                    PostMessage(ghwndError, WM_REDRAW, 0, 0L);
                    break;

                default:
                    break;
            }

            break;

        default:
            return (DefWindowProc(hwnd, msg, wParam, lParam));
    }

    return 0;
}



LRESULT CALLBACK
SplitterFrame(
    HWND hWnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    RECT rc;

    switch (msg)
    {
        case WM_SIZE:
            if (wParam != SIZEICONIC && ghwndBar[APPEARANCE])
            {
                GetClientRect(hWnd, &rc);

                // Make sure the splitter bar is still valid
                xSplit = Constrain(xSplit, rc.right);
                CalcWindows(FALSE);

                // Invalidate the splitter bar, forcing a repaint
                rc.left = xSplit - cxSplit / 2 - cxBorder;
                rc.right = xSplit + cxSplit / 2 + cxBorder;
                InvalidateRect(hWnd, &rc, TRUE);
            }

            break;

        case WM_PAINT:
            {
                PAINTSTRUCT ps;
                RECT rcBlack;

                BeginPaint(hWnd, &ps);
                GetClientRect(hWnd, &rc);

                SetRect(&rcBlack, xSplit - cxSplit / 2 - cxBorder,
                    rc.top, xSplit + cxSplit / 2 + cxBorder,
                    rc.top + cyHeight + cyBorder);
                FillRect(ps.hdc, &rcBlack, hbrBlack);

                SetRect(&rcBlack, xSplit - cxSplit / 2 - cxBorder,
                    rc.bottom - GetSystemMetrics(SM_CYHSCROLL) + 1,
                    xSplit + cxSplit / 2 + cxBorder,
                    rc.bottom);
                FillRect(ps.hdc, &rcBlack, hbrBlack);

                EndPaint(hWnd, &ps);
                break;
            }

        case WM_GETMINMAXINFO:
            {
                LPPOINT rgpt = (LPPOINT)lParam;

                rgpt[3].x = cxMinWidth;
                rgpt[3].y = cyHeight * 6;
                break;
            }

        case WM_LBUTTONDOWN:
            {
                MSG msg1;
                INT x;
                INT y;
                INT dy;
                HDC hdc;
                HCURSOR hcurOld;

                if (IsIconic(hWnd))
                    break;

                x  = LOWORD(lParam);
                GetClientRect(hWnd, &rc);
                y  = 0;
                dy = rc.bottom;

                // Constrain the splitter bar...
                x = Constrain(x, rc.right);
                hdc = GetDC(hWnd);

                // split bar loop
                PatBlt(hdc, x - cxSplit / 2, y, cxSplit, dy, PATINVERT);
                SetCapture(hWnd);
                hcurOld = SetCursor(hcurSplit);

                while (GetMessage(&msg1, NULL, 0, 0))
                {
                    if (msg1.message >= WM_MOUSEFIRST
                        && msg1.message <= WM_MOUSELAST)
                    {
                        if (msg1.message == WM_LBUTTONUP
                            || msg1.message == WM_LBUTTONDOWN)
                            break;

                        if (msg1.message == WM_MOUSEMOVE)
                        {
                            ScreenToClient(hWnd, &msg1.pt);
                            x = Constrain(x, rc.right);

                            // erase old
                            PatBlt(hdc, x - cxSplit / 2, y, cxSplit, dy,
                                PATINVERT);

                            // put down new
                            x = Constrain(msg1.pt.x, rc.right);
                            PatBlt(hdc, x - cxSplit / 2, y, cxSplit, dy,
                                PATINVERT);
                        }
                    }
                    else
                    {
                        DispatchMessage(&msg1);
                    }
                }

                SetCursor(hcurOld);
                ReleaseCapture();

                // Constrain the splitter bar...
                x = Constrain(x, rc.right);

                // erase old
                PatBlt(hdc, x - cxSplit / 2, y, cxSplit, dy, PATINVERT);

                ReleaseDC(hWnd, hdc);

                if (msg1.wParam != VK_ESCAPE)
                {
                    xSplit = x;
                    CalcWindows(FALSE);
                    InvalidateRect(ghwndBar[APPEARANCE], NULL, TRUE);
                    InvalidateRect(ghwndBar[CONTENT], NULL, TRUE);
                }

                break;
            }

        default:
            return FALSE;
    }

    return TRUE;
}



VOID
DeletePane(
    INT iPane,
    BOOL fDeleteUndo
    )
{
    // Delete the last Undo object
    if (glpobjUndo[iPane])
        DeletePaneObject(glpobjUndo[iPane], gptyUndo[iPane]);

    // If we don't wish to keep an undo, delete the object too!
    if (fDeleteUndo)
    {
        DeletePaneObject(glpobj[iPane], gpty[iPane]);
        gptyUndo[iPane] = NOTHING;
        glpobjUndo[iPane] = NULL;
    }
    else
    {
        gptyUndo[iPane] = gpty[iPane];
        glpobjUndo[iPane] = glpobj[iPane];
    }

    // Handle the buttons and such
    if (gpty[iPane] == PICTURE || gpty[iPane] == CMDLINK)
    {
        CHAR szUndoName[CBMESSAGEMAX];

        EnableScrollBar(ghwndPane[iPane], SB_HORZ, ESB_DISABLE_BOTH);
        EnableScrollBar(ghwndPane[iPane], SB_VERT, ESB_DISABLE_BOTH);

        if (gpty[iPane] == PICTURE)
        {
            if (iPane == CONTENT)
            {
                CheckRadioButton(ghwndBar[CONTENT], IDM_PICT, IDM_DESC, IDM_DESC);
                EnableWindow(ghwndPict, FALSE);
            }

            // If the Undo object isn't deleted already, rename it
            if (!fDeleteUndo)
            {
                if(SUCCEEDED(StringCchPrintf(szUndoName, ARRAYSIZE(szUndoName), szUndo, gszCaption[iPane])))
                {
                    OleRename(((LPPICT)glpobjUndo[iPane])->lpObject, szUndoName);
                }
            }
        }
    }

    glpobj[iPane] = NULL;
    gpty[iPane]   = NOTHING;

    if (IsWindow(ghwndPane[iPane]))
        InvalidateRect(ghwndPane[iPane], NULL, TRUE);
}



static VOID
RecalibrateScroll(
    INT iPane,
    DWORD lParam
    )
{
    INT cxDel;
    INT cyDel;
    BOOL bDesc = FALSE;
    LPPICT lppict = (LPPICT)glpobj[iPane];

    // Compute the amount of scrolling possible
    cxDel = lppict->rc.right - lppict->rc.left - (INT)(lParam & 0xffff);
    cyDel = lppict->rc.bottom - lppict->rc.top - (INT)(lParam >> 16);

    // Normalize the scroll bar lengths
    if (cxDel < 0)
        cxDel = 0;

    if (cyDel < 0)
        cyDel = 0;

    if (iPane == CONTENT)
    {
        bDesc = IsDlgButtonChecked(ghwndBar[iPane], IDM_DESC);
        fHScrollEnable = cxDel;
        fVScrollEnable = cyDel;
    }

    EnableScrollBar(ghwndPane[iPane], SB_HORZ,
        (cxDel && !bDesc) ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH);

    EnableScrollBar(ghwndPane[iPane], SB_VERT,
        (cyDel && !bDesc) ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH);

    // Ensure that the thumb is at a meaningful position
    if (GetScrollPos(ghwndPane[iPane], SB_HORZ) > cxDel)
        SetScrollPos(ghwndPane[iPane], SB_HORZ, cxDel, TRUE);

    if (GetScrollPos(ghwndPane[iPane], SB_VERT) > cyDel)
        SetScrollPos(ghwndPane[iPane], SB_VERT, cyDel, TRUE);
}



static VOID
Undo(
    INT iPane
    )
{
    DWORD ot;
    LPPICT lppict;
    LPVOID lpobjTemp;
    INT ptyTemp;

    if (gpty[iPane] == PICTURE)
    {
        lppict = glpobj[iPane];

        // Close the old object
        if (lppict->lpObject)
        {
            OleQueryType(lppict->lpObject, &ot);
            if (ot != OT_STATIC)
                Error(OleClose(lppict->lpObject));
        }

        OleRename(lppict->lpObject, gszTemp);
    }

    if (gptyUndo[iPane] == PICTURE)
    {
        lppict = glpobjUndo[iPane];

        // Try to reconnect the new object if it's a link
        if (lppict->lpObject)
        {
            OleQueryType(lppict->lpObject, &ot);
            if (ot == OT_LINK && Error(OleReconnect(lppict->lpObject)))
                ErrorMessage(E_FAILED_TO_RECONNECT_OBJECT);
        }

        OleRename(lppict->lpObject, gszCaption[iPane]);
    }

    if (gpty[iPane] == PICTURE)
    {
        CHAR szUndoName[CBMESSAGEMAX];

        lppict = glpobj[iPane];
        if(SUCCEEDED(StringCchPrintf(szUndoName, ARRAYSIZE(szUndoName), szUndo, gszCaption[iPane])))
        {
            OleRename(lppict->lpObject, szUndoName);
        }
    }

    // Handle the buttons and enable/disable scroll bars

    // Going from picture to non-picture, disable all special things
    if (gptyUndo[iPane] != PICTURE && gpty[iPane] == PICTURE)
    {
        if (iPane == CONTENT)
        {
            CheckRadioButton(ghwndBar[CONTENT], IDM_PICT, IDM_DESC, IDM_DESC);
            EnableWindow(ghwndPict, FALSE);
        }

        EnableScrollBar(ghwndPane[iPane], SB_HORZ, ESB_DISABLE_BOTH);
        EnableScrollBar(ghwndPane[iPane], SB_VERT, ESB_DISABLE_BOTH);
    }

    if (gptyUndo[iPane] == PICTURE || gptyUndo[iPane] == CMDLINK)
    {
        SendMessage(ghwndPane[iPane], WM_FIXSCROLL, 0, 0L);

        if (gptyUndo[iPane] == PICTURE)
        {
            // Going from non-picture to picture, enable button
            if (gpty[iPane] != PICTURE && iPane == CONTENT)
                EnableWindow(ghwndPict, TRUE);
        }
    }

    lpobjTemp = glpobj[iPane];
    glpobj[iPane] = glpobjUndo[iPane];
    glpobjUndo[iPane] = lpobjTemp;

    ptyTemp = gpty[iPane];
    gpty[iPane] = gptyUndo[iPane];
    gptyUndo[iPane] = ptyTemp;

    InvalidateRect(ghwndPane[iPane], NULL, TRUE);
    Dirty();
}



static VOID
CalcWindows(
    BOOL fFirst
    )
{
    if (fFirst)
    {
        // Figure out the length of the text strings, and all dimensions
        HDC hdc = GetWindowDC(ghwndFrame);
        if (hdc)
        {
            cxBorder = GetSystemMetrics(SM_CXBORDER);
            cyBorder = GetSystemMetrics(SM_CYBORDER);
            cxFudge = cxBorder * 2;
            cyFudge = cyBorder * 2;
            cxSplit = cxBorder * 4;

            if (gbDBCS)
            {
                /* #3963 13-Dec-93 v-katsuy */
                /* ORIGINALBUG! Window width calculated for just "&Picture".
                 *  This width should be calculate
                 *  [Radiobutton] + [Text](not include '&').
                 */
                CHAR  szTemp[CBMESSAGEMAX];
                LPSTR lpText, lpTemp;

                for (lpText = szPicture, lpTemp = szTemp; *lpText; ) {
                    if (*lpText == '&')
                        lpText++;
                    else
                        *lpTemp++ = *lpText++;
                }
                *lpTemp = 0;
                cxPict = GetTextLen(hdc, szTemp) + cxFudge * 2
                       + GetSystemMetrics(SM_CXSIZE); // for radiobutton 

                for (lpText = szDescription, lpTemp = szTemp; *lpText; ) {
                    if (*lpText == '&')
                        lpText++;
                    else
                        *lpTemp++ = *lpText++;
                }
                *lpTemp = 0;
                cxDesc = GetTextLen(hdc, szTemp)
                       + GetSystemMetrics(SM_CXSIZE); // for radiobutton
            }
            else
            {
                cxPict = GetTextLen(hdc, szPicture) + cxFudge * 2;
                cxDesc = GetTextLen(hdc, szDescription);
            }

            cxView = GetTextLen(hdc, szView);
            cxInsertIcon = GetTextLen(hdc, szInsertIcon) + cxFudge;

            cxMin[CONTENT] = cxPict + cxDesc + cxView +
                GetTextLen(hdc, gszCaption[CONTENT]) + cxFudge;
            cxMin[APPEARANCE] = cxInsertIcon +
                GetTextLen(hdc, gszCaption[APPEARANCE]) + cxFudge;
            cyHeight = GetSystemMetrics(SM_CYMENU) + cyFudge * 2;
            ReleaseDC(ghwndFrame, hdc);

            cxMinWidth = cxMin[APPEARANCE] + cxMin[CONTENT] + cxSplit +
                GetSystemMetrics(SM_CXFRAME) + cxFudge;

            // Compute all the window sizes that we can
            SetWindowPos(ghwndFrame, 0, 0, 0,
                cxMinWidth + cxFudge * 20,
                cxMinWidth * 7 / 18,
                SWP_NOMOVE | SWP_NOZORDER);
            SetWindowPos(hwndInsertIcon, 0, 0, 0,
                cxInsertIcon - cxFudge,
                GetSystemMetrics(SM_CYMENU),
                SWP_NOMOVE | SWP_NOZORDER);
            SetWindowPos(hwndView, 0, 0, 0,
                cxView, cyHeight - cyFudge,
                SWP_NOMOVE | SWP_NOZORDER);
            SetWindowPos(ghwndPict, 0, 0, 0,
                (cxPict - cxFudge) << 1,
                GetSystemMetrics(SM_CYMENU),
                SWP_NOMOVE | SWP_NOZORDER);
            SetWindowPos(hwndDesc, 0, 0, 0,
                cxDesc, GetSystemMetrics(SM_CYMENU),
                SWP_NOMOVE | SWP_NOZORDER);
        }
    }
    else
    {
        RECT rc;

        GetClientRect(ghwndFrame, &rc);

        // Move the windows to the appropriate locations
        SetWindowPos(ghwndBar[APPEARANCE], 0, 0, 0,
            xSplit - cxSplit / 2 - cxBorder, cyHeight, SWP_NOZORDER);

        SetWindowPos(ghwndBar[CONTENT], 0,
            xSplit + cxSplit / 2 + cxBorder,
            0,
            rc.right - (xSplit + cxSplit / 2) + 1 - cxBorder,
            cyHeight,
            SWP_NOZORDER);

        SetWindowPos(ghwndPane[APPEARANCE], 0,
            -cxBorder,
            cyHeight,
            cxBorder + xSplit - cxSplit / 2,
            rc.bottom + cyBorder - cyHeight,
            SWP_NOZORDER);

        SetWindowPos(ghwndPane[CONTENT], 0,
            xSplit + cxSplit / 2,
            cyHeight,
            cxBorder + rc.right - (xSplit + cxSplit / 2) + 1,
            rc.bottom + cyBorder - cyHeight,
            SWP_NOZORDER);
    }
}



static INT
Constrain(
    INT x,
    INT right
    )
{
    // Constrain the splitter bar...
    if (x < cxMin[APPEARANCE] + cxSplit / 2 - 1)
        return cxMin[APPEARANCE] + cxSplit / 2 - 1;
    else if (x > (right - cxMin[CONTENT] - cxSplit / 2 + 1))
        return right - cxMin[CONTENT] - cxSplit / 2 + 1;

    return x;
}



/* CopyOther() - Copies the picture in appearance pane
 *
 * Returns:  none
 */
static VOID
CopyOther(
    VOID
    )
{
    HANDLE hdata;

    if (OpenClipboard(ghwndFrame))
    {
        Hourglass(TRUE);
        EmptyClipboard();

        if (hdata = GetMF())
            SetClipboardData(CF_METAFILEPICT, hdata);

        CloseClipboard();
        Hourglass(FALSE);
    }
}



VOID
DeletePaneObject(
    LPVOID lpobj,
    INT objType
    )
{
    switch (objType)
    {
        case CMDLINK:
            CmlDelete(lpobj);
            break;

        case PEMBED:
            EmbDelete(lpobj);
            break;

        case ICON:
            IconDelete(lpobj);
            break;

        case PICTURE:
            PicDelete(lpobj);
            break;
    }
}
