home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / GRAPHICS / THREEDOM.ZIP / WINLIB / WINLIB.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-27  |  20.2 KB  |  692 lines

  1. /* @(#)winlib.c 1.1   8/27/96 */
  2.  
  3. /******************************************************************************
  4. * WinLib: a simple Windows 95 GUI library                                     *
  5. * (C) Copyright 1996 by Philip Stephens                                       *
  6. * (C) Copyright 1996 by the IBM Corporation                                   *
  7. * All Rights Reserved                                                         *
  8. *                                                                             *
  9. * Permission to use, copy, modify, and distribute this software and its       *
  10. * documentation without fee for any non-commerical purpose is hereby granted, *
  11. * provided that the above copyright notice appears on all copies and that     *
  12. * both that copyright notice and this permission notice appear in all         *
  13. * supporting documentation.                                                   *
  14. *                                                                             *
  15. * NO REPRESENTATIONS ARE MADE ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY  *
  16. * PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.       *
  17. * NEITHER PHILIP STEPHENS OR IBM SHALL BE LIABLE FOR ANY DAMAGES SUFFERED BY  *
  18. * THE USE OF THIS SOFTWARE.                                                   *
  19. ******************************************************************************/
  20.  
  21. #define STRICT
  22. #define WIN32_LEAN_AND_MEAN
  23. #include <windows.h>
  24. #include <windowsx.h>
  25. #include <commdlg.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <stdarg.h>
  29. #include "winlib.h"
  30.  
  31. /*
  32.  * I don't want to hear warnings about unused parameters or local variables in
  33.  * functions.
  34.  */
  35. #pragma warn -aus
  36. #pragma warn -par
  37.  
  38. /*
  39.  * Type definition of a window node.
  40.  */
  41. typedef struct window_node {
  42.     HWND window_handle;
  43.     event_callbacks *callback_list;
  44.     WNDPROC default_window_proc;
  45.     struct window_node *next_window_node;
  46. } window_node;
  47.  
  48. /*
  49.  * Window list.
  50.  */
  51. static window_node *window_list;
  52.  
  53. /*
  54.  * Application instance.
  55.  */
  56. static HINSTANCE instance_handle;
  57.  
  58. /***********************************************************************
  59.  * Function to add a window and it's callback list to the window list. *
  60.  ***********************************************************************/
  61.  
  62. static BOOL
  63. AddWindowToList(HWND window_handle, event_callbacks *callback_list,
  64.                      WNDPROC default_window_proc)
  65. {
  66.     window_node *window_node_ptr;
  67.  
  68.     if ((window_node_ptr = (window_node *)malloc(sizeof(window_node)))
  69.         == NULL) {
  70.         MessageBox(NULL, "Unable to add window to event handler list",
  71.                "Fatal Error", MB_OK);
  72.         return(FALSE);
  73.     }
  74.     window_node_ptr->window_handle = window_handle;
  75.     window_node_ptr->callback_list = callback_list;
  76.     window_node_ptr->default_window_proc = default_window_proc;
  77.     window_node_ptr->next_window_node = window_list;
  78.     window_list = window_node_ptr;
  79.     return(TRUE);
  80. }
  81.  
  82. /***************************************************
  83.  * Function to locate a window in the window list. *
  84.  ***************************************************/
  85.  
  86. static window_node *
  87. FindWindowInList(HWND window_handle)
  88. {
  89.     window_node *window_node_ptr;
  90.  
  91.     window_node_ptr = window_list;
  92.     while (window_node_ptr) {
  93.         if (window_node_ptr->window_handle == window_handle)
  94.             return(window_node_ptr);
  95.         window_node_ptr = window_node_ptr->next_window_node;
  96.     }
  97.     return(NULL);
  98. }
  99.  
  100.  /*************************************************************************
  101.  * Function to handle events by looking up the window in the window list, *
  102.  * and executing the approapiate callback function for that window.       *
  103.  **************************************************************************/
  104.  
  105. /*
  106.  * Macro to do what HANDLE_MSG does, except for adding a case
  107.  * switch and asking for the window parameter.
  108.  */
  109. #define MY_HANDLE_MSG(message, fn) \
  110.     return(HANDLE_##message((window_handle), (wparam), (lparam), (fn)))
  111.  
  112. /*
  113.  * Macro to pass a button message to a handler along with the message type,
  114.  * so that we can handle button events all in one function.  Note that if
  115.  * the application wishes to forward a button message, they'll need to
  116.  * use a switch statement so that they can use the existing FORWARD macros in
  117.  * windowsx.h
  118.  */
  119. #define MY_HANDLE_WM_BUTTON(message, fn) \
  120.     return(((fn)((window_handle), (message), (int)LOWORD(lparam), \
  121.         (int)HIWORD(lparam), (UINT)(wparam)), 0L))
  122.  
  123. LRESULT CALLBACK
  124. HandleEvent(HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam)
  125. {
  126.     window_node *window_node_ptr;
  127.     WNDPROC default_window_proc;
  128.     event_callbacks *callback_list;
  129.  
  130.     /*
  131.      * The default window procedure is DefWindowProc().
  132.      */
  133.     default_window_proc = DefWindowProc;
  134.  
  135.     /*
  136.      * Check if we can handle the event using a function in the window's
  137.      * callback list.  Note that we don't handle WM_CREATE messages
  138.      * here because the window hasn't been added to the window list yet.
  139.      * Instead, we explicitly call the window create function when the
  140.      * window is being created.
  141.      */
  142.     if ((window_node_ptr = FindWindowInList(window_handle)) != NULL) {
  143.         default_window_proc = window_node_ptr->default_window_proc;
  144.         callback_list = window_node_ptr->callback_list;
  145.         switch(message) {
  146.         case WM_DESTROY:
  147.             if (callback_list->destroy_fn)
  148.                 MY_HANDLE_MSG(WM_DESTROY,
  149.                           callback_list->destroy_fn);
  150.             break;
  151.         case WM_PAINT:
  152.             if (callback_list->paint_fn)
  153.                 MY_HANDLE_MSG(WM_PAINT,
  154.                           callback_list->paint_fn);
  155.             break;
  156.         case WM_CHAR:
  157.             if (callback_list->char_fn)
  158.                 MY_HANDLE_MSG(WM_CHAR, callback_list->char_fn);
  159.             break;
  160.         case WM_KEYUP:
  161.             if (callback_list->key_fn)
  162.                 MY_HANDLE_MSG(WM_KEYUP, callback_list->key_fn);
  163.             break;
  164.         case WM_KEYDOWN:
  165.             if (callback_list->key_fn)
  166.                 MY_HANDLE_MSG(WM_KEYDOWN,
  167.                           callback_list->key_fn);
  168.             break;
  169.         case WM_LBUTTONDOWN:
  170.         case WM_MBUTTONDOWN:
  171.         case WM_RBUTTONDOWN:
  172.         case WM_LBUTTONUP:
  173.         case WM_MBUTTONUP:
  174.         case WM_RBUTTONUP:
  175.         case WM_LBUTTONDBLCLK:
  176.         case WM_MBUTTONDBLCLK:
  177.         case WM_RBUTTONDBLCLK:
  178.             if (callback_list->button_fn)
  179.                 MY_HANDLE_WM_BUTTON(message,
  180.                             callback_list->button_fn);
  181.             break;
  182.         case WM_MOUSEMOVE:
  183.             if (callback_list->mouse_fn)
  184.                 MY_HANDLE_MSG(WM_MOUSEMOVE,
  185.                           callback_list->mouse_fn);
  186.             break;
  187.         case WM_QUERYNEWPALETTE:
  188.             if (callback_list->focus_fn)
  189.                 MY_HANDLE_MSG(WM_QUERYNEWPALETTE,
  190.                           callback_list->focus_fn);
  191.             break;
  192.         case WM_COMMAND:
  193.             if (callback_list->command_fn)
  194.                 MY_HANDLE_MSG(WM_COMMAND,
  195.                           callback_list->command_fn);
  196.             break;
  197.         }
  198.     }
  199.  
  200.     /*
  201.      * If all else fails, call the default window procedure.
  202.      */
  203.     return(CallWindowProc(default_window_proc, window_handle, message,
  204.                   wparam, lparam));
  205. }
  206.  
  207. /***************************************
  208.  * Function to initialise the library. *
  209.  ***************************************/
  210.  
  211. void
  212. WinLibInit(HINSTANCE instance)
  213. {
  214.     WNDCLASS window_class;
  215.  
  216.     /*
  217.      * Initialise the window list and instance handle.
  218.      */
  219.     window_list = NULL;
  220.     instance_handle = instance;
  221.  
  222.     /*
  223.      * Define the main window class.
  224.      */
  225.     window_class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
  226.     window_class.lpfnWndProc = DefWindowProc;
  227.     window_class.cbClsExtra = 0;
  228.     window_class.cbWndExtra = 0;
  229.     window_class.hInstance = instance_handle;
  230.     window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  231.     window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
  232.     window_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  233.     window_class.lpszMenuName = NULL;
  234.     window_class.lpszClassName = "MainWindow";
  235.  
  236.     /*
  237.      * Register the main window class.
  238.      */
  239.     if (!RegisterClass(&window_class)) {
  240.         MessageBox(NULL, "Unable to register main window class",
  241.                "Fatal Error", MB_OK | MB_ICONEXCLAMATION);
  242.         return;
  243.     }
  244.  
  245.     /*
  246.      * Define the child window class.
  247.      */
  248.     window_class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC;
  249.     window_class.lpfnWndProc = DefWindowProc;
  250.     window_class.cbClsExtra = 0;
  251.     window_class.cbWndExtra = 0;
  252.     window_class.hInstance = instance_handle;
  253.     window_class.hIcon = NULL;
  254.     window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
  255.     window_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  256.     window_class.lpszMenuName = NULL;
  257.     window_class.lpszClassName = "ChildWindow";
  258.  
  259.     /*
  260.      * Register the child window class.
  261.      */
  262.     if (!RegisterClass(&window_class)) {
  263.         MessageBox(NULL, "Unable to register child window class",
  264.                "Fatal Error", MB_OK | MB_ICONEXCLAMATION);
  265.         return;
  266.     }
  267. }
  268.  
  269. /*****************************************************
  270.  * Function to clean up the library data structures. *
  271.  *****************************************************/
  272.  
  273. void
  274. WinLibCleanUp(void)
  275. {
  276.     window_node *window_node_ptr;
  277.  
  278.     while (window_list) {
  279.         window_node_ptr = window_list->next_window_node;
  280.         free(window_list);
  281.         window_list = window_node_ptr;
  282.     }
  283. }
  284.  
  285. /********************************************
  286.  * Function to create a window of any sort. *
  287.  ********************************************/
  288.  
  289. static HWND
  290. CreateNewWindow(LPCSTR window_class, LPCSTR window_title, UINT window_style,
  291.         UINT x, UINT y, UINT window_width, UINT window_height,
  292.         HWND parent_window_handle, HMENU menu_handle,
  293.         event_callbacks *callback_list)
  294. {
  295.     HWND window_handle;
  296.  
  297.     /*
  298.      * Create the window.
  299.      */
  300.     window_handle = CreateWindow(window_class, window_title, window_style,
  301.                      x, y, window_width, window_height,
  302.                      parent_window_handle, menu_handle,
  303.                      instance_handle, NULL);
  304.     if (!window_handle) {
  305.         MessageBox(NULL, "Unable to create a window", "Fatal Error",
  306.                MB_OK | MB_ICONEXCLAMATION);
  307.         return(NULL);
  308.     }
  309.  
  310.     /*
  311.      * If a callback list was provided, override the default window procedure
  312.      * with HandleEvent() and add the control to the window list, then call
  313.      * the create callback function.
  314.      */
  315.     if (callback_list) {
  316.         WNDPROC def_window_proc;
  317.  
  318.         def_window_proc = (WNDPROC)SetWindowLong(window_handle,
  319.                              GWL_WNDPROC,
  320.                             (LONG)HandleEvent);
  321.         if (!AddWindowToList(window_handle, callback_list,
  322.                      def_window_proc)) {
  323.             (void)SetWindowLong(window_handle, GWL_WNDPROC,
  324.                        (LONG)def_window_proc);
  325.             return(NULL);
  326.         }
  327.         if (callback_list->create_fn)
  328.             callback_list->create_fn(window_handle);
  329.     }
  330.  
  331.     /*
  332.      * Return the handle to the new window.
  333.      */
  334.     return(window_handle);
  335. }
  336.  
  337. /****************************************************
  338.  * Function to register and create the main window. *
  339.  ****************************************************/
  340.  
  341. HWND
  342. CreateMainWindow(LPCSTR window_title, UINT window_width, UINT window_height,
  343.          UINT window_state, LPCSTR menu_name,
  344.          event_callbacks *callback_list)
  345. {
  346.     HWND window_handle;
  347.     HMENU menu_handle;
  348.  
  349.     /*
  350.      * Load the menu from the resource file if a menu name was given.
  351.      */
  352.     if (menu_name) {
  353.         menu_handle = LoadMenu(instance_handle, menu_name);
  354.         if (!menu_handle) {
  355.             MessageBox(NULL, "Unable to load main window menu",
  356.                    "Fatal Error", MB_OK | MB_ICONEXCLAMATION);
  357.             return(NULL);
  358.         }
  359.     } else
  360.         menu_handle = NULL;
  361.  
  362.     /*
  363.      * Create the main window.
  364.      */
  365.     window_handle = CreateNewWindow("MainWindow", window_title,
  366.                     WS_OVERLAPPEDWINDOW,
  367.                     CW_USEDEFAULT, CW_USEDEFAULT,
  368.                     window_width, window_height, NULL,
  369.                     menu_handle, callback_list);
  370.  
  371.     /*
  372.      * Make the main window visible and generate a WM_PAINT event.
  373.      */
  374.     ShowWindow(window_handle, window_state);
  375.     UpdateWindow(window_handle);
  376.     return(window_handle);
  377. }
  378.  
  379. /**********************************************
  380.  * Function to create a generic child window. *
  381.  **********************************************/
  382.  
  383. HWND
  384. CreateChildWindow(HWND parent_window_handle, UINT x, UINT y,
  385.           UINT window_width, UINT window_height, UINT window_style,
  386.           event_callbacks *callback_list)
  387. {
  388.     HWND child_handle;
  389.  
  390.     /*
  391.      * Create the child window.
  392.      */
  393.     child_handle = CreateNewWindow("ChildWindow", NULL,
  394.                        WS_CHILD | WS_VISIBLE | window_style,
  395.                        x, y, window_width, window_height,
  396.                        parent_window_handle, NULL,
  397.                        callback_list);
  398.  
  399.     /*
  400.      * Return the handle to the child window.
  401.      */
  402.     return(child_handle);
  403. }
  404.  
  405. /****************************************
  406.  * Function to create a static control. *
  407.  ****************************************/
  408.  
  409. HWND
  410. CreateStaticControl(HWND parent_window_handle, UINT x, UINT y,
  411.             UINT window_width, UINT window_height, UINT window_style,
  412.             event_callbacks *callback_list)
  413. {
  414.     HWND child_handle;
  415.  
  416.     /*
  417.      * Create the static control.
  418.      */
  419.     child_handle = CreateNewWindow("static", NULL,
  420.                        WS_CHILD | WS_VISIBLE | window_style,
  421.                        x, y, window_width, window_height,
  422.                        parent_window_handle, NULL,
  423.                        callback_list);
  424.  
  425.     /*
  426.      * Return the handle to the static control.
  427.      */
  428.     return(child_handle);
  429. }
  430.  
  431. /***************************************
  432.  * Function to create an edit control. *
  433.  ***************************************/
  434.  
  435. HWND
  436. CreateEditControl(HWND parent_window_handle, UINT x, UINT y,
  437.           UINT window_width, UINT window_height, UINT window_style,
  438.           event_callbacks *callback_list)
  439. {
  440.     HWND child_handle;
  441.  
  442.     /*
  443.      * Create the edit control window.
  444.      */
  445.     child_handle = CreateNewWindow("edit", NULL,
  446.                        WS_CHILD | WS_VISIBLE | WS_VSCROLL |
  447.                        ES_LEFT | ES_MULTILINE | window_style,
  448.                        x, y, window_width, window_height,
  449.                        parent_window_handle, NULL,
  450.                        callback_list);
  451.  
  452.     /*
  453.      * Return the handle to the control window.
  454.      */
  455.     return(child_handle);
  456. }
  457.  
  458. /*************************************************************************
  459.  * Function to create a bitmap that can be displayed in a child window   *
  460.  * very quickly.  Note that the window must be of class ChildWindow as   *
  461.  * it must have a style of CS_OWNDC, otherwise the chosen colour palette *
  462.  * won't be correctly installed whenever the window gets input focus.    *
  463.  *************************************************************************/
  464.  
  465. fast_bitmap *
  466. CreateFastBitmap(HWND window_handle, UINT width, UINT height, RGBQUAD *palette,
  467.          UINT colours, UINT offset)
  468. {
  469.     HDC dc = GetDC(window_handle);
  470.     fast_bitmap *bitmap_ptr;
  471.     BITMAPINFOHEADER *bitmap_header;
  472.     RGBQUAD *bitmap_palette;
  473.     PALETTEENTRY *window_palette;
  474.     PALETTEENTRY system_palette[256];
  475.     int index;
  476.  
  477.     /*
  478.      * Allocate a fast bitmap structure.
  479.      */
  480.     bitmap_ptr = (fast_bitmap *)malloc(sizeof(fast_bitmap));
  481.     bitmap_ptr->window_handle = window_handle;
  482.     bitmap_ptr->bitmap_info = (BITMAPINFO *)malloc(sizeof(BITMAPINFO) +
  483.                                255 * sizeof(RGBQUAD));
  484.     bitmap_ptr->palette_info = (LOGPALETTE *)malloc(sizeof(LOGPALETTE) +
  485.                             255 *
  486.                             sizeof(PALETTEENTRY));
  487.  
  488.     /*
  489.      * Fill in the bitmap info header.
  490.      */
  491.     bitmap_header = &bitmap_ptr->bitmap_info->bmiHeader;
  492.     bitmap_header->biSize = sizeof(BITMAPINFOHEADER);
  493.     bitmap_header->biWidth = width;
  494.     bitmap_header->biHeight = -height;    /* top-down bitmap */
  495.     bitmap_header->biPlanes = 1;
  496.     bitmap_header->biBitCount = 8;
  497.     bitmap_header->biCompression = BI_RGB;
  498.     bitmap_header->biSizeImage = NULL;
  499.     bitmap_header->biXPelsPerMeter = NULL;
  500.     bitmap_header->biYPelsPerMeter = NULL;
  501.     bitmap_header->biClrUsed = 256;
  502.     bitmap_header->biClrImportant = 256;
  503.  
  504.     /*
  505.      * Retrieve the palette entries from the system palette.
  506.      */
  507.     GetSystemPaletteEntries(dc, 0, 256, (PALETTEENTRY *)&system_palette);
  508.  
  509.     /*
  510.      * Fill in the bitmap palette entries.  The first and last 10 entries
  511.      * are taken from the system palette, to ensure no colour remapping is
  512.      * done by Windows or the BitBlt function.
  513.      */
  514.     bitmap_palette = (RGBQUAD *)&bitmap_ptr->bitmap_info->bmiColors;
  515.     for (index = 0; index < 10; index++) {
  516.         bitmap_palette[index].rgbRed = system_palette[index].peRed;
  517.         bitmap_palette[index].rgbGreen = system_palette[index].peGreen;
  518.         bitmap_palette[index].rgbBlue = system_palette[index].peBlue;
  519.     }
  520.     for (index = offset; index < offset + colours; index++)
  521.         bitmap_palette[index] = palette[index];
  522.     for (index = 246; index < 256; index++) {
  523.         bitmap_palette[index].rgbRed = system_palette[index].peRed;
  524.         bitmap_palette[index].rgbGreen = system_palette[index].peGreen;
  525.         bitmap_palette[index].rgbBlue = system_palette[index].peBlue;
  526.     }
  527.  
  528.     /*
  529.      * Fill in the window palette entries.  Once again, the first and last 10
  530.      * entries come from the system palette, with the remaining 236 entries
  531.      * from the requested palette.  We set the PC_NOCOLLAPSE flag to ensure
  532.      * Windows doesn't try to remap any of the requested colours.
  533.      */
  534.     bitmap_ptr->palette_info->palVersion = 0x300;
  535.     bitmap_ptr->palette_info->palNumEntries = 256;
  536.     window_palette = (PALETTEENTRY *)&bitmap_ptr->palette_info->palPalEntry;
  537.     for (index = 0; index < 10; index++)
  538.         window_palette[index] = system_palette[index];
  539.     for (index = offset; index < offset + colours; index++) {
  540.         window_palette[index].peRed = palette[index].rgbRed;
  541.         window_palette[index].peGreen = palette[index].rgbGreen;
  542.         window_palette[index].peBlue = palette[index].rgbBlue;
  543.         window_palette[index].peFlags = PC_NOCOLLAPSE;
  544.     }
  545.     for (index = 246; index < 256; index++)
  546.         window_palette[index] = system_palette[index];
  547.  
  548.     /*
  549.      * Create the logical palette.
  550.      */
  551.     bitmap_ptr->palette_handle = CreatePalette(bitmap_ptr->palette_info);
  552.  
  553.     /*
  554.      * Create the bitmap.
  555.      */
  556.     bitmap_ptr->bitmap_handle = CreateDIBSection(dc,
  557.                              bitmap_ptr->bitmap_info,
  558.                              DIB_RGB_COLORS,
  559.                              (void **)&bitmap_ptr->bits,
  560.                              NULL, NULL);
  561.  
  562.     /*
  563.      * Release the window DC and return the fast bitmap pointer.
  564.      */
  565.     ReleaseDC(window_handle, dc);
  566.     return(bitmap_ptr);
  567. }
  568.  
  569. /**************************
  570.  * Destroy a fast bitmap. *
  571.  **************************/
  572.  
  573. void
  574. DestroyFastBitmap(fast_bitmap *bitmap_ptr)
  575. {
  576.     free(bitmap_ptr->bitmap_info);
  577.     free(bitmap_ptr->palette_info);
  578.     DeleteObject(bitmap_ptr->bitmap_handle);
  579.     DeleteObject(bitmap_ptr->palette_handle);
  580.     free(bitmap_ptr);
  581. }
  582.  
  583. /**************************
  584.  * Display a fast bitmap. *
  585.  **************************/
  586.  
  587. void
  588. DisplayFastBitmap(fast_bitmap *bitmap_ptr)
  589. {
  590.     HDC dc = GetDC(bitmap_ptr->window_handle);
  591.     HDC newdc = CreateCompatibleDC(NULL);
  592.     RECT dest;
  593.     HBITMAP prev_bitmap;
  594.  
  595.     GetClientRect(bitmap_ptr->window_handle, &dest);
  596.     prev_bitmap = SelectBitmap(newdc, bitmap_ptr->bitmap_handle);
  597.     BitBlt(dc, 0, 0, dest.right - dest.left, dest.bottom - dest.top, newdc,
  598.            0, 0, SRCCOPY);
  599.     (void)SelectBitmap(newdc, prev_bitmap);
  600.     DeleteDC(newdc);
  601.     ReleaseDC(bitmap_ptr->window_handle, dc);
  602. }
  603.  
  604. /******************************************************
  605.  * Install the palette associated with a fast bitmap. *
  606.  ******************************************************/
  607.  
  608. BOOL
  609. InstallFastBitmapPalette(fast_bitmap *bitmap_ptr)
  610. {
  611.     HDC dc;
  612.     UINT entries_changed;
  613.  
  614.     /*
  615.      * Realize the bitmap palette in the bitmap window.
  616.      */
  617.     dc = GetDC(bitmap_ptr->window_handle);
  618.     (void)SelectPalette(dc, bitmap_ptr->palette_handle, FALSE);
  619.     UnrealizeObject(bitmap_ptr->palette_handle);
  620.     entries_changed = RealizePalette(dc);
  621.     ReleaseDC(bitmap_ptr->window_handle, dc);
  622.  
  623.     /*
  624.      * If any palette entries changed, repaint the bitmap window.
  625.      */
  626.     if (entries_changed > 0)
  627.           InvalidateRect(bitmap_ptr->window_handle, NULL, TRUE);
  628.     return(entries_changed);
  629. }
  630.  
  631. /*****************************************************************************
  632.  * Function to display text in an edit control.  Normally used for read-only *
  633.  * edit controls.                                                            *
  634.  *****************************************************************************/
  635.  
  636. void
  637. DisplayMessage(HWND edit_handle, char *format, ...)
  638. {
  639.     va_list arg_ptr;
  640.     char message[1024];
  641.  
  642.     va_start(arg_ptr, format);
  643.     vsprintf(message, format, arg_ptr);
  644.     va_end(arg_ptr);
  645.  
  646.     SendMessage(edit_handle, EM_REPLACESEL, 0, (LPARAM)message);
  647. }
  648.  
  649. /**********************************************
  650.  * Function to implement the main event loop. *
  651.  **********************************************/
  652.  
  653. void
  654. EnterEventLoop(void (*work_proc_ptr)(void))
  655. {
  656.     MSG message;
  657.  
  658.     for (;;) {
  659.         while (!PeekMessage(&message, NULL, 0, 0, PM_NOREMOVE))
  660.             (*work_proc_ptr)();
  661.         if (!GetMessage(&message, NULL, 0, 0))
  662.             break;
  663.         TranslateMessage(&message);
  664.         DispatchMessage(&message);
  665.     }
  666. }
  667.  
  668. /*************************************************************************
  669.  * Display an Open File dialog box and return the user's file selection. *
  670.  *************************************************************************/
  671.  
  672. char *
  673. OpenFileDialog(char *title, char *filter)
  674. {
  675.     OPENFILENAME file_struct;
  676.     static char file_name[256];
  677.  
  678.     *file_name = '\0';
  679.     memset(&file_struct, 0, sizeof(OPENFILENAME));
  680.     file_struct.lStructSize = sizeof(OPENFILENAME);
  681.     file_struct.hInstance = instance_handle;
  682.     file_struct.lpstrFilter = filter;
  683.     file_struct.lpstrFile = file_name;
  684.     file_struct.nMaxFile = 256;
  685.     file_struct.lpstrTitle = title;
  686.     file_struct.Flags = OFN_FILEMUSTEXIST | OFN_NONETWORKBUTTON |
  687.                 OFN_HIDEREADONLY;
  688.     if (GetOpenFileName(&file_struct))
  689.         return((char *)file_name);
  690.     return(NULL);
  691. }
  692.