    /***************************************************************************
     *                                                                         *
     *  MODULE      : ColorWP.c                                                *
     *                                                                         *
     *  DESCRIPTION : Window function for the colors window and related fns.   *
     *                                                                         *
     *  FUNCTIONS   : ColorWP ()            - Window function for colors       *
     *                                        window.                          *
     *                                                                         *
     *                ComputeInverseColor() - Gets the inverse RGB of a given  *
     *                                        RGB value                        *
     *                                                                         *
     *  HISTORY     : 6/21/89 - adapted from pBrush - LR                       *
     *                                                                         *
     ***************************************************************************/
    
    #include "imagedit.h"
    #include "dialogs.h"

	 #include <windowsx.h>
    #include <commdlg.h>
    
    
    STATICFN VOID NEAR ColorInit(HWND hwnd);
    STATICFN VOID NEAR ColorProcessCommand(HWND hwnd, INT idCtrl, INT NotifyCode);
    STATICFN VOID NEAR ColorBoxPaint(HDC hdc);
    STATICFN VOID NEAR DrawColorRect(HDC hdc, DWORD rgb, INT x, INT y,
        INT cx, INT cy, HDC hdcMem, BOOL fMonoOK);
    STATICFN VOID NEAR MyRectangle(HDC hdc, INT left, INT top, INT right,
        INT bottom, HDC hdcMem, BOOL fMonoOK);
    STATICFN VOID NEAR ColorBoxClicked(UINT msg, PPOINT ppt);
    STATICFN BOOL NEAR ColorBoxHitTest(PPOINT ppt, PINT piColor, PINT pfMode);
    STATICFN VOID NEAR ColorLRPaint(HWND hwnd, HDC hdc);
    STATICFN VOID NEAR ColorLRDrawSamples(HDC hdc, PRECT prc, BOOL fLeft);
    STATICFN VOID NEAR ColorLRUpdate(BOOL fLeft);
    STATICFN VOID NEAR ColorEdit(VOID);
    STATICFN VOID NEAR SetLeftColor(INT iColor, INT iMode);
    STATICFN VOID NEAR SetRightColor(INT iColor, INT iMode);
    STATICFN HBRUSH NEAR MyCreateSolidBrush(DWORD rgb);
    STATICFN DWORD NEAR MyGetNearestColor(DWORD rgb, BOOL fMonoOK);
    STATICFN DWORD NEAR ComputeInverseColor(DWORD rgb);
    
    /*
     * Width/height of a single color square.
     */
    static INT gcxColorBox;
    
    /*
     * Vertical offset within the color box control to where to start the
     * top row of color squares (the color squares are vertically centered
     * within the color box control).
     */
    static INT gyColorBoxStart;
    
    /*
     * Number of colors and image type.  These globals are used by the
     * the color palette routines to know what mode the color palette
     * is in.
     */
    static INT gnColorPalColors;
    static INT giColorPalType;
    
    
    
    /****************************************************************************
    * ColorShow
    *
    * This function shows or hides the color palette.
    *
    * History:
    *
    ****************************************************************************/
    
    VOID ColorShow(
        BOOL fShow)
    {
        if (fShow)
            ShowWindow(ghwndColor, SW_SHOWNA);
        else
            ShowWindow(ghwndColor, SW_HIDE);
    }
    
    
    
    /************************************************************************
    * ColorDlgProc
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    DIALOGPROC ColorDlgProc(
        HWND hwnd,
        UINT msg,
        WPARAM wParam,
        LPARAM lParam)
    {
        switch (msg) {
            case WM_INITDIALOG:
                ColorInit(hwnd);
    
                /*
                 * Return TRUE so that the dialog manager does NOT set the focus
                 * for me.  This prevents the status window from initially having
                 * the focus when the editor is started.
                 */
                return TRUE;
    
            case WM_ACTIVATE:
                if (GET_WM_ACTIVATE_STATE(wParam, lParam))
                    gidCurrentDlg = DID_COLOR;
    
                break;
    
            case WM_CTLCOLORBTN:
            case WM_CTLCOLORDLG:
            case WM_CTLCOLORSTATIC:
                switch (GET_WM_CTLCOLOR_TYPE(wParam, lParam, msg)) {
                    case CTLCOLOR_BTN:
                    case CTLCOLOR_DLG:
                        return (BOOL)GetStockObject(LTGRAY_BRUSH);
    
                    case CTLCOLOR_STATIC:
                        SetBkColor(GET_WM_CTLCOLOR_HDC(wParam, lParam, msg),
                                RGB_LIGHTGRAY);
                        return (BOOL)GetStockObject(LTGRAY_BRUSH);
                }
    
                return (BOOL)NULL;
    
            case  WM_PAINT:
                {
                    HDC hdc;
                    PAINTSTRUCT ps;
    
                    hdc = BeginPaint(hwnd, &ps);
                    DrawMarginBorder(hwnd, hdc);
                    EndPaint(hwnd, &ps);
                }
    
                break;
    
            case WM_COMMAND:
                ColorProcessCommand(hwnd,
                        GET_WM_COMMAND_ID(wParam, lParam),
                        GET_WM_COMMAND_CMD(wParam, lParam));
                break;
    
            case WM_CLOSE:
                /*
                 * The user closed the color palette from the system menu.
                 * Hide the window (we don't actually destroy it so
                 * that it will appear in the same spot when they show
                 * it again).
                 */
                ColorShow(FALSE);
                gfShowColor = FALSE;
                break;
    
            case WM_DESTROY:
                {
                    RECT rc;
    
                    /*
                     * Save the position of the color palette.
                     */
                    GetWindowRect(hwnd, &rc);
                    WriteWindowPos(&rc, FALSE, szColorPos);
    
                    /*
                     * Null out the global window handle for the color palette
                     * for safety's sake.
                     */
                    ghwndColor = NULL;
                }
    
                break;
    
            default:
                return FALSE;
        }
    
        return FALSE;
    }
    
    
    
    /************************************************************************
    * ColorInit
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    STATICFN VOID NEAR ColorInit(
        HWND hwnd)
    {
        RECT rc;
    
        /*
         * Get the dimension of a single color square, and the vertical
         * offset to where the top of the squares are.
         */
        GetWindowRect(GetDlgItem(hwnd, DID_COLORBOX), &rc);
        gcxColorBox = (rc.right - rc.left) / COLORCOLS;
        gyColorBoxStart = ((rc.right - rc.left) - (gcxColorBox * COLORCOLS)) / 2;
    }
    
    
    
    /************************************************************************
    * ColorProcessCommand
    *
    *
    * Arguments:
    *   HWND hwnd        - The window handle.
    *   INT idCtrl       - The id of the control the WM_COMMAND is for.
    *   INT NotifyCode   - The control's notification code.
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR ColorProcessCommand(
        HWND hwnd,
        INT idCtrl,
        INT NotifyCode)
    {
        switch (idCtrl) {
            case DID_COLOREDIT:
                ColorEdit();
                break;
    
            case DID_COLORDEFAULT:
                if (gfModeLeft == MODE_COLOR) {
                    gargbColor[giColorLeft] = gargbDefaultColor[giColorLeft];
                    InvalidateRect(GetDlgItem(ghwndColor, DID_COLORBOX),
                            NULL, TRUE);
                    SetLeftColor(giColorLeft, gfModeLeft);
                }
    
                break;
        }
    }
    
    
    
    /************************************************************************
    * ColorBoxWndProc
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    WINDOWPROC ColorBoxWndProc(
        HWND hwnd,
        UINT msg,
        WPARAM wParam,
        LPARAM lParam)
    {
        POINT pt;
        HDC hdc;
        PAINTSTRUCT ps;
        INT iColor;
        INT iMode;
    
        switch (msg) {
            case WM_PAINT:
                hdc = BeginPaint(hwnd, &ps);
                ColorBoxPaint(hdc);
                EndPaint(hwnd, &ps);
                break;
    
            case WM_LBUTTONDOWN:
            case WM_RBUTTONDOWN:
                ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);
                ColorBoxClicked(msg, &pt);
                break;
    
            case WM_LBUTTONDBLCLK:
                ((pt).x = ((*((POINTS *)&(lParam)))).x, (pt).y = ((*((POINTS *)&(lParam)))).y);
                if (ColorBoxHitTest(&pt, &iColor, &iMode))
                    ColorEdit();
    
                break;
    
            default:
                return DefWindowProc(hwnd, msg, wParam, lParam);
        }
    
        return 0;
    }
    
    
    
    /************************************************************************
    * ColorBoxPaint
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR ColorBoxPaint(
        HDC hdc)
    {
        HDC hdcMem;
        HBITMAP hbmMem;
        INT i;
        INT x;
        INT y;
        INT cx = gcxColorBox + 1;
        INT cy = gcxColorBox + 1;
    
        if (giColorPalType != FT_BITMAP) {
            x = 0;
            y = gyColorBoxStart;
            DrawColorRect(hdc, grgbScreen, x, y, cx, cy, NULL, FALSE);
            y += gcxColorBox;
            DrawColorRect(hdc, grgbInverse, x, y, cx, cy, NULL, FALSE);
        }
    
        if (!(hdcMem = CreateCompatibleDC(hdc)))
            return;
    
        /*
         * Create a bitmap.  It will have the same number of colors as the
         * current image.
         */
        if (!(hbmMem = MyCreateBitmap(hdc, cx, cy, gnColorPalColors))) {
            DeleteDC(hdcMem);
            return;
        }
    
        SelectObject(hdcMem, hbmMem);
    
        x = gcxColorBox * 2;
        y = gyColorBoxStart;
    
        for (i = 1; i <= COLORSMAX; i++) {
            DrawColorRect(hdc, gargbCurrent[i - 1], x, y, cx, cy, hdcMem, TRUE);
    
            if (i % COLORROWS) {
                y += gcxColorBox;
            }
            else {
                x += gcxColorBox;
                y = gyColorBoxStart;
            }
        }
    
        DeleteDC(hdcMem);
        DeleteObject(hbmMem);
    }
    
    
    
    /************************************************************************
    * DrawColorRect
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR DrawColorRect(
        HDC hdc,
        DWORD rgb,
        INT x,
        INT y,
        INT cx,
        INT cy,
        HDC hdcMem,
        BOOL fMonoOK)
    {
        HBRUSH hbr;
        HBRUSH hbrOld;
    
        hbr = CreateSolidBrush(rgb);
        hbrOld = SelectObject(hdc, hbr);
        MyRectangle(hdc, x, y, x + cx, y + cy, hdcMem, fMonoOK);
        SelectObject(hdc, hbrOld);
        DeleteObject(hbr);
    }
    
    
    
    /************************************************************************
    * MyRectangle
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR MyRectangle(
        HDC hdc,
        INT left,
        INT top,
        INT right,
        INT bottom,
        HDC hdcMem,
        BOOL fMonoOK)
    {
        HBITMAP hbmMem;
        HBRUSH hbr;
        HPEN hpen;
        HBRUSH hbrOld;
        HPEN hpenOld;
        BOOL fDCCreated = FALSE;
        INT cx = right - left;
        INT cy = bottom - top;
        INT nColors;
    
        /*
         * Do they want us to create the memory DC and bitmap for them?
         */
        if (!hdcMem) {
            if (!(hdcMem = CreateCompatibleDC(hdc)))
                return;
    
            /*
             * Create a bitmap.  It will be monochrome if in 2 color mode
             * and monochrome is ok, otherwise it will be 16 color.
             */
            nColors = gnColorPalColors;
            if (!fMonoOK)
                nColors = 16;
    
            if (!(hbmMem = MyCreateBitmap(hdc, cx, cy, nColors))) {
                DeleteDC(hdcMem);
                return;
            }
    
            SelectObject(hdcMem, hbmMem);
            fDCCreated = TRUE;
        }
    
        /*
         * Extract the current pen and brush out of the passed in DC.
         */
        hbr = SelectObject(hdc, GetStockObject(NULL_BRUSH));
        hpen = SelectObject(hdc, GetStockObject(NULL_PEN));
    
        /*
         * Select them into the memory DC.
         */
        hbrOld = SelectObject(hdcMem, hbr);
        hpenOld = SelectObject(hdcMem, hpen);
    
        /*
         * Draw the rectangle in the memory bitmap.
         */
        Rectangle(hdcMem, 0, 0, cx, cy);
    
        /*
         * Unselect the pen and brush from the memory DC.
         */
        SelectObject(hdcMem, hbrOld);
        SelectObject(hdcMem, hpenOld);
    
        /*
         * Restore the pen and brush to the original DC.
         */
        SelectObject(hdc, hbr);
        SelectObject(hdc, hpen);
    
        /*
         * Blit the memory image to the passed in DC.
         */
        BitBlt(hdc, left, top, cx, cy, hdcMem, 0, 0, SRCCOPY);
    
        if (fDCCreated) {
            DeleteDC(hdcMem);
            DeleteObject(hbmMem);
        }
    }
    
    
    
    /************************************************************************
    * ColorBoxClicked
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR ColorBoxClicked(
        UINT msg,
        PPOINT ppt)
    {
        INT iColor;
        INT iMode;
    
        if (ColorBoxHitTest(ppt, &iColor, &iMode)) {
            switch (msg) {
                case WM_LBUTTONDOWN:
                    SetLeftColor(iColor, iMode);
                    break;
    
                case WM_RBUTTONDOWN:
                    SetRightColor(iColor, iMode);
                    break;
            }
        }
    }
    
    
    
    /************************************************************************
    * ColorBoxHitTest
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN BOOL NEAR ColorBoxHitTest(
        PPOINT ppt,
        PINT piColor,
        PINT pfMode)
    {
        INT iCol;
        INT iRow;
        INT iBox;
    
        if (ppt->y < gyColorBoxStart)
            return FALSE;
    
        iCol = ppt->x / gcxColorBox;
        iRow = (ppt->y - gyColorBoxStart) / gcxColorBox;
    
        if (iCol >= (COLORSMAX / COLORROWS) + 2 || iRow >= COLORROWS)
            return FALSE;
    
        iBox = iRow + (iCol * COLORROWS);
    
        switch (iBox) {
            case 0:
                if (giColorPalType == FT_BITMAP)
                    return FALSE;
    
                *piColor = 0;
                *pfMode = MODE_SCREEN;
                return TRUE;
    
            case 1:
                if (giColorPalType == FT_BITMAP)
                    return FALSE;
    
                *piColor = 0;
                *pfMode = MODE_INVERSE;
                return TRUE;
    
            case 2:
            case 3:
                return FALSE;
    
            default:
                *piColor = iBox - (COLORROWS * 2);
                *pfMode = MODE_COLOR;
                return TRUE;
        }
    }
    
    
    
    /************************************************************************
    * ColorLRWndProc
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    WINDOWPROC ColorLRWndProc(
        HWND hwnd,
        UINT msg,
        WPARAM wParam,
        LPARAM lParam)
    {
        HDC hdc;
        PAINTSTRUCT ps;
    
        switch (msg) {
            case WM_PAINT:
                hdc = BeginPaint(hwnd, &ps);
                ColorLRPaint(hwnd, hdc);
                EndPaint(hwnd, &ps);
                break;
    
            default:
                return DefWindowProc(hwnd, msg, wParam, lParam);
        }
    
        return 0;
    }
    
    
    
    /************************************************************************
    * ColorLRPaint
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR ColorLRPaint(
        HWND hwnd,
        HDC hdc)
    {
        RECT rc;
    
        GetClientRect(hwnd, &rc);
        DrawSunkenRect(&rc, hdc);
        ColorLRDrawSamples(hdc, &rc, TRUE);
        ColorLRDrawSamples(hdc, &rc, FALSE);
    }
    
    
    
    /************************************************************************
    * ColorLRDrawSamples
    *
    * Draws the sample colors in the Color Left-Right control.
    *
    * Arguments:
    *   HDC hdc    - DC to draw into.
    *   PRECT prc  - Rectangle of color sample control.  The samples will
    *                be centered within this with an appropriate margin.
    *   BOOL fLeft - TRUE if the left sample is to be drawn, FALSE for the right.
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR ColorLRDrawSamples(
        HDC hdc,
        PRECT prc,
        BOOL fLeft)
    {
        INT xLeftStart;
        INT xRightStart;
        INT ySolidStart;
        INT yDitherStart;
        INT cx;
        INT cy;
        HBRUSH hbrOld;
        HPEN hpenOld;
        BOOL fMonoOK;
    
        /*
         * The width and height of each square includes the border.
         */
        cx = ((prc->right - prc->left) - (6 * PALETTEMARGIN)) / 2;
        cy = ((prc->bottom - prc->top) - (4 * PALETTEMARGIN)) / 2;
    
        xLeftStart = prc->left + (PALETTEMARGIN * 2) + 1;
        xRightStart = xLeftStart + cx + (PALETTEMARGIN * 2);
    
        ySolidStart = prc->top + (PALETTEMARGIN * 2) + 1;
        yDitherStart = ySolidStart - 1 + cy;
    
        /*
         * Draw either the left or the right color sample.
         */
        if (fLeft) {
            fMonoOK = (gfModeLeft == MODE_COLOR) ? TRUE : FALSE;
    
            /*
             * Draw the solid color.
             */
            hbrOld = SelectObject(hdc, ghbrLeftSolid);
            hpenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
            MyRectangle(hdc, xLeftStart, ySolidStart,
                    xLeftStart + cx, yDitherStart + 1, NULL, fMonoOK);
    
            /*
             * Draw the true color (may be dithered).
             */
            SelectObject(hdc, ghbrLeft);
            MyRectangle(hdc, xLeftStart, yDitherStart,
                    xLeftStart + cx, yDitherStart + cy, NULL, fMonoOK);
        }
        else {
            fMonoOK = (gfModeRight == MODE_COLOR) ? TRUE : FALSE;
    
            hbrOld = SelectObject(hdc, ghbrRightSolid);
            hpenOld = SelectObject(hdc, GetStockObject(NULL_PEN));
            MyRectangle(hdc, xRightStart, ySolidStart,
                    xRightStart + cx, yDitherStart + 1, NULL, fMonoOK);
    
            SelectObject(hdc, ghbrRight);
            MyRectangle(hdc, xRightStart, yDitherStart,
                    xRightStart + cx, yDitherStart + cy, NULL, fMonoOK);
        }
    
        /*
         * Now draw the outline rectangle.
         */
        SelectObject(hdc, GetStockObject(BLACK_PEN));
        SelectObject(hdc, GetStockObject(NULL_BRUSH));
    
        if (fLeft) {
            Rectangle(hdc, xLeftStart - 1, ySolidStart - 1,
                    xLeftStart + cx, yDitherStart + cy);
        }
        else {
            Rectangle(hdc, xRightStart - 1, ySolidStart - 1,
                    xRightStart + cx, yDitherStart + cy);
        }
    
        /*
         * Clean up.
         */
        SelectObject(hdc, hpenOld);
        SelectObject(hdc, hbrOld);
    }
    
    
    
    /************************************************************************
    * ColorLRUpdate
    *
    * Called when the left or right color has been changed.  This function
    * will cause the specified color sample to be updated in the color palette.
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR ColorLRUpdate(
        BOOL fLeft)
    {
        RECT rc;
        HWND hwndLR;
        HDC hdc;
    
        hwndLR = GetDlgItem(ghwndColor, DID_COLORLR);
        GetClientRect(hwndLR, &rc);
        hdc = GetDC(hwndLR);
        ColorLRDrawSamples(hdc, &rc, fLeft);
        ReleaseDC(hwndLR, hdc);
    }
    
    
    
    /************************************************************************
    * ColorEdit
    *
    * This function calls the standard color chooser dialog to get a
    * new color for the selected palette entry.
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR ColorEdit(VOID)
    {
        /*
         * This array of custom colors is initialized to all white colors.
         * The custom colors will be remembered between calls, but not
         * between sessions.
         */
        static DWORD argbCust[16] = {
            RGB(255, 255, 255), RGB(255, 255, 255),
            RGB(255, 255, 255), RGB(255, 255, 255),
            RGB(255, 255, 255), RGB(255, 255, 255),
            RGB(255, 255, 255), RGB(255, 255, 255),
            RGB(255, 255, 255), RGB(255, 255, 255),
            RGB(255, 255, 255), RGB(255, 255, 255),
            RGB(255, 255, 255), RGB(255, 255, 255),
            RGB(255, 255, 255), RGB(255, 255, 255)
        };
        CHOOSECOLOR cc;
        DWORD rgbOld;
        BOOL fResult;
        INT idPrevDlg;
    
        switch (gfModeLeft) {
            case MODE_COLOR:
                /*
                 * The monochrome palette cannot be edited.
                 */
                if (gnColorPalColors == 2)
                    return;
    
                rgbOld = gargbCurrent[giColorLeft];
                break;
    
            case MODE_SCREEN:
                rgbOld = grgbScreen;
                break;
    
            case MODE_INVERSE:
                rgbOld = grgbInverse;
                break;
        }
    
        cc.lStructSize = sizeof(CHOOSECOLOR);
        cc.hwndOwner = ghwndMain;
        cc.hInstance = ghInst;
        cc.rgbResult = rgbOld;
        cc.lpCustColors = argbCust;
        cc.Flags = CC_RGBINIT | CC_SHOWHELP;
        cc.lCustData = 0;
        cc.lpfnHook = NULL;
        cc.lpTemplateName = NULL;
    
        EnteringDialog(DID_COMMONFILECHOOSECOLOR, &idPrevDlg, TRUE);
        fResult = ChooseColor(&cc);
        EnteringDialog(idPrevDlg, NULL, FALSE);
    
        if (fResult && rgbOld != cc.rgbResult) {
            switch (gfModeLeft) {
                case MODE_COLOR:
                    gargbCurrent[giColorLeft] = cc.rgbResult;
                    break;
    
                case MODE_SCREEN:
                    SetScreenColor(cc.rgbResult);
                    break;
    
                case MODE_INVERSE:
                    SetScreenColor(ComputeInverseColor(cc.rgbResult));
                    break;
            }
    
            SetLeftColor(giColorLeft, gfModeLeft);
            InvalidateRect(GetDlgItem(ghwndColor, DID_COLORBOX), NULL, TRUE);
        }
    }
    
    
    
    /************************************************************************
    * SetLeftColor
    *
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR SetLeftColor(
        INT iColor,
        INT iMode)
    {
        DWORD rgbSolid;
        BOOL fEnableDefault = FALSE;
        BOOL fEnableEdit = FALSE;
    
        if (ghbrLeft)
            DeleteObject(ghbrLeft);
    
        if (ghbrLeftSolid)
            DeleteObject(ghbrLeftSolid);
    
        if (ghpenLeft)
            DeleteObject(ghpenLeft);
    
        switch (iMode) {
            case MODE_COLOR:
                ghbrLeft = MyCreateSolidBrush(gargbCurrent[iColor]);
                rgbSolid = MyGetNearestColor(gargbCurrent[iColor], TRUE);
                ghbrLeftSolid = CreateSolidBrush(rgbSolid);
                ghpenLeft = CreatePen(PS_INSIDEFRAME, 1, rgbSolid);
                giColorLeft = iColor;
    
                /*
                 * We will enable the "Default" button if the current color
                 * on the left button is not the default color, and we are
                 * not in monochrome mode.
                 */
                if (gargbColor[giColorLeft] != gargbDefaultColor[giColorLeft] &&
                        gnColorPalColors > 2)
                    fEnableDefault = TRUE;
    
                /*
                 * For non-screen colors, the Edit button will be enabled
                 * if we are not in monochrome mode.
                 */
                if (gnColorPalColors > 2)
                    fEnableEdit = TRUE;
    
                break;
    
            case MODE_SCREEN:
                ghbrLeft = CreateSolidBrush(grgbScreen);
                ghbrLeftSolid = CreateSolidBrush(grgbScreen);
                ghpenLeft = CreatePen(PS_INSIDEFRAME, 1, grgbScreen);
                giColorLeft = 0;
                fEnableEdit = TRUE;
                break;
    
            case MODE_INVERSE:
                ghbrLeft = CreateSolidBrush(grgbInverse);
                ghbrLeftSolid = CreateSolidBrush(grgbInverse);
                ghpenLeft = CreatePen(PS_INSIDEFRAME, 1, grgbInverse);
                giColorLeft = 0;
                fEnableEdit = TRUE;
                break;
        }
    
        EnableWindow(GetDlgItem(ghwndColor, DID_COLORDEFAULT), fEnableDefault);
        EnableWindow(GetDlgItem(ghwndColor, DID_COLOREDIT), fEnableEdit);
    
        gfModeLeft = iMode;
        ColorLRUpdate(TRUE);
    }
    
    
    
    /************************************************************************
    * SetRightColor
    *
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR SetRightColor(
        INT iColor,
        INT iMode)
    {
        DWORD rgbSolid;
    
        if (ghbrRight)
            DeleteObject(ghbrRight);
    
        if (ghbrRightSolid)
            DeleteObject(ghbrRightSolid);
    
        if (ghpenRight)
            DeleteObject(ghpenRight);
    
        switch (iMode) {
            case MODE_COLOR:
                ghbrRight = MyCreateSolidBrush(gargbCurrent[iColor]);
                rgbSolid = MyGetNearestColor(gargbCurrent[iColor], TRUE);
                ghbrRightSolid = CreateSolidBrush(rgbSolid);
                ghpenRight = CreatePen(PS_INSIDEFRAME, 1, rgbSolid);
                giColorRight = iColor;
                break;
    
            case MODE_SCREEN:
                ghbrRight = CreateSolidBrush(grgbScreen);
                ghbrRightSolid = CreateSolidBrush(grgbScreen);
                ghpenRight = CreatePen(PS_INSIDEFRAME, 1, grgbScreen);
                giColorRight = 0;
                break;
    
            case MODE_INVERSE:
                ghbrRight = CreateSolidBrush(grgbInverse);
                ghbrRightSolid = CreateSolidBrush(grgbInverse);
                ghpenRight = CreatePen(PS_INSIDEFRAME, 1, grgbInverse);
                giColorRight = 0;
                break;
        }
    
        gfModeRight = iMode;
        ColorLRUpdate(FALSE);
    }
    
    
    
    /************************************************************************
    * SetScreenColor
    *
    *
    * History:
    *
    ************************************************************************/
    
    VOID SetScreenColor(
        DWORD rgb)
    {
        DWORD rgbInverse;
        HDC hdcTemp;
        HBITMAP hbmOld;
        HDC hdcANDTemp;
        HBITMAP hbmANDOld;
    
        rgb = MyGetNearestColor(rgb, FALSE);
    
        /*
         * Because we are about to change the screen color, separate
         * out the XOR mask (but only for icons/cursors).
         */
        if (giColorPalType != FT_BITMAP) {
            if (gpImageCur) {
                ImageDCSeparate(ghdcImage, gcxImage, gcyImage, ghdcANDMask,
                        grgbScreen);
    
                /*
                 * Is there a pending undo buffer?  If so, it must be
                 * changed as well or an undo that is done after a screen
                 * color change will restore the wrong colors!
                 */
                if (ghbmUndo) {
                    /*
                     * Create some temporary DC's to use when separating
                     * out the undo buffer's masks.  These will be deleted
                     * a little later.
                     */
                    hdcTemp = CreateCompatibleDC(ghdcImage);
                    hbmOld = SelectObject(hdcTemp, ghbmUndo);
                    hdcANDTemp = CreateCompatibleDC(ghdcANDMask);
                    hbmANDOld = SelectObject(hdcANDTemp, ghbmUndoMask);
    
                    /*
                     * Separate out the undo buffer's colors, before
                     * changing the screen color.  It will be combined
                     * later.
                     */
                    ImageDCSeparate(hdcTemp, gcxImage, gcyImage, hdcANDTemp,
                            grgbScreen);
                }
            }
        }
    
        if (ghbrScreen)
            DeleteObject(ghbrScreen);
    
        ghbrScreen = CreateSolidBrush(rgb);
        grgbScreen = rgb;
    
        if (ghbrInverse)
            DeleteObject(ghbrInverse);
    
        rgbInverse = ComputeInverseColor(rgb);
        ghbrInverse = CreateSolidBrush(rgbInverse);
        grgbInverse = rgbInverse;
    
        /*
         * For icons and cursors, we might need to update a few more things.
         */
        if (giColorPalType != FT_BITMAP) {
            /*
             * Recombine the XOR and AND images now that there is a new screen
             * color.  This updates the image DC with the new color properly.
             */
            if (gpImageCur) {
                ImageDCCombine(ghdcImage, gcxImage, gcyImage, ghdcANDMask);
    
                /*
                 * Is there a pending undo buffer?  If so, it has to be
                 * recombined with the new screen color.
                 */
                if (ghbmUndo) {
                    ImageDCCombine(hdcTemp, gcxImage, gcyImage, hdcANDTemp);
    
                    /*
                     * Clean up the DC's that were allocated a little earlier.
                     */
                    SelectObject(hdcANDTemp, hbmANDOld);
                    DeleteDC(hdcANDTemp);
                    SelectObject(hdcTemp, hbmOld);
                    DeleteDC(hdcTemp);
                }
            }
    
            /*
             * Reset the colors on the mouse buttons, just in case a screen
             * or inverse screen color was assigned to either of them.
             */
            SetLeftColor(giColorLeft, gfModeLeft);
            SetRightColor(giColorRight, gfModeRight);
    
            InvalidateRect(GetDlgItem(ghwndColor, DID_COLORBOX), NULL, TRUE);
        }
    
        ViewUpdate();
    }
    
    
    
    /************************************************************************
    * MyCreateSolidBrush
    *
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN HBRUSH NEAR MyCreateSolidBrush(
        DWORD rgb)
    {
        HDC hdc;
        HDC hdcMem;
        HBRUSH hbr;
        HBRUSH hbrOld;
        HBITMAP hbmPat;
        HBITMAP hbmOld;
    
        /*
         * First, create a brush for the given RGB value.
         */
        hbr = CreateSolidBrush(rgb);
    
        /*
         * Create a temporary memory DC.
         */
        hdc = GetDC(ghwndMain);
        hdcMem = CreateCompatibleDC(hdc);
    
        /*
         * Create a temporary bitmap.
         */
        hbmPat = MyCreateBitmap(hdc, 8, 8, gnColorPalColors);
        ReleaseDC(ghwndMain, hdc);
    
        /*
         * Draw the (possibly) dithered pattern on the temporary bitmap.
         */
        hbmOld = SelectObject(hdcMem, hbmPat);
        hbrOld = SelectObject(hdcMem, hbr);
        PatBlt(hdcMem, 0, 0, 8, 8, PATCOPY);
        SelectObject(hdcMem, hbrOld);
        SelectObject(hdcMem, hbmOld);
        DeleteDC(hdcMem);
    
        /*
         * Delete the first brush.
         */
        DeleteObject(hbr);
    
        /*
         * Now create a pattern brush out of the (dithered) bitmap.
         */
        hbr = CreatePatternBrush(hbmPat);
    
        DeleteObject(hbmPat);
    
        /*
         * Return the pattern brush.
         */
        return hbr;
    }
    
    
    
    /************************************************************************
    * MyGetNearestColor
    *
    * This function returns the RGB value of the nearest color to the
    * specified RGB value.  If fMonoOK is TRUE, it takes into account
    * the number of colors of the current image being edited.  In other
    * words, it will return the nearest solid color for a device that
    * has the number of colors of the current image.
    *
    * Arguments:
    *   DWORD rgb    - RGB value of the color.
    *   BOOL fMonoOK - TRUE if the returned color should be mapped to a
    *                  color in a monochrome palette, if the current image
    *                  is monochrome.  A value of FALSE will return a
    *                  color mapped to the closest color in a 16 color
    *                  palette.
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN DWORD NEAR MyGetNearestColor(
        DWORD rgb,
        BOOL fMonoOK)
    {
        HDC hdc;
        HDC hdcMem;
        DWORD rgbNearest;
        HBITMAP hbmMem;
        HBITMAP hbmOld;
    
        hdc = GetDC(ghwndMain);
        hdcMem = CreateCompatibleDC(hdc);
        hbmMem = MyCreateBitmap(hdc, 1, 1, (fMonoOK) ? gnColorPalColors : 16);
        hbmOld = SelectObject(hdcMem, hbmMem);
        rgbNearest = GetNearestColor(hdcMem, rgb);
        SelectObject(hdcMem, hbmOld);
        DeleteObject(hbmMem);
        DeleteDC(hdcMem);
        ReleaseDC(ghwndMain, hdc);
    
        return rgbNearest;
    }
    
    
    
    /************************************************************************
    * ComputeInverseColor
    *
    * Computes the inverse value of a given rgb color.
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN DWORD NEAR ComputeInverseColor(
        DWORD rgb)
    {
        HBITMAP hTempBit1;
        HBITMAP hTempBit2;
        HDC hTempDC1;
        HDC hTempDC2;
        HDC hdc;
        HANDLE hOldObj1;
        HANDLE hOldObj2;
        DWORD rgbInv;
    
        hdc = GetDC(ghwndMain);
        hTempDC1 = CreateCompatibleDC(hdc);
        hTempDC2 = CreateCompatibleDC(hdc);
    
        /* create two temporary 1x1, 16 color bitmaps */
        hTempBit1 = MyCreateBitmap(hdc, 1, 1, 16);
        hTempBit2 = MyCreateBitmap(hdc, 1, 1, 16);
    
        ReleaseDC(ghwndMain, hdc);
    
        hOldObj1 = SelectObject(hTempDC1, hTempBit1);
        hOldObj2 = SelectObject(hTempDC2, hTempBit2);
    
        /* method for getting inverse color : set the given pixel (rgb) on
         * one DC. Now blt it to the other DC using a SRCINVERT rop.
         * This yields a pixel of the inverse color on the destination DC
         */
        SetPixel(hTempDC1, 0, 0, rgb);
        PatBlt(hTempDC2, 0, 0, 1, 1, WHITENESS);
        BitBlt(hTempDC2, 0, 0, 1, 1, hTempDC1, 0, 0, SRCINVERT);
        rgbInv = GetPixel(hTempDC2, 0, 0);
    
        /* clean up ... */
        SelectObject(hTempDC1, hOldObj1);
        SelectObject(hTempDC2, hOldObj2);
        DeleteObject(hTempBit1);
        DeleteObject(hTempBit2);
        DeleteDC(hTempDC1);
        DeleteDC(hTempDC2);
    
        /* ...and return the inverted RGB value */
        return rgbInv;
    }
    
    
    
    /************************************************************************
    * SetColorPalette
    *
    *
    * History:
    *
    ************************************************************************/
    
    VOID SetColorPalette(
        INT nColors,
        INT iType,
        BOOL fForce)
    {
        /*
         * Quit if nothing changed (unless they are forcing it to be updated).
         */
        if (!fForce && nColors == gnColorPalColors && iType == giColorPalType)
            return;
    
        /*
         * Set the globals that all the color palette routines use.
         */
        gnColorPalColors = nColors;
        giColorPalType = iType;
    
        if (gnColorPalColors == 2)
            gargbCurrent = gargbMono;
        else
            gargbCurrent = gargbColor;
    
        ShowWindow(GetDlgItem(ghwndColor, DID_COLORSCREENLABEL),
                (giColorPalType == FT_BITMAP) ? SW_HIDE : SW_SHOW);
        ShowWindow(GetDlgItem(ghwndColor, DID_COLORINVERSELABEL),
                (giColorPalType == FT_BITMAP) ? SW_HIDE : SW_SHOW);
    
        SetLeftColor(1, MODE_COLOR);
        SetRightColor(0, MODE_COLOR);
    
        InvalidateRect(GetDlgItem(ghwndColor, DID_COLORBOX), NULL, TRUE);
    }
    
    
    
    /************************************************************************
    * RestoreDefaultColors
    *
    *
    * History:
    *
    ************************************************************************/
    
    VOID RestoreDefaultColors(VOID)
    {
        INT i;
    
        for (i = 0; i < COLORSMAX; i++)
            gargbColor[i] = gargbDefaultColor[i];
    
        SetColorPalette(16, giColorPalType, TRUE);
    }
