/*===========================================================================*/
/*                                                                           */
/* File    :  ICONPROC.C                                                     */
/*                                                                           */
/* Purpose :  Contains the winproc's for the various window classes.         */
/*                                                                           */
/* History :                                                                 */
/*                                                                           */
/* (C) Copyright 1991      Marc Adler/Magma Systems     All Rights Reserved  */
/*===========================================================================*/

#include <memory.h>
#include <windows.h>
#include "ico.h"

typedef struct tagScrapInfo
{
  HANDLE hPasteBuffer;
  int    iWidth, iHeight;
} SCRAPINFO;

SCRAPINFO ScrapInfo =
{
  NULL, 0, 0
};


WORD CurrentColor = 0;
WORD iCurrentTool = ID_PENCIL;
RECT rMarked;


/****************************************************************************/
/*                                                                          */
/* Function : ICOViewWndProc                                                */
/*                                                                          */
/* Purpose  : Winproc for the main window                                   */
/*                                                                          */
/* Returns  :                                                               */
/*                                                                          */
/****************************************************************************/
long FAR PASCAL ICOViewWndProc(hWnd, message, wParam, lParam)
  HWND hWnd;
  unsigned message;
  WORD wParam;
  LONG lParam;
{
  FARPROC lpProc;
  char    buf[128];
  int     rc, i;

  switch (message)
  {
    case WM_COMMAND :
      switch(wParam)
      {
        case IDM_NEW:
          if (IconPromptForSave(hWnd) == FALSE)
            break;
          NewIcon();
          InvalidateAllWindows();
          break;

        case IDM_OPEN:
          if (IconPromptForSave(hWnd) != IDCANCEL)
          {
            lpProc = MakeProcInstance(OpenFileDlgProc, hInst);
            rc = DialogBox(hInst, (LPSTR) "OPEN", hWnd, lpProc);
            FreeProcInstance(lpProc);
          }
          break;

        case IDM_SAVE:
          if (IconInfo.szFilename[0])
          {
            ICOWrite(IconInfo.szFilename, IconInfo.hBitmapBits);
            break;
          }
          /* fall through...*/

        case IDM_SAVEAS:
          lpProc = MakeProcInstance(SaveAsDlgProc,hInst);
          rc = DialogBox(hInst, (LPSTR) "SAVE", hWnd, lpProc);
          FreeProcInstance(lpProc);
          break;

        case IDM_EXIT:
          if (IconPromptForSave(hWnd) == TRUE)
            PostQuitMessage(0);
          break;

        case IDM_ABOUT:
          /*
            Display the "About" box
          */
          lpProc = MakeProcInstance(About, hInst);
          DialogBox(hInst, "About", hWnd, lpProc);
          FreeProcInstance(lpProc);
          break;

        case IDM_COPY :
          if (IconInfo.fFlags & STATE_SOMETHING_SELECTED)
          {
            int x, y;
            LPSTR lpBuf;

            SortRect((LPRECT) &rMarked);

            if (ScrapInfo.hPasteBuffer == NULL)
              ScrapInfo.hPasteBuffer = 
                         GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 32L*32L);
            lpBuf = GlobalLock(ScrapInfo.hPasteBuffer);

            for (x = rMarked.left;  x <= rMarked.right;  x += 8)
              for (y = rMarked.top;  y <= rMarked.bottom;  y += 8)
                *lpBuf++ = (BYTE) GetIconPixel(x/8, y/8);
            ScrapInfo.iWidth = (rMarked.right - rMarked.left) / 8 + 1;
            ScrapInfo.iHeight = (rMarked.bottom - rMarked.top) / 8 + 1;

            ClearSelection(hWndEdit, &rMarked, iCurrentTool);
            IconInfo.fFlags &= ~STATE_SOMETHING_SELECTED;
            EnableMenuItem(GetMenu(hWndMain), IDM_PASTE, MF_BYCOMMAND | MF_ENABLED);
            EnableMenuItem(GetMenu(hWndMain), IDM_COPY, MF_BYCOMMAND | MF_DISABLED);
          }
          break;

        case IDM_PASTE :
          if (ScrapInfo.hPasteBuffer != NULL &&
                (IconInfo.fFlags & STATE_SOMETHING_SELECTED))
          {
            LPSTR lpBuf = GlobalLock(ScrapInfo.hPasteBuffer);
            int   x, y;

            for (x = rMarked.left;  x <= rMarked.right;  x += 8)
              for (y = rMarked.top;  y <= rMarked.bottom;  y += 8)
                SetIconPixel(x/8, y/8, *lpBuf++);

            GlobalUnlock(ScrapInfo.hPasteBuffer);
            ClearSelection(hWndEdit, &rMarked, iCurrentTool);
            IconInfo.fFlags &= ~STATE_SOMETHING_SELECTED;

            InvalidateAllWindows();
          }
          break;

        case IDM_UNDO :
          IconUndo();
          break;


        case ID_COLOR0  :  case ID_COLOR1 :  case ID_COLOR2 :  case ID_COLOR3 :
        case ID_COLOR4  :  case ID_COLOR5 :  case ID_COLOR6 :  case ID_COLOR7 :
        case ID_COLOR8  :  case ID_COLOR9 :  case ID_COLOR10 : case ID_COLOR11 :
        case ID_COLOR12 : case ID_COLOR13 : case ID_COLOR14 : case ID_COLOR15 :
          CurrentColor = wParam - ID_COLOR0;
          break;

        case ID_PENCIL   :
        case ID_LINE     :
        case ID_OPENRECT :
        case ID_FILLRECT :
        case ID_CIRCLE   :
        case ID_FILLCIRC :
        case ID_FLOOD    :
        case ID_SELECT   :
          iCurrentTool = wParam;
          break;

      } /* end WM_COMMAND */
      break;


    case WM_CLOSE:
      if (IconPromptForSave(hWnd) == TRUE)
        DestroyWindow(hWnd);
      break;

    case WM_QUERYENDSESSION:
      return IconPromptForSave(hWnd);

    case WM_DESTROY:
      PostQuitMessage(0);
      break;

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


/****************************************************************************/
/*                                                                          */
/* Function : ICOEditWndProc()                                              */
/*                                                                          */
/* Purpose  : Window proc for the icon editing window                       */
/*                                                                          */
/* Returns  :                                                               */
/*                                                                          */
/****************************************************************************/
long FAR PASCAL ICOEditWndProc(hWnd, message, wParam, lParam)
  HWND hWnd;
  unsigned message;
  WORD wParam;
  LONG lParam;
{
  static BOOL bMarking = FALSE;
  static POINT ptLast;
  POINT  pt;
  char   szMsg[80];


  switch (message)
  {
    case WM_SETCURSOR :
      /*
        Set the mouse cursor to certain shape, depending on the tool
      */
      switch (iCurrentTool)
      {
        case ID_PENCIL   :
          SetCursor(LoadCursor(NULL, IDC_ARROW));
          break;
        case ID_LINE     :
          SetCursor(LoadCursor(NULL, IDC_CROSS));
          break;
        case ID_OPENRECT :
          SetCursor(LoadCursor(NULL, IDC_CROSS));
          break;
        case ID_FILLRECT :
          SetCursor(LoadCursor(NULL, IDC_CROSS));
          break;
        case ID_CIRCLE   :
          SetCursor(LoadCursor(NULL, IDC_CROSS));
          break;
        case ID_FILLCIRC :
          SetCursor(LoadCursor(NULL, IDC_CROSS));
          break;
        default :
          SetCursor(LoadCursor(NULL, IDC_ARROW));
          break;
      }
      return TRUE;


    case WM_LBUTTONDOWN:
      if (IconInfo.fFlags & STATE_SOMETHING_SELECTED)
      {
        ClearSelection(hWnd, &rMarked, ID_SELECT);
        IconInfo.fFlags &= ~STATE_SOMETHING_SELECTED;
      }

      bMarking = TRUE;     /* user has pressed the left button */
      IconCheckpoint();

      if (iCurrentTool == ID_PENCIL)
      {
        HBRUSH hBrush, hOldBrush;
        HDC    hDC;
        RECT   rPixel;
        extern BYTE rgb[16][3];

        pt = MAKEPOINT(lParam);
        SetIconPixel(ptLast.x = (pt.x/8), ptLast.y = (pt.y/8), CurrentColor);

        hDC = GetDC(hWndEdit);
        hBrush = CreateSolidBrush(RGB(rgb[CurrentColor][2], rgb[CurrentColor][1], rgb[CurrentColor][0]));
        hOldBrush = SelectObject(hDC, hBrush);

        pt.x = (pt.x * 8) / 8;
        pt.y = (pt.y * 8) / 8;
        pt.x = ptLast.x * 8;
        pt.y = ptLast.y * 8;
        SetRect((LPRECT) &rPixel, pt.x, pt.y, pt.x+8, pt.y+8);
        FillRect(hDC, (LPRECT) &rPixel, hBrush);

        SelectObject(hDC, hOldBrush);
        DeleteObject(hBrush);
        ReleaseDC(hWndEdit, hDC);
        InvalidateRect(hWndActualImage, (LPRECT) NULL, FALSE);
        if (hWndDebug)
        {
          InvalidateRect(hWndDebug, (LPRECT) NULL, FALSE);
          UpdateWindow(hWndDebug);
        }
      }
      else
      {
        SetRectEmpty(&rMarked);
        StartSelection(hWnd, MAKEPOINT(lParam), &rMarked, iCurrentTool);
      }
      IconInfo.fFlags |= STATE_DIRTY;
      break;


    case WM_MOUSEMOVE:
      if (bMarking)
      {
        if (iCurrentTool == ID_PENCIL)
        {
          HBRUSH hBrush, hOldBrush;
          HDC    hDC;
          RECT   rPixel;
          extern BYTE rgb[16][3];

          pt = MAKEPOINT(lParam);
          if (ptLast.x == pt.x / 8  &&  ptLast.y == pt.y / 8)
            break;
          SetIconPixel(ptLast.x = (pt.x/8), ptLast.y = (pt.y/8), CurrentColor);

          hDC = GetDC(hWndEdit);
          hBrush = CreateSolidBrush(RGB(rgb[CurrentColor][2], rgb[CurrentColor][1], rgb[CurrentColor][0]));
          hOldBrush = SelectObject(hDC, hBrush);

          pt.x = pt.x * 8 / 8;
          pt.y = pt.y * 8 / 8;
          pt.x = ptLast.x * 8;
          pt.y = ptLast.y * 8;
          SetRect((LPRECT) &rPixel, pt.x, pt.y, pt.x+8, pt.y+8);
          FillRect(hDC, (LPRECT) &rPixel, hBrush);

          SelectObject(hDC, hOldBrush);
          DeleteObject(hBrush);
          ReleaseDC(hWndEdit, hDC);

          InvalidateRect(hWndActualImage, (LPRECT) NULL, FALSE);
          if (hWndDebug)
          {
            InvalidateRect(hWndDebug, (LPRECT) NULL, FALSE);
            UpdateWindow(hWndDebug);
          }
        }
        else
          UpdateSelection(hWnd, MAKEPOINT(lParam), &rMarked, iCurrentTool);
      }
      break;


    case WM_LBUTTONUP:
      if (bMarking) 
      {
        bMarking = FALSE;
        EndSelection(MAKEPOINT(lParam), &rMarked);
        if (iCurrentTool != ID_SELECT)
          ClearSelection(hWnd, &rMarked, iCurrentTool);

        switch (iCurrentTool)
        {
          case ID_FILLRECT :
          {
            int x, y;
            SortRect((LPRECT) &rMarked);
            for (x = rMarked.left;  x <= rMarked.right;  x += 8)
              for (y = rMarked.top;  y <= rMarked.bottom;  y += 8)
                SetIconPixel(x/8, y/8, CurrentColor);
            break;
          }

          case ID_OPENRECT :
          {
            int x, y;
            SortRect((LPRECT) &rMarked);
            for (x = rMarked.left;  x <= rMarked.right;  x += 8)
              SetIconPixel(x/8, rMarked.top/8, CurrentColor);
            for (x = rMarked.left;  x <= rMarked.right;  x += 8)
              SetIconPixel(x/8, rMarked.bottom/8, CurrentColor);
            for (y = rMarked.top;  y <= rMarked.bottom;  y += 8)
              SetIconPixel(rMarked.left/8, y/8, CurrentColor);
            for (y = rMarked.top;  y <= rMarked.bottom;  y += 8)
              SetIconPixel(rMarked.right/8, y/8, CurrentColor);
            break;
          }


          case ID_LINE     :
          {
            FARPROC lpProc;

            lpProc = MakeProcInstance(IconPlotLine, hInst);
            LineDDA(rMarked.left, rMarked.top, rMarked.right, rMarked.bottom,
                    lpProc, (LPSTR) MAKELONG(CurrentColor, 0));
            FreeProcInstance(lpProc);
            break;
          }

          case ID_CIRCLE   :
          case ID_FILLCIRC :
          {
            HDC hDC = GetDC(hWndActualImage);

            SortRect((LPRECT) &rMarked);
            DrawCircleInBitmap(hWndActualImage, hDC, IconInfo.hBitmapBits,
                rMarked.left/8, rMarked.top/8, rMarked.right/8, rMarked.bottom/8);

            ReleaseDC(hWndActualImage, hDC);
            break;
          }

          case ID_FLOOD :
          {
            HDC hDC = GetDC(hWndActualImage);

            pt = MAKEPOINT(lParam);
            FloodFillBitmap(hWndActualImage, hDC, IconInfo.hBitmapBits,
                            rMarked.right/8, rMarked.bottom/8);
            ReleaseDC(hWndActualImage, hDC);
          }
        }

        if (iCurrentTool == ID_SELECT)
        {
          SortRect((LPRECT) &rMarked);
          IconInfo.fFlags |= STATE_SOMETHING_SELECTED;
          EnableMenuItem(GetMenu(hWndMain), IDM_COPY, MF_BYCOMMAND | MF_ENABLED);
          if (ScrapInfo.hPasteBuffer != NULL)
            EnableMenuItem(GetMenu(hWndMain), IDM_PASTE, MF_BYCOMMAND | MF_ENABLED);
        }
        else if (iCurrentTool != ID_PENCIL)
        {
          InvalidateAllWindows();
        }
      }
      break;


    case WM_PAINT :
      if (IconInfo.hBitmapBits != NULL)
      {
        HDC hDC;
        PAINTSTRUCT ps;

        hDC = BeginPaint(hWnd, (LPPAINTSTRUCT) &ps);
        DisplayDIBitmap(hWnd, hDC, IconInfo.hBitmapBits);
        EndPaint(hWnd, (LPPAINTSTRUCT) &ps);
        break;
      }
      /* else fall through ...*/

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


/****************************************************************************/
/*                                                                          */
/* Function : ICOImageWndProc()                                             */
/*                                                                          */
/* Purpose  : Winproc for the actual icon image window. All this does is    */
/*            call the bitmap display procedure in response to WM_PAINTs.   */
/*                                                                          */
/* Returns  :                                                               */
/*                                                                          */
/****************************************************************************/
long FAR PASCAL ICOImageWndProc(hWnd, message, wParam, lParam)
  HWND hWnd;
  unsigned message;
  WORD wParam;
  LONG lParam;
{
  switch (message)
  {
    case WM_PAINT :
      if (IconInfo.hBitmapBits != NULL)
      {
        HDC hDC;
        PAINTSTRUCT ps;

        hDC = BeginPaint(hWnd, (LPPAINTSTRUCT) &ps);
        DisplayDIBitmap(hWnd, hDC, IconInfo.hBitmapBits);
        EndPaint(hWnd, (LPPAINTSTRUCT) &ps);
        break;
      }
      /* else fall through ...*/

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


/****************************************************************************/
/*                                                                          */
/* Function : IconPromptForSave()                                           */
/*                                                                          */
/* Purpose  : Prompts the user to save the current pixmap                   */
/*                                                                          */
/* Returns  : TRUE if the app should terminate (or start a new icon).       */
/*                                                                          */
/****************************************************************************/
BOOL PASCAL IconPromptForSave(HWND hWnd)
{
  if (IconInfo.fFlags & STATE_DIRTY)
  {
    int rc = MessageBox(hWnd, "Save changes?", "Warning", MB_YESNOCANCEL);
    if (rc == IDYES)
    {
      SendMessage(hWnd, WM_COMMAND, IDM_SAVE, 0L);
      IconInfo.fFlags &= ~ STATE_DIRTY;
      return TRUE;
    }
    else if (rc == IDCANCEL)
      return FALSE;
    else if (rc == IDNO)
      return TRUE;
  }
  else
    return TRUE;
}


/****************************************************************************/
/*                                                                          */
/*          ROUTINES TO IMPLEMENT UNDO IN THE ICON EDITOR                   */
/*                                                                          */
/****************************************************************************/

typedef struct tagUndoInfo
{
  HANDLE hUndoBitmap;
} UNDOINFO;

UNDOINFO UndoInfo = 
{
  NULL
};


/****************************************************************************/
/*                                                                          */
/* Function : IconUndo()                                                    */
/*                                                                          */
/* Purpose  : Restores the undo info.                                       */
/*                                                                          */
/* Returns  : Nicht.                                                        */
/*                                                                          */
/****************************************************************************/
VOID PASCAL IconUndo(void)
{
  if (UndoInfo.hUndoBitmap != NULL)
  {
    /*
      Make the shadow buffer the current pixmap
    */
    GlobalFree(IconInfo.hBitmapBits);
    IconInfo.hBitmapBits = UndoInfo.hUndoBitmap;
    UndoInfo.hUndoBitmap = NULL;

    InvalidateAllWindows();
    /*
      No more undo info, so disable the UNDO menu selection
    */
    EnableMenuItem(GetMenu(hWndMain), IDM_UNDO, 
                   MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
  }
}


/****************************************************************************/
/*                                                                          */
/* Function : IconCheckpoint()                                              */
/*                                                                          */
/* Purpose  : Saves a copy of the current pixmap for undo purposes.         */
/*                                                                          */
/* Returns  : Squat.                                                        */
/*                                                                          */
/****************************************************************************/
VOID PASCAL IconCheckpoint(void)
{
  LPSTR lpUndo, lpBits;
  int   nPixelsPerByte = 2;

  /*
    Allocate a shadow pixmap to hold the undo info in
  */
  if (UndoInfo.hUndoBitmap == NULL)
  {
    if ((UndoInfo.hUndoBitmap = 
           GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 32L*32L)) == NULL)
      return;
  }

  lpUndo = GlobalLock(UndoInfo.hUndoBitmap);
  lpBits = GlobalLock(IconInfo.hBitmapBits);

  /*
    Save a copy of the current pixmap in the shadow buffer
  */
  _fmemcpy(lpUndo, lpBits, IconDir.Width / nPixelsPerByte * IconDir.Height);

  GlobalUnlock(UndoInfo.hUndoBitmap);
  GlobalUnlock(IconInfo.hBitmapBits);

  /*
    We now have undo info saved, so enable the UNDO menu selection
  */
  EnableMenuItem(GetMenu(hWndMain), IDM_UNDO, MF_BYCOMMAND | MF_ENABLED);
}


/****************************************************************************/
/*                                                                          */
/* Function : ICODebugWndProc()                                             */
/*                                                                          */
/* Purpose  : Winproc for the debugging window.                             */
/*                                                                          */
/* Returns  :                                                               */
/*                                                                          */
/****************************************************************************/
long FAR PASCAL ICODebugWndProc(hWnd, message, wParam, lParam)
  HWND hWnd;
  unsigned message;
  WORD wParam;
  LONG lParam;
{
  switch (message)
  {
    case WM_DESTROY :
      hWndDebug = NULL;
      break;

    case WM_PAINT :
      if (IconInfo.hBitmapBits != NULL)
      {
        PAINTSTRUCT ps;

        BeginPaint(hWnd, (LPPAINTSTRUCT) &ps);
        EndPaint(hWnd, (LPPAINTSTRUCT) &ps);
#ifdef DEBUG
        DebugBitmap(hWnd, IconInfo.hBitmapBits);
#endif
        break;
      }
      /* else fall through ...*/

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


VOID PASCAL InvalidateAllWindows(void)
{
  InvalidateRect(hWndEdit, (LPRECT) NULL, FALSE);
  UpdateWindow(hWndEdit);
  InvalidateRect(hWndActualImage, (LPRECT) NULL, FALSE);
  UpdateWindow(hWndActualImage);
  if (hWndDebug)
  {
    InvalidateRect(hWndDebug, (LPRECT) NULL, FALSE);
    UpdateWindow(hWndDebug);
  }
}
