/*
 * $Header: /home/gene/library/website/docsrc/vwu/src/RCS/bitmap.c,v 395.1 2008/04/20 17:25:48 gene Exp $
 *
 * Copyright (c) 2005 Gene Michael Stover.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */
/*
 * You choose a bitmap file, & this program shows it in a window.
 * The purpose is to test that the FRAME module properly reads
 * bitmap files.
 */

#include "this.h"

/*
 */
static char *S_moduleName = __FILE__;

/*
 */
static MsgMap *S_map;

/*
 * Menu items
 */
enum {
  IDM_FILE_OPEN = 1              /* The Open item in the File drop-down menu */
};

/*
 * Window instance data
 */
typedef struct {
  char *magic;
  HWND child;
  HWND button;
  Frame *frame;
  HBITMAP ddb;
} Data;

/*
 */
static Data *
S_GetData (HWND wnd)
{
  Data *x;

  x = (Data *) GetProp (wnd, S_moduleName);
  return x;
}

/*
 * Given a Frame & the Device Context in which you'll draw it,
 * creates & returns a Device Dependant Bitmap.
 */
static HBITMAP
S_MakeDDB (Frame *frame, HDC dc)
{
  HBITMAP bitmap = NULL;

  bitmap = CreateDIBitmap (dc, frame->bmih, CBM_INIT, frame->pixels,
                           frame->bmi, DIB_RGB_COLORS);
  if (bitmap != NULL) {
    /* good */
  } else {
    LOG_LastError ("CreateDIBitmap", __FILE__, __LINE__);
  }
  return bitmap;
}

/*
 */
static LRESULT CALLBACK
S_OnCommandFileOpen (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  LRESULT rc = 0;
  FILE *fp;
  Data *x;
  OPENFILENAME openfilename;
  char pathname[500];
  HDC dc;

  fp = LOG_Open ();
  assert (wParam == IDM_FILE_OPEN);
  x = S_GetData (hwnd);
  if (x != NULL) {
    bzero (&openfilename, sizeof openfilename);
    /*
     * This restriction for OPENFILENAME.lStructSize is weird.
     * It's from MSDN:
     * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/commondialogboxlibrary/commondialogboxreference/commondialogboxstructures/openfilename.asp
     * I pasted it in here without putting much thought into it.
     */
#if defined(WINVER) && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
    openfilename.lStructSize = OPENFILENAME_SIZE_VERSION_400;
#else
    openfilename.lStructSize = sizeof openfilename;
#endif
    openfilename.hwndOwner = hwnd;
    openfilename.hInstance = CFG_Instance ();
    pathname[0] = '\0';
    openfilename.lpstrFile = pathname;
    openfilename.nMaxFile = sizeof pathname;
    if (GetOpenFileName (&openfilename)) {
      x->frame = FRAME_Destroy (x->frame);
      x->frame = FRAME_Load (pathname);
      if (x->ddb != NULL) DeleteObject (x->ddb);
      dc = GetDC (hwnd);
      x->ddb = S_MakeDDB (x->frame, dc);
      ReleaseDC (hwnd, dc);
      InvalidateRect (hwnd, NULL, TRUE);
    } else {
      /*
       * User probably cancelled.  So we do nothing.
       */
    }
  } else {
    MessageBox (hwnd, "Window's data is NULL", "Error", MB_OK);
    rc = 3;
  }
  if (fp != NULL) {
    fclose (fp);
  }
  return rc;
}

/*
 */
static LRESULT CALLBACK
S_OnCommand (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  LRESULT rc = 0;

  switch (wParam) {
  case IDM_FILE_OPEN:
    rc = S_OnCommandFileOpen (hwnd, uMsg, wParam, lParam);
    break;
  default:
    rc = DefWindowProc (hwnd, uMsg, wParam, lParam);
  }
  return rc;
}

/*
 */
static LRESULT CALLBACK
S_OnCreate (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  LRESULT rc = 0;
  Data *x;
  HMENU menu, popup;

  DefWindowProc (hwnd, uMsg, wParam, lParam);
  x = (Data *) xmalloc (sizeof *x);
  SetProp (hwnd, S_moduleName, x);
  bzero (x, sizeof *x);
  menu = CreateMenu ();
  popup = CreatePopupMenu ();
  AppendMenu (popup, MF_STRING, IDM_FILE_OPEN, "Open");
  AppendMenu (menu, MF_POPUP, (UINT) popup, "File");
  AppendMenu (menu, MF_STRING, (UINT) popup, "Help");
  SetMenu (hwnd, menu);
  x->child = CreateWindow ("STATIC", /* class name */
                           "static text goes here", /* window name */
                           WS_CHILD | WS_VISIBLE, /* style */
                           0, 0, 0, 0, /* pos & siz.  Figure them later. */
                           hwnd, /* parent */
                           NULL, /* menu */
                           CFG_Instance (),
                           NULL); /* lpParam, unused */
  if (x->child != NULL) {
    x->button = CreateWindow ("BUTTON", /* class name */
                              "Open", /* window name */
                              WS_CHILD | WS_VISIBLE, /* style */
                              0, 0, 0, 0, /* pos & siz.  Figure them later. */
                              hwnd, /* parent */
                              (HMENU) IDM_FILE_OPEN, /* button's id */
                              CFG_Instance (),
                              NULL); /* lpParam, unused */
    if (x->button != NULL) {
      /* good */
    } else {
      LOG_LastError ("CreateWindow", __FILE__, __LINE__);
      rc= 3;
    }
  } else {
    LOG_LastError ("CreateWindow", __FILE__, __LINE__);
    rc= 3;
  }
  return rc;
}

/*
 */
static LRESULT CALLBACK
S_OnDestroy (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  Data *x;

  x = (Data *) GetProp (hwnd, S_moduleName);
  if (x != NULL) {
    SetProp (hwnd, S_moduleName, NULL);
    if (x->frame != NULL) x->frame = FRAME_Destroy (x->frame);
    x = (Data *) xfree (x);
  }
  PostQuitMessage (0);
  return DefWindowProc (hwnd, uMsg, wParam, lParam);
}

/*
 */
static void
S_PaintChild (HWND child, HDC client, HDC mem, HBITMAP bm)
{
  BITMAP bitmap, bitmap2;
  unsigned long client_bitspixel, mem_bitspixel;
  char str[100];
  HBITMAP bm2;

  client_bitspixel = GetDeviceCaps (client, BITSPIXEL);
  mem_bitspixel = GetDeviceCaps (mem, BITSPIXEL);
  GetObject (bm, sizeof bitmap, &bitmap);
  bm2 = CreateCompatibleBitmap (client, 100, 100);
  GetObject (bm2, sizeof bitmap2, &bitmap2);
  sprintf (str, "client.bitspixel = %lu.  mem.bitspixel = %lu.  width = %d.  height = %d.  bits/pixel = %d.  bitmap2.bmBitsPixel = %d.",
           client_bitspixel,
           mem_bitspixel,
           (int) bitmap.bmWidth,
           (int) bitmap.bmHeight,
           (int) bitmap.bmBitsPixel,
           bitmap2.bmBitsPixel);
  SetWindowText (child, str);
  DeleteObject (bm2);
}

/*
 */
static LRESULT CALLBACK
S_OnPaint (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  LRESULT rc = 0;
  Data *x;
  HDC dc, mem;
  HBITMAP bitmap;

  if (DefWindowProc (hwnd, uMsg, wParam, lParam) == 0) {
    x = S_GetData (hwnd);
    if (x != NULL) {
      if (x->frame != NULL) {
        dc = GetDC (hwnd);
        mem = CreateCompatibleDC (dc);
        SelectObject (mem, x->ddb);
        BitBlt (dc, 0, 0, x->frame->bmih->biWidth, x->frame->bmih->biHeight,
                mem, 0, 0, SRCCOPY);
        S_PaintChild (x->child, dc, mem, x->ddb);
        DeleteDC (mem);
        ReleaseDC (hwnd, dc);
      } else {
        /*
         * We don't have a frame to display, so we don't bother
         * displaying anything.  Should we erase the window here?
         */
      }
    } else {
      /* Window's data is NULL */
      rc = 55;
    }
  } else {
    /* DefWindowProc failed */
    rc = 44;
  }
  return rc;
}

/*
 */
static LRESULT CALLBACK
S_OnSize (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  LRESULT rc = 0;
  Data *x;
  RECT rect;
  int top, height, width;

  x = S_GetData (hwnd);
  if (x != NULL) {
    GetClientRect (hwnd, &rect);
    /*
     * x->child is a static text.  It's at the bottom.
     * Wide as our client area.  Height is always 40.
     */
    width= rect.right - rect.left;
    height = 40;
    top = rect.bottom - height;
    SetWindowPos (x->child, HWND_TOP, 0, top, width, height, SWP_SHOWWINDOW);
    /*
     * The button is above the static text.  We do that so we
     * can see where the static text is.
     */
    width = 200;
    height = 66;
    top = top - height - 10;
    SetWindowPos (x->button, HWND_TOP, 10, top, width, height, SWP_SHOWWINDOW);
    /*
     * Force a redraw.  Experiments have shown that, without this,
     * it's possible to resize the window without redrawing the
     * button or the static text.  So we need this to force a clean
     * redraw.
     * It looks better if we do NOT erase the background.  That's
     * why FALSE in the third argument.
     */
    InvalidateRect (hwnd, NULL, FALSE);
  }
  rc = DefWindowProc (hwnd, uMsg, wParam, lParam);
  return rc;
}

/*
 */
static LRESULT CALLBACK
S_WindowProc (HWND wnd, WindowMessage msg, WPARAM wparam, LPARAM lparam)
{
  return GWP_WindowProc0 (wnd, msg, wparam, lparam, S_map, &DefWindowProc);
}

/*
 */
static int
S_Init (HINSTANCE hInstance)
{
  int rc = 0;
  WNDCLASS wndclass;

  setbuf (stdout, NULL);
  CFG_hInstance = hInstance;
  bzero (&wndclass, sizeof wndclass);
  wndclass.lpfnWndProc = &S_WindowProc;
  wndclass.hInstance = hInstance;
  wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
  wndclass.hbrBackground = GetStockObject (WHITE_BRUSH);
  wndclass.lpszClassName = S_moduleName;
  S_map = MSGMAP_Create ();
  MSGMAP_Insert (WM_COMMAND, &S_OnCommand, S_map);
  MSGMAP_Insert (WM_CREATE, &S_OnCreate, S_map);
  MSGMAP_Insert (WM_DESTROY, &S_OnDestroy, S_map);
  MSGMAP_Insert (WM_PAINT, &S_OnPaint, S_map);
  MSGMAP_Insert (WM_SIZE, &S_OnSize, S_map);
  if (RegisterClass (&wndclass)) {
    /* good */
  } else {
    printf ("\n%s:%d: RegisterClass failed", __FILE__, __LINE__);
    rc = 44;
  }
  return rc;
}

/*
 */
static HWND
S_CreateWindow (int nShowCmd)
{
  HWND wnd;

  LOG_TraceEntry (__FILE__, __LINE__, "S_CreateWindow");
  wnd = CreateWindow (S_moduleName, /* class name */
                      S_moduleName, /* window name */
                      WS_OVERLAPPEDWINDOW,
                      CW_USEDEFAULT, /* x position */
                      CW_USEDEFAULT, /* y position */
                      CW_USEDEFAULT, /* width */
                      CW_USEDEFAULT, /* height */
                      NULL, /* parent window */
                      NULL, /* menu */
                      CFG_Instance (),
                      NULL); /* not used */
  if (wnd != NULL) {
    LOG_TraceMoo (__FILE__, __LINE__, "S_CreateWindow");
    ShowWindow (wnd, nShowCmd);
  } else {
    LOG_LastError ("CreateWindow", __FILE__, __LINE__);
  }
  LOG_TraceExit (__FILE__, __LINE__, "S_CreateWindow");
  return wnd;
}

/*
 */
static int
S_Loop ()
{
  int rc = 0;
  MSG msg;

  while (rc == 0 && GetMessage (&msg, NULL, 0, 0)) {
    TranslateMessage (&msg);
    DispatchMessage (&msg);
  }
  return rc;
}

/*
 */
static void
S_Uninit ()
{
}

/*
 */
int WINAPI
WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, char lpCmdLine[],
         int nShowCmd)
{
  int rc = 0;
  HWND wnd;

  LOG_TraceMoo (__FILE__, __LINE__, "WinMain");
  if (S_Init (hInstance) == 0) {
    LOG_TraceMoo (__FILE__, __LINE__, "WinMain");
    wnd = S_CreateWindow (nShowCmd);
    LOG_TraceMoo (__FILE__, __LINE__, "WinMain");
    if (wnd != NULL) {
      LOG_TraceMoo (__FILE__, __LINE__, "WinMain");
      if (S_Loop () == 0) {
        /* good */
      } else {
        printf ("\n%s:%d: S_Loop failed", __FILE__, __LINE__);
        rc = -11;
      }
    } else {
      printf ("\n%s:%d: S_CreateWindow failed", __FILE__, __LINE__);
      rc = 40;
    }
    S_Uninit ();
  } else {
    printf ("\n%s:%d: S_Init failed", __FILE__, __LINE__);
    rc = 3;
  }
  return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

/* --- end of file --- */
