view win/dw.c @ 2909:3fe7641f027c

WARNING: Fix an API inconsistency in dw_notebook_page_destroy/set() These two APIs incorrectly referenced the page ID as an int not long. This change may cause ABI problems with programs that use these on some platforms. Recompile your apps that use these functions for safety.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Tue, 27 Dec 2022 00:58:58 +0000
parents 761b7a12b079
children edb4307ac7ce
line wrap: on
line source

/*
 * Dynamic Windows:
 *          A GTK like implementation of the Win32 GUI
 *
 * (C) 2000-2022 Brian Smith <brian@dbsoft.org>
 * (C) 2003-2021 Mark Hessling <mark@rexx.org>
 *
 */

#ifdef AEROGLASS
#define _WIN32_IE 0x0501
#define WINVER 0x501
#else
#define _WIN32_IE 0x0500
#define WINVER 0x500
#endif
#include <winsock2.h>
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <userenv.h>
#include <tchar.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <process.h>
#include <malloc.h>
#include <io.h>
#include <time.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include "dw.h"
#ifdef BUILD_DLL
#include "XBrowseForFolder.h"
#endif
#ifdef AEROGLASS
#include <uxtheme.h>
#endif
#include <richedit.h>

#ifdef RICHEDIT
int _DW_MLE_RICH_EDIT = DW_FEATURE_UNSUPPORTED;
#endif

#define STATICCLASSNAME TEXT("STATIC")
#define COMBOBOXCLASSNAME TEXT("COMBOBOX")
#define LISTBOXCLASSNAME TEXT("LISTBOX")
#define BUTTONCLASSNAME TEXT("BUTTON")
#define POPUPMENUCLASSNAME TEXT("POPUPMENU")
#define EDITCLASSNAME TEXT("EDIT")
#define FRAMECLASSNAME TEXT("FRAME")
#define SCROLLBARCLASSNAME TEXT("SCROLLBAR")

#define ClassName TEXT("dynamicwindows")
#define SplitbarClassName TEXT("dwsplitbar")
#define ObjectClassName TEXT("dwobjectclass")
#define BrowserClassName TEXT("dwbrowserclass")
#define ScrollClassName TEXT("dwscrollclass")
#define StatusbarClassName TEXT("dwstatusbar")
#define DefaultFont NULL

#ifdef GDIPLUS
/* GDI+ Headers are not C compatible... so define what we need here instead */
struct GdiplusStartupInput
{
    UINT32 GdiplusVersion;
    void *DebugEventCallback;
    BOOL SuppressBackgroundThread;
    BOOL SuppressExternalCodecs;
};

typedef enum  {
  Ok                          = 0,
  GenericError                = 1,
  InvalidParameter            = 2,
  OutOfMemory                 = 3,
  ObjectBusy                  = 4,
  InsufficientBuffer          = 5,
  NotImplemented              = 6,
  Win32Error                  = 7,
  WrongState                  = 8,
  Aborted                     = 9,
  FileNotFound                = 10,
  ValueOverflow               = 11,
  AccessDenied                = 12,
  UnknownImageFormat          = 13,
  FontFamilyNotFound          = 14,
  FontStyleNotFound           = 15,
  NotTrueTypeFont             = 16,
  UnsupportedGdiplusVersion   = 17,
  GdiplusNotInitialized       = 18,
  PropertyNotFound            = 19,
  PropertyNotSupported        = 20,
  ProfileNotFound             = 21
} GpStatus;

typedef enum {
  UnitWorld,
  UnitDisplay,
  UnitPixel,
  UnitPoint,
  UnitInch,
  UnitDocument,
  UnitMillimeter
} GpUnit;

typedef enum  {
  FlushIntentionFlush   = 0,
  FlushIntentionSync    = 1
} GpFlushIntention;

typedef enum {
  QualityModeInvalid  = -1,
  QualityModeDefault  = 0,
  QualityModeLow      = 1,
  QualityModeHigh     = 2
} QualityMode;

typedef enum  {
  SmoothingModeInvalid     = QualityModeInvalid,
  SmoothingModeDefault     = QualityModeDefault,
  SmoothingModeHighSpeed   = QualityModeLow,
  SmoothingModeHighQuality = QualityModeHigh,
  SmoothingModeNone,
  SmoothingModeAntiAlias
} SmoothingMode;

typedef void GpGraphics;
typedef void GpPen;
typedef void GpBrush;
typedef void GpBitmap;
typedef void GpImage;
typedef POINT GpPoint;
typedef DWORD ARGB;
typedef float REAL;

#define ALPHA_SHIFT 24
#define RED_SHIFT   16
#define GREEN_SHIFT 8
#define BLUE_SHIFT  0
#define ALPHA_MASK  ((ARGB) 0xff << ALPHA_SHIFT)

#define MAKEARGB(a, r, g, b) \
                (((ARGB) ((a) & 0xff) << ALPHA_SHIFT)| \
                 ((ARGB) ((r) & 0xff) << RED_SHIFT)  | \
                 ((ARGB) ((g) & 0xff) << GREEN_SHIFT)| \
                 ((ARGB) ((b) & 0xff) << BLUE_SHIFT))

/* Graphic conversion functions */
GpStatus WINAPI GdipCreateBitmapFromFile(const WCHAR* filename, GpBitmap **bitmap);
GpStatus WINAPI GdipCreateHBITMAPFromBitmap(GpBitmap *bitmap, HBITMAP* hbmReturn, DWORD background);
GpStatus WINAPI GdipCreateHICONFromBitmap(GpBitmap *bitmap, HICON *hbmReturn);
GpStatus WINAPI GdipDisposeImage(GpImage *image);
GpStatus WINAPI GdipGetImagePixelFormat(GpImage *image, INT *format);
/* Startup and Shutdown */
GpStatus WINAPI GdiplusStartup(ULONG_PTR *token, const struct GdiplusStartupInput *input, void *output);
VOID WINAPI GdiplusShutdown(ULONG_PTR token);
/* Drawing functions */
GpStatus WINAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics);
GpStatus WINAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics);
GpStatus WINAPI GdipDeleteGraphics(GpGraphics *graphics);
GpStatus WINAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode smoothingMode);
GpStatus WINAPI GdipCreatePen1(ARGB color, REAL width, GpUnit unit, GpPen **pen);
GpStatus WINAPI GdipDeletePen(GpPen *pen);
GpStatus WINAPI GdipCreateSolidFill(ARGB color, GpBrush **brush);
GpStatus WINAPI GdipDeleteBrush(GpBrush *brush);
GpStatus WINAPI GdipSetSolidFillColor(GpBrush *brush, ARGB color);
GpStatus WINAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1, INT y1, INT x2, INT y2);
GpStatus WINAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, const GpPoint *points, INT count);
GpStatus WINAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x, INT y, INT width, INT height);
GpStatus WINAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush, INT x, INT y, INT width, INT height);
GpStatus WINAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x, INT y, INT width, INT height, REAL startAngle, REAL sweepAngle);
GpStatus WINAPI GdipDrawPolygonI(GpGraphics *graphics, GpPen *pen, const GpPoint *points, INT count);
GpStatus WINAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush, const GpPoint *points, INT count);
GpStatus WINAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x, INT y, INT width, INT height);
GpStatus WINAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x, INT y, INT width, INT height);
GpStatus WINAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention);

/* Pixel format information */
#define    PixelFormatIndexed      0x00010000
#define    PixelFormatGDI          0x00020000
#define    PixelFormatAlpha        0x00040000
#define    PixelFormatPAlpha       0x00080000
#define    PixelFormatExtended     0x00100000
#define    PixelFormatCanonical    0x00200000

#define    PixelFormatUndefined       0
#define    PixelFormatDontCare        0

#define    PixelFormat1bppIndexed     (1 | ( 1 << 8) | PixelFormatIndexed | PixelFormatGDI)
#define    PixelFormat4bppIndexed     (2 | ( 4 << 8) | PixelFormatIndexed | PixelFormatGDI)
#define    PixelFormat8bppIndexed     (3 | ( 8 << 8) | PixelFormatIndexed | PixelFormatGDI)
#define    PixelFormat16bppGrayScale  (4 | (16 << 8) | PixelFormatExtended)
#define    PixelFormat16bppRGB555     (5 | (16 << 8) | PixelFormatGDI)
#define    PixelFormat16bppRGB565     (6 | (16 << 8) | PixelFormatGDI)
#define    PixelFormat16bppARGB1555   (7 | (16 << 8) | PixelFormatAlpha | PixelFormatGDI)
#define    PixelFormat24bppRGB        (8 | (24 << 8) | PixelFormatGDI)
#define    PixelFormat32bppRGB        (9 | (32 << 8) | PixelFormatGDI)
#define    PixelFormat32bppARGB       (10 | (32 << 8) | PixelFormatAlpha | PixelFormatGDI | PixelFormatCanonical)
#define    PixelFormat32bppPARGB      (11 | (32 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatGDI)
#define    PixelFormat48bppRGB        (12 | (48 << 8) | PixelFormatExtended)
#define    PixelFormat64bppARGB       (13 | (64 << 8) | PixelFormatAlpha  | PixelFormatCanonical | PixelFormatExtended)
#define    PixelFormat64bppPARGB      (14 | (64 << 8) | PixelFormatAlpha  | PixelFormatPAlpha | PixelFormatExtended)
#define    PixelFormat32bppCMYK       (15 | (32 << 8))

/* Token to the GDI+ Instance */
ULONG_PTR gdiplusToken;
#endif

#ifdef AEROGLASS
HRESULT (WINAPI *_DwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS *pMarInset) = 0;
HRESULT (WINAPI *_DwmIsCompositionEnabled)(BOOL *pfEnabled) = 0;
HRESULT (WINAPI *_DwmSetWindowAttribute)(HWND, DWORD, LPCVOID, DWORD) = 0;
BOOL (WINAPI *_DwmDefWindowProc)(HWND, UINT, WPARAM, LPARAM, LRESULT *) = 0;
HTHEME (WINAPI *_OpenThemeData)(HWND hwnd, LPCWSTR pszClassList) = 0;
HPAINTBUFFER (WINAPI *_BeginBufferedPaint)(HDC hdcTarget, const RECT *prcTarget, BP_BUFFERFORMAT dwFormat, BP_PAINTPARAMS *pPaintParams, HDC *phdc) = 0;
HRESULT (WINAPI *_BufferedPaintSetAlpha)(HPAINTBUFFER hBufferedPaint, const RECT *prc, BYTE alpha) = 0;
HRESULT (WINAPI *_DrawThemeTextEx)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, DWORD dwFlags, LPRECT pRect, const DTTOPTS *pOptions) = 0;
HRESULT (WINAPI *_EndBufferedPaint)(HPAINTBUFFER hBufferedPaint, BOOL fUpdateTarget) = 0;
HRESULT (WINAPI *_CloseThemeData)(HTHEME hTheme) = 0;
HRESULT (WINAPI *_GetThemeSysFont)(HTHEME hTheme, int iFontId, LOGFONTW *plf) = 0;
DWORD(WINAPI *_GetImmersiveColorFromColorSetEx)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, BOOL bIgnoreHighContrast, UINT dwHighContrastCacheMode) = 0;
int (WINAPI *_GetImmersiveColorTypeFromName)(const WCHAR *name) = 0;
int (WINAPI *_GetImmersiveUserColorSetPreference)(BOOL bForceCheckRegistry, BOOL bSkipCheckOnFail) = 0;
BOOL _dw_composition = FALSE;
COLORREF _dw_transparencykey = RGB(200,201,202);
HANDLE hdwm = 0;
#endif
/* Aero related but separate functions and handles */
HRESULT (WINAPI *_SetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList) = 0;
HANDLE huxtheme = 0;

/* Needed for Rich Edit controls */
HANDLE hrichedit = 0;
HANDLE hmsftedit = 0;

/*
 * MinGW Is missing a bunch of definitions
 * so #define them here...
 */

#if !defined( _tstol )
#if defined(UNICODE)
# define _tstol _wtol
#else
# define _tstol atol
#endif
#endif
#if !defined(PBS_MARQUEE)
# define PBS_MARQUEE 0x08
#endif
#if !defined(PBM_SETMARQUEE)
# define PBM_SETMARQUEE (WM_USER+10)
#endif
#if !defined(LVS_EX_DOUBLEBUFFER)
# define LVS_EX_DOUBLEBUFFER 0x10000
#endif

HWND _dw_popup = (HWND)NULL, DW_HWND_OBJECT = (HWND)NULL, _dw_tooltip = (HWND)NULL;

HINSTANCE _DWInstance = NULL;

DWORD _dwVersion = 0, _dwComctlVer = 0;
DWTID _dwtid = -1;
SECURITY_DESCRIPTOR _dwsd;

#define PACKVERSION(major,minor) MAKELONG(minor,major)

#define IS_XPPLUS (_dwComctlVer >= PACKVERSION(5,82))
#define IS_VISTAPLUS (_dwComctlVer >= PACKVERSION(6,10))
#define IS_WIN7PLUS (PACKVERSION(LOBYTE(LOWORD(_dwVersion)),HIBYTE(LOWORD(_dwVersion))) >= PACKVERSION(6,1))
#define IS_WIN8PLUS (PACKVERSION(LOBYTE(LOWORD(_dwVersion)),HIBYTE(LOWORD(_dwVersion))) >= PACKVERSION(6,2))
#define IS_WIN81PLUS (PACKVERSION(LOBYTE(LOWORD(_dwVersion)),HIBYTE(LOWORD(_dwVersion))) >= PACKVERSION(6,3))
#define IS_WIN10PLUS (PACKVERSION(LOBYTE(LOWORD(_dwVersion)),HIBYTE(LOWORD(_dwVersion))) >= PACKVERSION(10,0))

#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif

#define _DW_DATA_TYPE_STRING  0
#define _DW_DATA_TYPE_POINTER 1

/*
 * For the dw*from_data() functions, a temporary file is required to write
 * the contents of the image to so it can be loaded by the Win32 API
 * We use _tempnam() which uses TMP env variable by default. It can be passed
 * an alternate temporary directory if TMP is not set, so we get the value
 * of TEMP and store it here.
 */
static char _dw_alternate_temp_dir[MAX_PATH+1] = {0};
static char _dw_exec_dir[MAX_PATH+1] = {0};
static char _dw_app_id[_DW_APP_ID_SIZE+1]= {0};
static char _dw_app_name[_DW_APP_ID_SIZE+1]= {0};

int main(int argc, char *argv[]);

#define ICON_INDEX_LIMIT 200
HICON _dw_icon_lookup[200];
HIMAGELIST _dw_image_list_small  = 0, _dw_image_list_large = 0;

/* Special flag used for internal tracking */
#define DW_CFA_RESERVED (1 << 30)

DWORD _dw_foreground;
DWORD _dw_background;
DWORD _dw_hpen;
DWORD _dw_hbrush;
#ifdef GDIPLUS
DWORD _dw_gppen;
DWORD _dw_gpbrush;
#endif

BYTE _dw_red[] = {   0x00, 0xbb, 0x00, 0xaa, 0x00, 0xbb, 0x00, 0xaa, 0x77,
           0xff, 0x00, 0xee, 0x00, 0xff, 0x00, 0xff, 0xaa, 0x00 };
BYTE _dw_green[] = { 0x00, 0x00, 0xbb, 0xaa, 0x00, 0x00, 0xbb, 0xaa, 0x77,
           0x00, 0xff, 0xee, 0x00, 0x00, 0xee, 0xff, 0xaa, 0x00 };
BYTE _dw_blue[] = {  0x00, 0x00, 0x00, 0x00, 0xcc, 0xbb, 0xbb, 0xaa, 0x77,
            0x00, 0x00, 0x00, 0xff, 0xff, 0xee, 0xff, 0xaa, 0x00};

HBRUSH _dw_colors[18];

HFONT _DWDefaultFont = NULL;

#if (defined(BUILD_DLL) || defined(BUILD_HTML))
LRESULT CALLBACK _dw_browserwndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#ifdef BUILD_EDGE
BOOL _dw_edge_detect(LPWSTR AppID);
BOOL _DW_EDGE_DETECTED = FALSE;
LRESULT CALLBACK _dw_edgewndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#endif
#endif
#ifdef BUILD_TOAST
void _dw_toast_init(LPWSTR AppName, LPWSTR AppID);
void *_dw_notification_new(LPWSTR title, LPWSTR image, LPWSTR description);
int _dw_notification_send(void *notification);
BOOL _dw_toast_is_compatible(void);
#endif 
LRESULT CALLBACK _dw_colorwndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2);
void _dw_resize_notebook_page(HWND handle, int pageid);
void _dw_handle_splitbar_resize(HWND hwnd, float percent, int type, int x, int y);
int _dw_lookup_icon(HWND handle, HICON hicon, int type);
HFONT _dw_acquire_font(HWND handle, const char *fontname);
void _dw_click_default(HWND handle);
void _dw_do_resize(Box *thisbox, int x, int y, int xborder, int yborder);

/* Internal function to queue a window redraw */
void _dw_redraw(HWND window, int skip)
{
    static HWND lastwindow = 0;
    HWND redraw = lastwindow;

    if(skip && !window)
        return;

    lastwindow = window;
    if(redraw != lastwindow && redraw)
    {
        dw_window_redraw(redraw);
    }
}

typedef struct _dwsighandler
{
   struct _dwsighandler   *next;
   ULONG message;
   HWND window;
   int id;
   void *signalfunction;
   void *discfunction;
   void *data;

} DWSignalHandler;

DWSignalHandler *Root = NULL;

typedef struct
{
   ULONG message;
   char name[30];

} DWSignalList;

static int in_checkbox_handler = 0;

/* List of signals and their equivalent Win32 message */
#define SIGNALMAX 19

DWSignalList DWSignalTranslate[SIGNALMAX] = {
   { WM_SIZE,         DW_SIGNAL_CONFIGURE },
   { WM_CHAR,         DW_SIGNAL_KEY_PRESS },
   { WM_LBUTTONDOWN,  DW_SIGNAL_BUTTON_PRESS },
   { WM_LBUTTONUP,    DW_SIGNAL_BUTTON_RELEASE },
   { WM_MOUSEMOVE,    DW_SIGNAL_MOTION_NOTIFY },
   { WM_CLOSE,        DW_SIGNAL_DELETE },
   { WM_PAINT,        DW_SIGNAL_EXPOSE },
   { WM_COMMAND,      DW_SIGNAL_CLICKED },
   { NM_DBLCLK,       DW_SIGNAL_ITEM_ENTER },
   { NM_RCLICK,       DW_SIGNAL_ITEM_CONTEXT },
   { LBN_SELCHANGE,   DW_SIGNAL_LIST_SELECT },
   { TVN_SELCHANGED,  DW_SIGNAL_ITEM_SELECT },
   { WM_SETFOCUS,     DW_SIGNAL_SET_FOCUS },
   { WM_VSCROLL,      DW_SIGNAL_VALUE_CHANGED },
   { TCN_SELCHANGE,   DW_SIGNAL_SWITCH_PAGE },
   { LVN_COLUMNCLICK, DW_SIGNAL_COLUMN_CLICK },
   { TVN_ITEMEXPANDED,DW_SIGNAL_TREE_EXPAND },
   { WM_USER+100,     DW_SIGNAL_HTML_RESULT },
   { WM_USER+101,     DW_SIGNAL_HTML_CHANGED }
};

#ifdef BUILD_DLL
/* Old function to store the instance handle, kept for compatibilty. */
void Win32_Set_Instance(HINSTANCE hInstance)
{
   _DWInstance = hInstance;
}
#endif

/*
 * Internal function to convert WinMain arguments into main() style.
 * Also saves the handle to the instance passed from WinMain().
 */
char ** API _dw_convertargs(int *count, char *start, HINSTANCE hInstance)
{
   char *tmp, *argstart, **argv;
   int loc = 0, inquotes = 0;

   _DWInstance = hInstance;
   (*count) = 1;

   tmp = start;

   /* Count the number of entries */
   if(*start)
   {
      (*count)++;

      while(*tmp)
      {
         if(*tmp == '"' && inquotes)
            inquotes = 0;
         else if(*tmp == '"' && !inquotes)
            inquotes = 1;
         else if(*tmp == ' ' && !inquotes)
         {
            /* Push past any white space */
            while(*(tmp+1) == ' ')
               tmp++;
            /* If we aren't at the end of the command
             * line increment the count.
             */
            if(*(tmp+1))
               (*count)++;
         }
         tmp++;
      }
   }

   argv = (char **)calloc(sizeof(char *), ((*count)+1));
   argv[0] = calloc(261, 1);
   GetModuleFileNameA(_DWInstance, argv[0], 260);

   argstart = tmp = start;

   if(*start)
   {
      loc = 1;

      while(*tmp)
      {
         if(*tmp == '"' && inquotes)
         {
            *tmp = 0;
            inquotes = 0;
         }
         else if(*tmp == '"' && !inquotes)
         {
            argstart = tmp+1;
            inquotes = 1;
         }
         else if(*tmp == ' ' && !inquotes)
         {
            *tmp = 0;
            argv[loc] = _strdup(argstart);

            /* Push past any white space */
            while(*(tmp+1) == ' ')
               tmp++;

            /* Move the start pointer */
            argstart = tmp+1;

            /* If we aren't at the end of the command
             * line increment the count.
             */
            if(*(tmp+1))
               loc++;
         }
         tmp++;
      }
      if(*argstart)
         argv[loc] = _strdup(argstart);
   }
   argv[loc+1] = NULL;
   return argv;
}

#ifdef UNICODE
/* Macro and internal function to convert UTF8 to Unicode wide characters */
#define UTF8toWide(a) _dw_UTF8toWide(a, a ? _alloca(MultiByteToWideChar(CP_UTF8, 0, a, -1, NULL, 0) * sizeof(WCHAR)) : NULL)
LPWSTR _dw_UTF8toWide(const char *utf8string, void *outbuf)
{
   LPWSTR retbuf = outbuf;

   if(retbuf)
      MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, retbuf, MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, NULL, 0) * sizeof(WCHAR));
   return retbuf;
}
#define WideToUTF8(a) _dw_WideToUTF8(a, a ? _alloca(WideCharToMultiByte(CP_UTF8, 0, a, -1, NULL, 0, NULL, NULL)) : NULL)
char *_dw_WideToUTF8(LPCWSTR widestring, void *outbuf)
{
   char *retbuf = outbuf;

   if(retbuf)
      WideCharToMultiByte(CP_UTF8, 0, widestring, -1, retbuf, WideCharToMultiByte(CP_UTF8, 0, widestring, -1, NULL, 0, NULL, NULL), NULL, NULL);
   return retbuf;
}
#else
#define UTF8toWide(a) a
#define WideToUTF8(a) a
#endif

DWORD _dw_get_dll_version(LPCTSTR lpszDllName)
{

   HINSTANCE hinstDll;
   DWORD dwVersion = 0;

   hinstDll = LoadLibrary(lpszDllName);

   if(hinstDll)
   {
      DLLGETVERSIONPROC pDllGetVersion;

      pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hinstDll, "DllGetVersion");

      /* Because some DLLs might not implement this function, you
       * must test for it explicitly. Depending on the particular
       * DLL, the lack of a DllGetVersion function can be a useful
       * indicator of the version.
       */
      if(pDllGetVersion)
      {
         DLLVERSIONINFO dvi;
         HRESULT hr;

         ZeroMemory(&dvi, sizeof(dvi));
         dvi.cbSize = sizeof(dvi);

            hr = (*pDllGetVersion)(&dvi);

         if(SUCCEEDED(hr))
         {
            dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
         }
      }

      FreeLibrary(hinstDll);
   }
    return dwVersion;
}

#ifdef GDIPLUS
/*
 * List those icons that have transparency first
 * GDI+ List of supported formats: BMP, ICON, GIF, JPEG, Exif, PNG, TIFF, WMF, and EMF.
 * Not sure if we should include all these or not... maybe we should add TIFF and GIF?
 */
#define NUM_EXTS 8
char *image_exts[NUM_EXTS+1] =
{
   "",
   ".png",
   ".ico",
   ".gif",
   ".jpg",
   ".jpeg",
   ".tiff",
   ".bmp",
   NULL
};

/* Section for loading files of types besides BMP and ICO and return HBITMAP or HICON */
void *_dw_load_gpbitmap(const char *filename)
{
   int i, wclen = (int)(strlen(filename) + 6) * sizeof(wchar_t);
   char *file = _alloca(strlen(filename) + 6);
   wchar_t *wfile = _alloca(wclen);
   void *image;

   /* Try various extentions */
   for ( i = 0; i < NUM_EXTS; i++ )
   {
      strcpy( file, filename );
      strcat( file, image_exts[i] );
      if ( _access( file, 04 ) == 0 )
      {
         /* Convert to wide format */
         MultiByteToWideChar(CP_ACP, 0, file, (int)strlen(file)+1, wfile, wclen);
         if(!GdipCreateBitmapFromFile(wfile, &image))
             return image;
      }
   }
   return NULL;
}

/* Try to load the appropriate image and return the HBITMAP handle */
HBITMAP _dw_load_bitmap(const char *filename, unsigned long *depth)
{
    void *bitmap = _dw_load_gpbitmap(filename);
    if(bitmap)
    {
        HBITMAP hbm = NULL;

        if(!GdipCreateHBITMAPFromBitmap(bitmap, &hbm, 0))
        {
            if(depth)
            {
                INT pf;

                /* Default to 0 */
                *depth = 0;

                /* Query the pixel format so we can determine the depth */
                if(!GdipGetImagePixelFormat(bitmap, &pf))
                {
                    switch(pf)
                    {
                    case PixelFormat1bppIndexed:
                        *depth = 1;
                        break;
                    case PixelFormat4bppIndexed:
                        *depth = 4;
                        break;
                    case PixelFormat8bppIndexed:
                        *depth = 8;
                        break;
                    case PixelFormat16bppGrayScale:
                    case PixelFormat16bppRGB555:
                    case PixelFormat16bppRGB565:
                    case PixelFormat16bppARGB1555:
                        *depth = 16;
                        break;
                    case PixelFormat24bppRGB:
                        *depth = 24;
                        break;
                    case PixelFormat32bppRGB:
                    case PixelFormat32bppARGB:
                    case PixelFormat32bppPARGB:
                    case PixelFormat32bppCMYK:
                        *depth = 32;
                        break;
                    case PixelFormat48bppRGB:
                        *depth = 48;
                        break;
                    case PixelFormat64bppARGB:
                    case PixelFormat64bppPARGB:
                        *depth = 64;
                        break;
                    }
                }
            }
        }
        GdipDisposeImage(bitmap);
        return hbm;
    }
    return NULL;
}

/* Try to load the appropriate image and return the HICON handle */
HICON _dw_load_icon(const char *filename)
{
    void *bitmap = _dw_load_gpbitmap(filename);
    if(bitmap)
    {
        HICON hicon = NULL;

        GdipCreateHICONFromBitmap(bitmap, &hicon);
        GdipDisposeImage(bitmap);
        return hicon;
    }
    return NULL;
}
#endif

#ifdef AEROGLASS
/* Set _DW_DARK_MODE_ALLOWED to FALSE to disable dark mode.
 * Set _DW_DARK_MODE_ALLOWED to TRUE for basic dark mode.
 * Set _DW_DARK_MODE_ALLOWED to 2 for full dark mode.
 * Set _DW_DARK_MODE_ALLOWED to 3 for forced full dark mode.
 */
int _DW_DARK_MODE_ALLOWED = TRUE;
int _DW_DARK_MODE_SUPPORTED = FALSE;
int _DW_DARK_MODE_ENABLED = FALSE;

typedef enum IMMERSIVE_HC_CACHE_MODE
{
   IHCM_USE_CACHED_VALUE,
   IHCM_REFRESH
} IMMERSIVE_HC_CACHE_MODE;

typedef enum _PreferredAppMode
{
   _Default,
   _AllowDark,
   _ForceDark,
   _ForceLight,
   _Max
} _PreferredAppMode;

typedef enum _WINDOWCOMPOSITIONATTRIB
{
   WCA_UNDEFINED = 0,
   WCA_NCRENDERING_ENABLED = 1,
   WCA_NCRENDERING_POLICY = 2,
   WCA_TRANSITIONS_FORCEDISABLED = 3,
   WCA_ALLOW_NCPAINT = 4,
   WCA_CAPTION_BUTTON_BOUNDS = 5,
   WCA_NONCLIENT_RTL_LAYOUT = 6,
   WCA_FORCE_ICONIC_REPRESENTATION = 7,
   WCA_EXTENDED_FRAME_BOUNDS = 8,
   WCA_HAS_ICONIC_BITMAP = 9,
   WCA_THEME_ATTRIBUTES = 10,
   WCA_NCRENDERING_EXILED = 11,
   WCA_NCADORNMENTINFO = 12,
   WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
   WCA_VIDEO_OVERLAY_ACTIVE = 14,
   WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
   WCA_DISALLOW_PEEK = 16,
   WCA_CLOAK = 17,
   WCA_CLOAKED = 18,
   WCA_ACCENT_POLICY = 19,
   WCA_FREEZE_REPRESENTATION = 20,
   WCA_EVER_UNCLOAKED = 21,
   WCA_VISUAL_OWNER = 22,
   WCA_HOLOGRAPHIC = 23,
   WCA_EXCLUDED_FROM_DDA = 24,
   WCA_PASSIVEUPDATEMODE = 25,
   WCA_USEDARKMODECOLORS = 26,
   WCA_LAST = 27
} _WINDOWCOMPOSITIONATTRIB;

typedef struct _WINDOWCOMPOSITIONATTRIBDATA
{
   _WINDOWCOMPOSITIONATTRIB Attrib;
   PVOID pvData;
   SIZE_T cbData;
} _WINDOWCOMPOSITIONATTRIBDATA;

HTHEME (WINAPI * _OpenNcThemeData)(HWND, LPCWSTR) = NULL;
VOID (WINAPI * _RefreshImmersiveColorPolicyState)(VOID) = NULL;
BOOL (WINAPI * _GetIsImmersiveColorUsingHighContrast)(IMMERSIVE_HC_CACHE_MODE) = NULL;
BOOL (WINAPI * _ShouldAppsUseDarkMode)(VOID) = NULL;
BOOL (WINAPI * _AllowDarkModeForWindow)(HWND, BOOL) = NULL;
BOOL (WINAPI * _AllowDarkModeForApp)(BOOL) = NULL;
_PreferredAppMode (WINAPI * _SetPreferredAppMode)(_PreferredAppMode) = NULL;
BOOL (WINAPI * _IsDarkModeAllowedForWindow)(HWND) = NULL;
BOOL (WINAPI * _ShouldSystemUseDarkMode)(VOID) = NULL;
BOOL (WINAPI* _SetWindowCompositionAttribute)(HWND, _WINDOWCOMPOSITIONATTRIBDATA *) = NULL;

BOOL _dw_is_high_contrast(VOID)
{
   HIGHCONTRASTW highContrast = { sizeof(highContrast) };
   if(SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(highContrast), &highContrast, FALSE))
      return highContrast.dwFlags & HCF_HIGHCONTRASTON;
   return FALSE;
}

/* Our own ShouldAppsUseDarkMode() that handles the forced option */
BOOL _dw_should_apps_use_dark_mode(void)
{
    if(_DW_DARK_MODE_ALLOWED == DW_DARK_MODE_FORCED)
        return TRUE;
    return (_ShouldAppsUseDarkMode() && !_dw_is_high_contrast());
}

void _dw_init_dark_mode(void)
{
   if(_DW_DARK_MODE_ALLOWED && _dwVersion && huxtheme)
   {
      /* Dark mode is introduced in Windows 10 (1809) build 17763 */
      if(LOBYTE(LOWORD(_dwVersion)) >= 10 && HIWORD(_dwVersion) >= 17763)
      {
         _OpenNcThemeData = (HTHEME (WINAPI *)(HWND, LPCWSTR))GetProcAddress(huxtheme, MAKEINTRESOURCEA(49));
         _RefreshImmersiveColorPolicyState = (VOID (WINAPI *)(VOID))GetProcAddress(huxtheme, MAKEINTRESOURCEA(104));
         _GetIsImmersiveColorUsingHighContrast = (BOOL (WINAPI *)(IMMERSIVE_HC_CACHE_MODE))GetProcAddress(huxtheme, MAKEINTRESOURCEA(106));
         _ShouldAppsUseDarkMode = (BOOL (WINAPI *)(VOID))GetProcAddress(huxtheme, MAKEINTRESOURCEA(132));
         _AllowDarkModeForWindow = (BOOL (WINAPI *)(HWND, BOOL))GetProcAddress(huxtheme, MAKEINTRESOURCEA(133));
         if(HIWORD(_dwVersion) < 18362)
            _AllowDarkModeForApp = (BOOL (WINAPI *)(BOOL))GetProcAddress(huxtheme, MAKEINTRESOURCEA(135));
         else
            _SetPreferredAppMode = (_PreferredAppMode (WINAPI *)(_PreferredAppMode))GetProcAddress(huxtheme, MAKEINTRESOURCEA(135));
         _IsDarkModeAllowedForWindow = (BOOL (WINAPI *)(HWND))GetProcAddress(huxtheme, MAKEINTRESOURCEA(137));
         _ShouldSystemUseDarkMode = (BOOL (WINAPI *)(VOID))GetProcAddress(huxtheme, MAKEINTRESOURCEA(138));
         _SetWindowCompositionAttribute = (BOOL (WINAPI*)(HWND, _WINDOWCOMPOSITIONATTRIBDATA*))GetProcAddress(GetModuleHandle(TEXT("user32.dll")), "SetWindowCompositionAttribute");
      }
      /* Make sure we were able to load all the Dark Mode functions */
      if(_OpenNcThemeData && _RefreshImmersiveColorPolicyState && _ShouldAppsUseDarkMode && _AllowDarkModeForWindow &&
         (_AllowDarkModeForApp || _SetPreferredAppMode) && _IsDarkModeAllowedForWindow &&
         (_DwmSetWindowAttribute || _SetWindowCompositionAttribute))
      {
         _DW_DARK_MODE_SUPPORTED = TRUE;
         if(_AllowDarkModeForApp)
            _AllowDarkModeForApp(TRUE);
         else
            _SetPreferredAppMode(_AllowDark);
         _RefreshImmersiveColorPolicyState();
         _DW_DARK_MODE_ENABLED = _dw_should_apps_use_dark_mode();
      }
   }
}

/* Return a margins struct based on the calculated window rect */
MARGINS _dw_rect_to_margins(RECT rect)
{
   /* Left, Right, Top, Bottom */
   MARGINS mar = { 1, 1, rect.top, 1 }, none = {0};
   
   if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_SUPPORTED)
      return mar;
   return none;
}

BOOL _dw_can_theme_window(HWND window)
{
   TCHAR tmpbuf[100] = {0};
   LONG_PTR style = GetWindowLongPtr(window, GWL_STYLE);

   GetClassName(window, tmpbuf, 99);

   /* Some controls don't display properly with visual styles enabled */
   if(_tcsnicmp(tmpbuf, BUTTONCLASSNAME, _tcslen(BUTTONCLASSNAME)+1) == 0 &&
      (style & (BS_AUTOCHECKBOX | BS_CHECKBOX | BS_RADIOBUTTON)))
      return FALSE;
#ifdef TOOLBAR
   else if(_tcsnicmp(tmpbuf, TOOLBARCLASSNAME, _tcslen(TOOLBARCLASSNAME)+1) == 0)
   {
     /* If we aren't in full dark mode */
      if(_DW_DARK_MODE_ALLOWED < DW_DARK_MODE_FULL)
      {
         /* Enable or disable visual themes */
         if(_SetWindowTheme)
            _SetWindowTheme(window, (style & TBSTYLE_FLAT) ? L"" : NULL, (style & TBSTYLE_FLAT) ? L"" : NULL);
      }
      return FALSE;
   }
#endif
   return TRUE;
}

BOOL _dw_allow_dark_mode_for_window(HWND window, BOOL allow)
{
   if(_DW_DARK_MODE_SUPPORTED)
   {
      if(_dw_can_theme_window(window))
      {
         if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC)
         {
            if(_DW_DARK_MODE_ENABLED)
               _SetWindowTheme(window, L"DarkMode_Explorer", NULL);
            else
               _SetWindowTheme(window, L"Explorer", NULL);
         }
      }
      return _AllowDarkModeForWindow(window, allow);
   }
   return FALSE;
}

BOOL _dw_is_color_scheme_change_message(LPARAM lParam)
{
   BOOL is = FALSE;
   if(lParam && _wcsicmp((LPCWCH)lParam, L"ImmersiveColorSet") == 0)
   {
      _RefreshImmersiveColorPolicyState();
      is = TRUE;
   }
   _GetIsImmersiveColorUsingHighContrast(IHCM_REFRESH);
   return is;
}

void _dw_refresh_titlebar_theme_color(HWND window)
{
   BOOL dark = FALSE;
   if (_IsDarkModeAllowedForWindow(window) && _dw_should_apps_use_dark_mode())
      dark = TRUE;
   if(HIWORD(_dwVersion) < 18362)
      SetProp(window, TEXT("UseImmersiveDarkModeColors"), (HANDLE)DW_INT_TO_POINTER(dark));
   else if (_SetWindowCompositionAttribute)
   {
      _WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &dark, sizeof(dark) };
      _SetWindowCompositionAttribute(window, &data);
   } else
      _DwmSetWindowAttribute(window, 19, &dark, sizeof(dark));
}

/* Call this on a window to apply the style */
BOOL CALLBACK _dw_set_child_window_theme(HWND window, LPARAM lParam)
{
   if(_DW_DARK_MODE_SUPPORTED)
   {
      _dw_allow_dark_mode_for_window(window, _DW_DARK_MODE_ENABLED);
      SendMessageW(window, WM_THEMECHANGED, 0, 0);
   }
   return TRUE;
}

#define RECTWIDTH(rc)   (rc.right - rc.left)
#define RECTHEIGHT(rc)  (rc.bottom - rc.top)

/* Our function to draw the titlebar in dark mode */
void _dw_draw_dark_mode_titlebar(HWND hWnd, HDC hdc)
{
   if(_OpenThemeData && _CloseThemeData && _DrawThemeTextEx && _GetThemeSysFont && 
      _GetImmersiveColorFromColorSetEx && _GetImmersiveColorTypeFromName && _GetImmersiveUserColorSetPreference)
   {
      HTHEME hTheme = _OpenThemeData(NULL, L"CompositedWindow::Window");
      RECT rcClient;

      GetClientRect(hWnd, &rcClient);

      if(hTheme)
      {
         HDC hdcPaint = CreateCompatibleDC(hdc);

         if(hdcPaint)
         {
            int cx = RECTWIDTH(rcClient);
            int cy = RECTHEIGHT(rcClient);
            HBITMAP hbm;

            /* Define the BITMAPINFO structure used to draw text.
             * Note that biHeight is negative. This is done because
             * DrawThemeTextEx() needs the bitmap to be in top-to-bottom
             * order.
             */
            BITMAPINFO dib = { 0 };
            dib.bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
            dib.bmiHeader.biWidth           = cx;
            dib.bmiHeader.biHeight          = -cy;
            dib.bmiHeader.biPlanes          = 1;
            dib.bmiHeader.biBitCount        = 32;
            dib.bmiHeader.biCompression     = BI_RGB;

            if((hbm = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0)))
            {
               HBITMAP hbmOld = (HBITMAP)SelectObject(hdcPaint, hbm);
               LOGFONT lgFont;
               HFONT hFontOld = NULL;
               RECT rcPaint = rcClient;
               TCHAR *tempbuf;
               HICON icon = (HICON)SendMessage(hWnd, WM_GETICON, ICON_SMALL2, 0);
               ColorInfo *cinfo = (ColorInfo *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
               LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE);
               int len;
               DWORD nColor = _GetImmersiveColorFromColorSetEx(_GetImmersiveUserColorSetPreference(FALSE, FALSE), 
                                                               _GetImmersiveColorTypeFromName(L"ImmersiveApplicationText"), FALSE, 0);

               /* Setup the theme drawing options. */
               DTTOPTS DttOpts = {sizeof(DTTOPTS)};
               DttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE | DTT_TEXTCOLOR;
               DttOpts.iGlowSize = 15;
               DttOpts.crText = RGB(0xFF & nColor, (0xFF00 & nColor) >> 8, (0xFF0000 & nColor) >> 16);

               /* Select a font. */
               if(_GetThemeSysFont(hTheme, 801 /*TMT_CAPTIONFONT*/, &lgFont) == S_OK)
               {
                  HFONT hFont = CreateFontIndirect(&lgFont);
                  hFontOld = (HFONT)SelectObject(hdcPaint, hFont);
               }

               /* Draw the title. */
               len = GetWindowTextLength(hWnd) + 1;
               if((tempbuf = _alloca(len * sizeof(TCHAR))))
                  GetWindowText(hWnd, tempbuf, len);
               rcPaint.top += 8;
               rcPaint.right -= 125;
               rcPaint.left += 8;
               if(style & WS_SYSMENU)
                  rcPaint.left += 24;
               if(cinfo && cinfo->hmenu)
                  rcPaint.left += 24;
               rcPaint.bottom = 50;
               _DrawThemeTextEx(hTheme, 
                               hdcPaint, 
                               0, 0, 
                               tempbuf ? tempbuf : TEXT("<no title>"), 
                               -1, 
                               DT_LEFT | DT_WORD_ELLIPSIS, 
                               &rcPaint, 
                               &DttOpts);

               if(style & WS_SYSMENU)
               {
                  /* Draw the application icon sysmenu */
                  DrawIconEx(hdcPaint, 8, 8, icon ? icon : LoadIcon(NULL, MAKEINTRESOURCE(32512)), 16, 16, 0, NULL, DI_NORMAL);
               }
               if(cinfo && cinfo->hmenu)
               {
                  /* Draw an icon to use as a menu click location */
                  DrawIconEx(hdcPaint, 8 + ((style & WS_SYSMENU) ? 24 : 0), 8, LoadIcon(NULL, MAKEINTRESOURCE(32516)), 16, 16, 0, NULL, DI_NORMAL);
               }

               /* Blit text to the frame. */
               BitBlt(hdc, 0, 0, cx, cy, hdcPaint, 0, 0, SRCCOPY);

               SelectObject(hdcPaint, hbmOld);
               if(hFontOld)
                  SelectObject(hdcPaint, hFontOld);
               DeleteObject(hbm);
            }
            DeleteDC(hdcPaint);
         }
         _CloseThemeData(hTheme);
      }
   }
}
#endif

/* Special wrappers for GetSysColor*() since they currently don't support
 * dark mode, we will have to return modified colors when dark mode is enabled.
 */
DWORD _dw_get_syscolor(int nIndex)
{
   DWORD retval = GetSysColor(nIndex);
#ifdef AEROGLASS
   if(_DW_DARK_MODE_ALLOWED > 1 && _DW_DARK_MODE_ENABLED)
   {
      const COLORREF darkBkColor = 0x383838;
      const COLORREF darkTextColor = 0xFFFFFF;

      switch(nIndex)
      {
         case COLOR_3DFACE:
         case COLOR_WINDOW:
            retval = darkBkColor;
         break;
         case COLOR_WINDOWTEXT:
            retval = darkTextColor;
         break;
      }
   }
#endif
   return retval;
}

HBRUSH _dw_get_syscolor_brush(int nIndex)
{
   HBRUSH retval = GetSysColorBrush(nIndex);
#ifdef AEROGLASS
   static HBRUSH darkBkColorBrush = 0;

   if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_ENABLED)
   {
      if(!darkBkColorBrush)
         darkBkColorBrush = CreateSolidBrush(0x383838);

      switch(nIndex)
      {
         case COLOR_3DFACE:
         case COLOR_WINDOW:
            retval = darkBkColorBrush;
         break;
         case COLOR_WINDOWTEXT:
            retval = _dw_colors[DW_CLR_WHITE];
         break;
      }
   }
#endif
   return retval;
}

/* This function adds a signal handler callback into the linked list.
 */
void _dw_new_signal(ULONG message, HWND window, int id, void *signalfunction, void *discfunc, void *data)
{
   DWSignalHandler *new = malloc(sizeof(DWSignalHandler));

   new->message = message;
   new->window = window;
   new->id = id;
   new->signalfunction = signalfunction;
   new->discfunction = discfunc;
   new->data = data;
   new->next = NULL;

   if (!Root)
   {
      Root = new;
   }
   else
   {
      DWSignalHandler *prev = NULL, *tmp = Root;
      while(tmp)
      {
         if(tmp->message == message &&
            tmp->window == window &&
            tmp->id == id &&
            tmp->signalfunction == signalfunction)
         {
            tmp->data = data;
            free(new);
            return;
         }
         prev = tmp;
         tmp = tmp->next;
      }
      if(prev)
         prev->next = new;
      else
         Root = new;
   }
}

/* Finds the message number for a given signal name */
ULONG _dw_findsigmessage(const char *signame)
{
   int z;

   for(z=0;z<SIGNALMAX;z++)
   {
      if(_stricmp(signame, DWSignalTranslate[z].name) == 0)
         return DWSignalTranslate[z].message;
   }
   return 0L;
}

/* Internal function to get the ColorInfo struct from a widget/window */
ColorInfo *_dw_window_get_cinfo(HWND handle)
{
   return (ColorInfo *)GetWindowLongPtr(handle, GWLP_USERDATA);
}

/* Internal function to add ColorInfo and subclass a widget/window */
ColorInfo *_dw_window_new_cinfo(HWND handle, BOOL subclass)
{
   ColorInfo *cinfo = calloc(1, sizeof(ColorInfo));

   if(cinfo)
   {
      cinfo->back = cinfo->fore = -1;
      if(subclass)
         cinfo->pOldProc = SubclassWindow(handle, _dw_colorwndproc);
      SetWindowLongPtr(handle, GWLP_USERDATA, (LONG_PTR)cinfo);
   }
   return cinfo;
}

/* This function removes any handlers on windows and frees
 * the user memory and resources allocated to it.
 */
BOOL CALLBACK _dw_free_window_memory(HWND handle, LPARAM lParam)
{
   ColorInfo *thiscinfo = _dw_window_get_cinfo(handle);
   HFONT oldfont = (HFONT)SendMessage(handle, WM_GETFONT, 0, 0);
   HICON oldicon = (HICON)SendMessage(handle, WM_GETICON, 0, 0);
   TCHAR tmpbuf[100] = {0};

   TOOLINFO ti = { 0 };

   ti.cbSize = sizeof(TOOLINFO);
   ti.hwnd = handle;
   ti.hinst = _DWInstance;

   SendMessage(_dw_tooltip, TTM_DELTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);

   GetClassName(handle, tmpbuf, 99);

   /* Don't try to free memory from an embedded IE or Edge/Chromium window */
   if(_tcsncmp(tmpbuf, TEXT("Internet Explorer_Server"), 25) == 0 ||
	   _tcsncmp(tmpbuf, TEXT("Chrome_WidgetWin_"), 17) == 0)
      return TRUE;

   /* Delete font, icon and bitmap GDI objects in use */
   if(oldfont)
      DeleteObject(oldfont);
   if(oldicon)
      DeleteObject(oldicon);

   if(_tcsnicmp(tmpbuf, STATICCLASSNAME, _tcslen(STATICCLASSNAME)+1)==0)
   {
      HBITMAP oldbitmap = (HBITMAP)SendMessage(handle, STM_GETIMAGE, IMAGE_BITMAP, 0);

      if(oldbitmap)
         DeleteObject(oldbitmap);
   }
   if(_tcsnicmp(tmpbuf, BUTTONCLASSNAME, _tcslen(BUTTONCLASSNAME)+1)==0)
   {
      HBITMAP oldbitmap = (HBITMAP)SendMessage(handle, BM_GETIMAGE, IMAGE_BITMAP, 0);
      HICON oldicon = (HICON)SendMessage(handle, BM_GETIMAGE, IMAGE_ICON, 0);

      if(oldbitmap)
         DeleteObject(oldbitmap);
      if(oldicon)
         DestroyIcon(oldicon);
   }
#ifdef TOOLBAR
   /* Bitmap Buttons */
   else if(_tcsnicmp(tmpbuf, TOOLBARCLASSNAME, _tcslen(TOOLBARCLASSNAME)+1) == 0)
   {
      HIMAGELIST imlist = (HIMAGELIST)SendMessage(handle, TB_GETIMAGELIST, 0, 0);
      HIMAGELIST dimlist = (HIMAGELIST)SendMessage(handle, TB_GETDISABLEDIMAGELIST, 0, 0);

      SendMessage(handle, TB_SETIMAGELIST, 0, 0);
      SendMessage(handle, TB_SETDISABLEDIMAGELIST, 0, 0);

      if(imlist)
         ImageList_Destroy(imlist);
      if(dimlist)
         ImageList_Destroy(dimlist);
   }
#endif
   else if(_tcsnicmp(tmpbuf, FRAMECLASSNAME, _tcslen(FRAMECLASSNAME)+1)==0)
   {
      Box *box = (Box *)thiscinfo;

      if(box && box->count && box->items)
         free(box->items);
   }
   else if(_tcsnicmp(tmpbuf, SplitbarClassName, _tcslen(SplitbarClassName)+1)==0)
   {
      void *data = dw_window_get_data(handle, "_dw_percent");

      if(data)
         free(data);
   }
   else if(_tcsnicmp(tmpbuf, WC_TREEVIEW, _tcslen(WC_TREEVIEW)+1)==0)
   {
      dw_tree_clear(handle);
   }
   else if(_tcsnicmp(tmpbuf, WC_TABCONTROL, _tcslen(WC_TABCONTROL)+1)==0) /* Notebook */
   {
      NotebookPage **array = (NotebookPage **)dw_window_get_data(handle, "_dw_array");

      if(array)
      {
         int z;

         for(z=0;z<256;z++)
         {
            if(array[z])
            {
               _dw_free_window_memory(array[z]->hwnd, 0);
               EnumChildWindows(array[z]->hwnd, _dw_free_window_memory, 0);
               DestroyWindow(array[z]->hwnd);
               free(array[z]);
            }
         }
         free(array);
      }
   }
   else if(_tcsnicmp(tmpbuf, UPDOWN_CLASS, _tcslen(UPDOWN_CLASS)+1)==0)
   {
      /* for spinbuttons, we need to get the spinbutton's "buddy", the text window associated and destroy it */
      ColorInfo *cinfo = _dw_window_get_cinfo(handle);

      if(cinfo && cinfo->buddy)
         DestroyWindow(cinfo->buddy);
   }
   /* Some Edge Windows have an empty class.. abort. */
   else if(_tcslen(tmpbuf) == 0)
      return TRUE;

   dw_signal_disconnect_by_window(handle);

   if(thiscinfo)
   {
      if(thiscinfo->pOldProc)
         SetWindowLongPtr(handle, GWLP_WNDPROC, (LPARAM)(WNDPROC)thiscinfo->pOldProc);

      /* Delete the brush so as not to leak GDI objects */
      if(thiscinfo->hbrush)
         DeleteObject(thiscinfo->hbrush);

      /* Free user data linked list memory */
      if(thiscinfo->root)
         dw_window_set_data(handle, NULL, NULL);

      SetWindowLongPtr(handle, GWLP_USERDATA, 0);
      free(thiscinfo);
   }
   return TRUE;
}

void _dw_free_menu_data(HMENU menu)
{
    int i, count = GetMenuItemCount(menu);

    for(i=0;i<count;i++)
    {
        MENUITEMINFO mii;

        mii.cbSize = sizeof(MENUITEMINFO);
        mii.fMask = MIIM_SUBMENU | MIIM_ID;

        if ( GetMenuItemInfo( menu, i, TRUE, &mii ) )
        {
           /* Free the data associated with the ID */
           if(mii.wID >= 30000)
           {
              char buffer[31] = {0};

              _snprintf(buffer, 30, "_dw_id%u", mii.wID);
              dw_window_set_data( DW_HWND_OBJECT, buffer, NULL );
              _snprintf(buffer, 30, "_dw_checkable%u", mii.wID);
              dw_window_set_data( DW_HWND_OBJECT, buffer, NULL );
              _snprintf(buffer, 30, "_dw_ischecked%u", mii.wID);
              dw_window_set_data( DW_HWND_OBJECT, buffer, NULL );
              _snprintf(buffer, 30, "_dw_isdisabled%u", mii.wID);
              dw_window_set_data( DW_HWND_OBJECT, buffer, NULL );
           }

           /* Check any submenus */
           if( mii.hSubMenu )
              _dw_free_menu_data(mii.hSubMenu);
        }
    }
    dw_signal_disconnect_by_name((HWND)menu, DW_SIGNAL_CLICKED);
}

/* Convert to our internal color scheme */
ULONG _dw_internal_color(ULONG color)
{
   if(color == DW_CLR_DEFAULT)
      return DW_RGB_TRANSPARENT;
   if(color < 18)
      return DW_RGB(_dw_red[color], _dw_green[color], _dw_blue[color]);
   return color;
}

/* This function returns 1 if the window (widget) handle
 * passed to it is a valid window that can gain input focus.
 */
int _dw_validate_focus(HWND handle)
{
   TCHAR tmpbuf[100] = {0};

   if(!handle)
      return 0;

   if(!IsWindowEnabled(handle))
      return 0;

   GetClassName(handle, tmpbuf, 99);

   /* These are the window classes which can
    * obtain input focus.
    */
   if(_tcsnicmp(tmpbuf, EDITCLASSNAME, _tcslen(EDITCLASSNAME)+1)==0 ||          /* Entryfield */
      _tcsnicmp(tmpbuf, BUTTONCLASSNAME, _tcslen(BUTTONCLASSNAME)+1)==0 ||      /* Button */
#ifdef TOOLBAR
      _tcsnicmp(tmpbuf, TOOLBARCLASSNAME, _tcslen(TOOLBARCLASSNAME)+1) == 0 ||  /* Toolbar */
#endif
      _tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0 ||  /* Combobox */
      _tcsnicmp(tmpbuf, LISTBOXCLASSNAME, _tcslen(LISTBOXCLASSNAME)+1)==0 ||    /* List box */
      _tcsnicmp(tmpbuf, UPDOWN_CLASS, _tcslen(UPDOWN_CLASS)+1)==0 ||            /* Spinbutton */
      _tcsnicmp(tmpbuf, TRACKBAR_CLASS, _tcslen(TRACKBAR_CLASS)+1)==0 ||        /* Slider */
      _tcsnicmp(tmpbuf, WC_LISTVIEW, _tcslen(WC_LISTVIEW)+1)== 0 ||             /* Container */
      _tcsnicmp(tmpbuf, WC_TREEVIEW, _tcslen(WC_TREEVIEW)+1)== 0)               /* Tree */
      return 1;
   /* Special case for the notebook, can get focus and contains other items */
   if(_tcsnicmp(tmpbuf, WC_TABCONTROL, _tcslen(WC_TABCONTROL))==0)              /* Notebook */
      return 2;
   return 0;
}

HWND _dw_normalize_handle(HWND handle)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);
   if(_tcsnicmp(tmpbuf, UPDOWN_CLASS, _tcslen(UPDOWN_CLASS))==0) /* Spinner */
   {
      ColorInfo *cinfo = _dw_window_get_cinfo(handle);

      if(cinfo && cinfo->buddy)
         return cinfo->buddy;
   }
   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME))==0) /* Combobox */
   {
      ColorInfo *cinfo = _dw_window_get_cinfo(handle);

      if(cinfo && cinfo->buddy)
         return cinfo->buddy;
   }
   return handle;
}

#define _DW_DIRECTION_FORWARD -1
#define _DW_DIRECTION_BACKWARD 1

int _dw_focus_check_box(Box *box, HWND handle, int start, int direction, HWND defaultitem);

/* Internal comparision function */
int _dw_focus_comp(int direction, int z, int end)
{
   if(direction == _DW_DIRECTION_FORWARD)
      return z > -1;
   return z < end;
}

int _dw_focus_notebook(HWND hwnd, HWND handle, int start, int direction, HWND defaultitem)
{
   NotebookPage **array = (NotebookPage **)dw_window_get_data(hwnd, "_dw_array");
   int pageid = TabCtrl_GetCurSel(hwnd);

   if(pageid > -1 && array && array[pageid])
   {
      Box *notebox;

      if(array[pageid]->hwnd)
      {
         notebox = (Box *)GetWindowLongPtr(array[pageid]->hwnd, GWLP_USERDATA);

         if(notebox && _dw_focus_check_box(notebox, handle, start == 3 ? 3 : 0, direction, defaultitem))
            return 1;
      }
   }
   return 0;
}

/* Handle box focus traversal in either direction */
int _dw_focus_check_box(Box *box, HWND handle, int start, int direction, HWND defaultitem)
{
   int z;
   static HWND lasthwnd, firsthwnd;
   static int finish_searching;
   int beg = (direction == _DW_DIRECTION_FORWARD) ? box->count-1 : 0;
   int end = (direction == _DW_DIRECTION_FORWARD) ? -1 : box->count;

   /* Start is 2 when we have cycled completely and
    * need to set the focus to the last widget we found
    * that was valid.
    */
   if(start == 2)
   {
      if(lasthwnd)
         SetFocus(lasthwnd);
      return 0;
   }

   /* Start is 1 when we are entering the function
    * for the first time, it is zero when entering
    * the function recursively.
    */
   if(start == 1)
   {
      lasthwnd = handle;
      finish_searching = 0;
      firsthwnd = 0;
   }

   for(z=beg;_dw_focus_comp(direction, z, end);z+=direction)
   {
      if(box->items[z].type == _DW_TYPE_BOX)
      {
         Box *thisbox = (Box *)GetWindowLongPtr(box->items[z].hwnd, GWLP_USERDATA);

         if(thisbox && _dw_focus_check_box(thisbox, handle, start == 3 ? 3 : 0, direction, defaultitem))
            return 1;
      }
      else
      {
         int type = _dw_validate_focus(box->items[z].hwnd);

         /* Special case notebook, can focus and contains items */
         if(type == 2 && direction == _DW_DIRECTION_FORWARD && _dw_focus_notebook(box->items[z].hwnd, handle, start, direction, defaultitem))
            return 1;
         if(box->items[z].hwnd == handle)
         {
            if(lasthwnd == handle && firsthwnd)
               SetFocus(firsthwnd);
            else if(lasthwnd == handle && !firsthwnd)
               finish_searching = 1;
            else
               SetFocus(lasthwnd);

            /* If we aren't looking for the last handle,
             * return immediately.
             */
            if(!finish_searching)
               return 1;
         }
         if(type > 0)
         {
            /* Start is 3 when we are looking for the
             * first valid item in the layout.
             */
            if(start == 3)
            {
               if(!defaultitem || (defaultitem && box->items[z].hwnd == defaultitem))
               {
                  SetFocus(_dw_normalize_handle(box->items[z].hwnd));
                  return 1;
               }
            }

            lasthwnd = _dw_normalize_handle(box->items[z].hwnd);

            if(!firsthwnd)
               firsthwnd = lasthwnd;
         }
         else
         {
            /* Handle controls that contain other items */
            TCHAR tmpbuf[100] = {0};

            GetClassName(box->items[z].hwnd, tmpbuf, 99);

            if(_tcsncmp(tmpbuf, SplitbarClassName, _tcslen(SplitbarClassName)+1)==0)
            {
               /* Then try the bottom or right box */
               HWND mybox = (HWND)dw_window_get_data(box->items[z].hwnd, (direction == _DW_DIRECTION_FORWARD) ? "_dw_bottomright" : "_dw_topleft");

               if(mybox)
               {
                  Box *splitbox = (Box *)GetWindowLongPtr(mybox, GWLP_USERDATA);

                  if(splitbox && _dw_focus_check_box(splitbox, handle, start == 3 ? 3 : 0, direction, defaultitem))
                     return 1;
               }

               /* Try the top or left box */
               mybox = (HWND)dw_window_get_data(box->items[z].hwnd, (direction == _DW_DIRECTION_FORWARD) ? "_dw_topleft" : "_dw_bottomright");

               if(mybox)
               {
                  Box *splitbox = (Box *)GetWindowLongPtr(mybox, GWLP_USERDATA);

                  if(splitbox && _dw_focus_check_box(splitbox, handle, start == 3 ? 3 : 0, direction, defaultitem))
                     return 1;
               }
            }
            else if(_tcsnicmp(tmpbuf, ScrollClassName, _tcslen(ScrollClassName))==0) /* Scroll Box */
            {
                ColorInfo *cinfo = _dw_window_get_cinfo(handle);
                Box *scrollbox = (Box *)GetWindowLongPtr(cinfo->combo, GWLP_USERDATA);

                if(scrollbox && _dw_focus_check_box(scrollbox, handle, start == 3 ? 3 : 0, direction, defaultitem))
                   return 1;
            }
         }
         /* Special case notebook, can focus and contains items */
         if(type == 2 && direction == _DW_DIRECTION_BACKWARD && _dw_focus_notebook(box->items[z].hwnd, handle, start, direction, defaultitem))
            return 1;
      }
   }
   return 0;
}

/* This function finds the first widget in the
 * layout and moves the current focus to it.
 */
void _dw_initial_focus(HWND handle)
{
   Box *thisbox = NULL;
   TCHAR tmpbuf[100] = {0};

   if(!handle)
      return;

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, ClassName, _tcslen(ClassName)+1)!=0)
      return;


   if(handle)
      thisbox = (Box *)GetWindowLongPtr(handle, GWLP_USERDATA);

   if(thisbox)
   {
      _dw_focus_check_box(thisbox, handle, 3, _DW_DIRECTION_FORWARD, thisbox->defaultitem);
   }
}

HWND _dw_toplevel_window(HWND handle)
{
   HWND box, lastbox = GetParent(handle);

   if(!lastbox)
      lastbox = handle;

   /* Find the toplevel window */
   while((box = GetParent(lastbox)))
   {
      /* If it hasn't been packed yet.. */
      if(box == DW_HWND_OBJECT)
         return 0;
      lastbox = box;
   }
   if(lastbox)
   {
      TCHAR tmpbuf[100] = {0};

      GetClassName(lastbox, tmpbuf, 99);

      if(_tcsncmp(tmpbuf, ClassName, _tcslen(ClassName)+1)==0)
         return lastbox;
   }
   return 0;
}

/* This function finds the current widget in the
 * layout and moves the current focus to the next item.
 */
void _dw_shift_focus(HWND handle, int direction)
{
   Box *thisbox;

   HWND box, lastbox = GetParent(handle);

   /* Find the toplevel window */
   while((box = GetParent(lastbox)))
   {
      lastbox = box;
   }

   thisbox = (Box *)GetWindowLongPtr(lastbox, GWLP_USERDATA);
   if(thisbox)
   {
      if(_dw_focus_check_box(thisbox, handle, 1, direction, 0)  == 0)
         _dw_focus_check_box(thisbox, handle, 2, direction, 0);
   }
}

/* This function calculates how much space the widgets and boxes require
 * and does expansion as necessary.
 */
static void _dw_resize_box(Box *thisbox, int *depth, int x, int y, int xborder, int yborder, int pass)
{
   /* Current item position */
   int z, currentx = thisbox->pad, currenty = thisbox->pad;
   /* Used x, y and padding maximum values...
    * These will be used to find the widest or
    * tallest items in a box.
    */
   int uymax = 0, uxmax = 0;
   int upymax = 0, upxmax = 0;

   /* Reset the box sizes */
   thisbox->minwidth = thisbox->minheight = thisbox->usedpadx = thisbox->usedpady = thisbox->pad * 2;

   if(thisbox->grouphwnd)
   {
      /* Only calculate the size on the first pass...
       * use the cached values on second.
       */
      if(pass == 1)
      {
         char *text = dw_window_get_text(thisbox->grouphwnd);

         thisbox->grouppady = 9;

         if(text)
         {
            if(*text)
               dw_font_text_extents_get(thisbox->grouphwnd, 0, text, NULL, &thisbox->grouppady);
            dw_free(text);
         }
         /* If the string height is less than 9...
          * set it to 9 anyway since that is the minimum.
          */
         if(thisbox->grouppady < 9)
            thisbox->grouppady = 9;

         if(thisbox->grouppady)
            thisbox->grouppady += 3;
         else
            thisbox->grouppady = 6;

         thisbox->grouppadx = 6;
      }

      thisbox->minwidth += thisbox->grouppadx;
      thisbox->usedpadx += thisbox->grouppadx;
      thisbox->minheight += thisbox->grouppady;
      thisbox->usedpady += thisbox->grouppady;
   }

   /* Count up all the space for all items in the box */
   for(z=0;z<thisbox->count;z++)
   {
      int itempad, itemwidth, itemheight;

      if(thisbox->items[z].type == _DW_TYPE_BOX)
      {
         Box *tmp = (Box *)GetWindowLongPtr(thisbox->items[z].hwnd, GWLP_USERDATA);

         if(tmp)
         {
            /* On the first pass calculate the box contents */
            if(pass == 1)
            {
               (*depth)++;

               /* Save the newly calculated values on the box */
               _dw_resize_box(tmp, depth, x, y, 0, 0, pass);

               /* Duplicate the values in the item list for use below */
               thisbox->items[z].width = tmp->minwidth;
               thisbox->items[z].height = tmp->minheight;

               /* If the box has no contents but is expandable... default the size to 1 */
               if(!thisbox->items[z].width && thisbox->items[z].hsize)
                  thisbox->items[z].width = 1;
               if(!thisbox->items[z].height && thisbox->items[z].vsize)
                  thisbox->items[z].height = 1;

               (*depth)--;
            }
         }
      }

      /* Precalculate these values, since they will
       * be used used repeatedly in the next section.
       */
      itempad = thisbox->items[z].pad * 2;
      itemwidth = thisbox->items[z].width + itempad;
      itemheight = thisbox->items[z].height + itempad;

      /* Calculate the totals and maximums */
      if(thisbox->type == DW_VERT)
      {
         if(itemwidth > uxmax)
            uxmax = itemwidth;

         if(thisbox->items[z].hsize != _DW_SIZE_EXPAND)
         {
            if(itemwidth > upxmax)
               upxmax = itemwidth;
         }
         else
         {
            if(itempad > upxmax)
               upxmax = itempad;
         }
         thisbox->minheight += itemheight;
         if(thisbox->items[z].vsize != _DW_SIZE_EXPAND)
            thisbox->usedpady += itemheight;
         else
            thisbox->usedpady += itempad;
      }
      else
      {
         if(itemheight > uymax)
            uymax = itemheight;
         if(thisbox->items[z].vsize != _DW_SIZE_EXPAND)
         {
            if(itemheight > upymax)
               upymax = itemheight;
         }
         else
         {
            if(itempad > upymax)
               upymax = itempad;
         }
         thisbox->minwidth += itemwidth;
         if(thisbox->items[z].hsize != _DW_SIZE_EXPAND)
            thisbox->usedpadx += itemwidth;
         else
            thisbox->usedpadx += itempad;
      }
   }

   /* Add the maximums which were calculated in the previous loop */
   thisbox->minwidth += uxmax;
   thisbox->minheight += uymax;
   thisbox->usedpadx += upxmax;
   thisbox->usedpady += upymax;

   /* Move the groupbox start past the group border */
   if(thisbox->grouphwnd)
   {
      currentx += 3;
      currenty += thisbox->grouppady - 3;
   }

   /* The second pass is for actual placement. */
   if(pass > 1)
   {
      for(z=0;z<(thisbox->count);z++)
      {
         int height = thisbox->items[z].height;
         int width = thisbox->items[z].width;
         int itempad = thisbox->items[z].pad * 2;
         int thispad = thisbox->pad * 2;

         /* Calculate the new sizes */
         if(thisbox->items[z].hsize == _DW_SIZE_EXPAND)
         {
            if(thisbox->type == DW_HORZ)
            {
               int expandablex = thisbox->minwidth - thisbox->usedpadx;

               if(expandablex)
                  width = (int)(((float)width / (float)expandablex) * (float)(x - thisbox->usedpadx));
            }
            else
               width = x - (itempad + thispad + thisbox->grouppadx);
         }
         if(thisbox->items[z].vsize == _DW_SIZE_EXPAND)
         {
            if(thisbox->type == DW_VERT)
            {
               int expandabley = thisbox->minheight - thisbox->usedpady;

               if(expandabley)
                  height = (int)(((float)height / (float)expandabley) * (float)(y - thisbox->usedpady));
            }
            else
               height = y - (itempad + thispad + thisbox->grouppady);
         }

         /* If the calculated size is valid... */
         if(width > 0 && height > 0)
         {
            int pad = thisbox->items[z].pad;
            HWND handle = thisbox->items[z].hwnd;
            TCHAR tmpbuf[100] = {0};

            GetClassName(handle, tmpbuf, 99);

            if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
            {
               /* Handle special case Combobox */
               MoveWindow(handle, currentx + pad + xborder, currenty + pad + yborder,
                        width, height + 400, FALSE);
            }
#ifdef TOOLBAR
            /* Bitmap Buttons */
            else if(_tcsnicmp(tmpbuf, TOOLBARCLASSNAME, _tcslen(TOOLBARCLASSNAME)+1) == 0)
            {
               SendMessage(handle, TB_SETBUTTONSIZE, 0, MAKELPARAM(width, height));

               MoveWindow(handle, currentx + pad + xborder, currenty + pad + yborder, width, height, FALSE);
            }
#endif
            else if(_tcsnicmp(tmpbuf, UPDOWN_CLASS, _tcslen(UPDOWN_CLASS)+1)==0)
            {
               /* Handle special case Spinbutton */
               ColorInfo *cinfo = _dw_window_get_cinfo(handle);

               MoveWindow(handle, currentx + pad + (width - 20) + xborder, currenty + pad + yborder,
                        20, height, FALSE);

               if(cinfo)
               {
                  MoveWindow(cinfo->buddy, currentx + pad + xborder, currenty + pad + yborder,
                           width - 20, height, FALSE);
               }
            }
            else if(_tcsncmp(tmpbuf, ScrollClassName, _tcslen(ScrollClassName)+1)==0)
            {
                /* Handle special case of scrollbox */
                ColorInfo *cinfo = _dw_window_get_cinfo(handle);
                int cx, cy, depth = 0;
                Box *thisbox = (Box *)GetWindowLongPtr(cinfo->combo, GWLP_USERDATA);
                SCROLLINFO hsi, vsi;
                RECT rect;

                vsi.cbSize = hsi.cbSize = sizeof(SCROLLINFO);
                vsi.fMask = hsi.fMask = SIF_POS;

                /* Save the current scroll positions */
                GetScrollInfo(handle, SB_HORZ, &hsi);
                GetScrollInfo(handle, SB_VERT, &vsi);

                /* Position the scrollbox */
                MoveWindow(handle, currentx + pad + xborder, currenty + pad + yborder, width, height, FALSE);

                GetClientRect(handle, &rect);
                cx = rect.right;
                cy = rect.bottom;


                /* Get the required space for the box */
                _dw_resize_box(thisbox, &depth, cx, cy, 0, 0, 1);

                if(cx < thisbox->minwidth)
                {
                    cx = thisbox->minwidth;
                }
                if(cy < thisbox->minheight)
                {
                    cy = thisbox->minheight;
                }

                /* Position the scrolled box */
                vsi.fMask = hsi.fMask = SIF_POS | SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL;
                vsi.nMin = hsi.nMin = vsi.nMax = hsi.nMax = 0;
                if(rect.bottom < thisbox->minheight)
                {
                    vsi.nMax = thisbox->minheight - 1;
                    vsi.nPage = rect.bottom;
                    if(vsi.nPos > vsi.nMax)
                    {
                        vsi.nPos = vsi.nMax;
                    }
                }
                if(rect.right < thisbox->minwidth)
                {
                    hsi.nMax = thisbox->minwidth - 1;
                    hsi.nPage = rect.right;
                    if(hsi.nPos > hsi.nMax)
                    {
                        hsi.nPos = hsi.nMax;
                    }
                }
                MoveWindow(cinfo->combo, -hsi.nPos, -vsi.nPos, cx, cy, FALSE);
                SetScrollInfo(handle, SB_HORZ, &hsi, TRUE);
                SetScrollInfo(handle, SB_VERT, &vsi, TRUE);

                /* Layout the content of the scrollbox */
                _dw_do_resize(thisbox, cx, cy, 0, 0);
            }
            else if(_tcsncmp(tmpbuf, SplitbarClassName, _tcslen(SplitbarClassName)+1)==0)
            {
               /* Then try the bottom or right box */
               float *percent = (float *)dw_window_get_data(handle, "_dw_percent");
               int type = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_type"));

               MoveWindow(handle, currentx + pad + xborder, currenty + pad + yborder,
                        width, height, FALSE);

               if(percent && width > 0 && height > 0)
                  _dw_handle_splitbar_resize(handle, *percent, type, width, height);
            }
            else if(_tcsnicmp(tmpbuf, STATICCLASSNAME, _tcslen(STATICCLASSNAME)+1)==0)
            {
               /* Handle special case Vertically Center static text */
               ColorInfo *cinfo = _dw_window_get_cinfo(handle);

               if(cinfo && cinfo->style & DW_DT_VCENTER)
               {
                  /* We are centered so calculate a new position */
                  TCHAR tmpbuf[1024] = {0}, *thisbuf = tmpbuf;
                  int textheight, diff, total = height;

                  GetWindowText(handle, thisbuf, 1023);

                  /* Figure out how big the text is */
                  dw_font_text_extents_get(handle, 0, WideToUTF8(thisbuf), 0, &textheight);

                  diff = (total - textheight) / 2;

                  MoveWindow(handle, currentx + pad + xborder, currenty + pad + diff + yborder,
                           width, height - diff, FALSE);
               }
               else
               {
                  MoveWindow(handle, currentx + pad + xborder, currenty + pad + yborder,
                           width, height, FALSE);
               }
            }
            else
            {
               /* Everything else */
               if(*depth)
                  MoveWindow(handle, currentx + pad + xborder, currenty + pad + yborder, width, height, FALSE);
               else /* FIXME: This is a hack to generate WM_PAINT messages for items on the top-level */
                  SetWindowPos(handle, HWND_TOP, currentx + pad + xborder, currenty + pad + yborder, width, height, 0);

               /* After placing a box... place its components */
               if(thisbox->items[z].type == _DW_TYPE_BOX)
               {
                  Box *boxinfo = (Box *)GetWindowLongPtr(handle, GWLP_USERDATA);

                  if(boxinfo)
                  {
                     /* Move the group border into place */
                     if(boxinfo->grouphwnd)
                     {
                        MoveWindow(boxinfo->grouphwnd, 0, 0,
                                 width, height, FALSE);
                     }
                     /* Dive into the box */
                     (*depth)++;
                     _dw_resize_box(boxinfo, depth, width, height, 0, 0, pass);
                     (*depth)--;
                  }
               }
            }

            /* Notebook dialog requires additional processing */
            if(_tcsncmp(tmpbuf, WC_TABCONTROL, _tcslen(WC_TABCONTROL))==0)
            {
               RECT rect;
               NotebookPage **array = (NotebookPage **)dw_window_get_data(handle, "_dw_array");
               int pageid = TabCtrl_GetCurSel(handle);

               if(pageid > -1 && array && array[pageid])
               {
                  GetClientRect(handle,&rect);
                  TabCtrl_AdjustRect(handle,FALSE,&rect);
                  MoveWindow(array[pageid]->hwnd, rect.left, rect.top,
                           rect.right - rect.left, rect.bottom-rect.top, FALSE);
                  dw_window_redraw(array[pageid]->hwnd);
               }
            }
            /* So does the List View... handle delayed cursoring */
            if(_tcsnicmp(tmpbuf, WC_LISTVIEW, _tcslen(WC_LISTVIEW)+1)==0 && width > 10 && height > 10)
            {
                int index = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_cursor"));

                if(index > 0)
                    ListView_EnsureVisible(handle, index, TRUE);
            }

            /* Advance the current position in the box */
            if(thisbox->type == DW_HORZ)
               currentx += width + (pad * 2);
            if(thisbox->type == DW_VERT)
               currenty += height + (pad * 2);
         }
      }
   }
}

void _dw_do_resize(Box *thisbox, int x, int y, int xborder, int yborder)
{
   if(x != 0 && y != 0)
   {
      if(thisbox)
      {
         int depth = 0;

         /* Calculate space requirements */
         _dw_resize_box(thisbox, &depth, x, y, xborder, yborder, 1);

         /* Finally place all the boxes and controls */
         _dw_resize_box(thisbox, &depth, x, y, xborder, yborder, 2);
      }
   }
}

int _dw_handle_scroller(HWND handle, int bar, int pos, int which)
{
   SCROLLINFO si;

   ZeroMemory( &si, sizeof(si) );
   si.cbSize = sizeof(SCROLLINFO);
   si.fMask = SIF_ALL;

   SendMessage(handle, SBM_GETSCROLLINFO, 0, (LPARAM)&si);

   switch(which)
   {
   case SB_THUMBTRACK:
      return pos;
   /*case SB_PAGEUP:*/
   case SB_PAGELEFT:
      pos = si.nPos - si.nPage;
      if(pos < si.nMin)
         pos = si.nMin;
      return pos;
   /*case SB_PAGEDOWN:*/
   case SB_PAGERIGHT:
      pos = si.nPos + si.nPage;
      if(pos > (int)(si.nMax - si.nPage) + 1)
         pos = (si.nMax - si.nPage) + 1;
      return pos;
   /*case SB_LINEUP:*/
   case SB_LINELEFT:
      pos = si.nPos - 1;
      if(pos < si.nMin)
         pos = si.nMin;
      return pos;
   /*case SB_LINEDOWN:*/
   case SB_LINERIGHT:
      pos = si.nPos + 1;
      if(pos > (int)(si.nMax - si.nPage) + 1)
         pos = (si.nMax - si.nPage) + 1;
      return pos;
   }
   return -1;
}

HMENU _dw_get_owner(HMENU menu)
{
   MENUINFO mi;

   mi.cbSize = sizeof(MENUINFO);
   mi.fMask = MIM_MENUDATA;

   if ( GetMenuInfo( menu, &mi ) )
      return (HMENU)mi.dwMenuData;
   return (HMENU)0;
}

/* Find the desktop window handle */
HMENU _dw_menu_owner(HMENU handle)
{
   HMENU menuowner = 0, lastowner = _dw_get_owner(handle);

   /* Find the toplevel menu */
   while((menuowner = _dw_get_owner(lastowner)) != 0)
   {
      if(menuowner == (HMENU)1)
         return lastowner;
      lastowner = menuowner;
   }
   return (HMENU)0;
}

/*
 * Determine if this is a checkable menu. If it is get the current state
 * and toggle it. Windows doesn't do this automatically :-(
 */
static void _dw_toggle_checkable_menu_item( HWND window, int id )
{
   char buffer[40];
   int checkable;
   sprintf( buffer, "_dw_checkable%d", id );
   checkable = DW_POINTER_TO_INT(dw_window_get_data(DW_HWND_OBJECT, buffer));
   if ( checkable )
   {
      int is_checked;
      sprintf( buffer, "_dw_ischecked%d", id );
      is_checked = DW_POINTER_TO_INT(dw_window_get_data(DW_HWND_OBJECT, buffer));
      is_checked = (is_checked) ? DW_MIS_UNCHECKED : DW_MIS_CHECKED;
      dw_menu_item_set_state( window, id, is_checked );
   }
}

#ifdef AEROGLASS
void _dw_show_margins(HWND handle, MARGINS mar, int line)
{
#ifdef DEBUG
   char *title = dw_window_get_text(handle);
   dw_debug("_DwmExtendFrameIntoClientArea(\"%s\",%d,%d,%d,%d) line %d\n", title,
            mar.cxLeftWidth, mar.cxRightWidth,
            mar.cyTopHeight, mar.cyBottomHeight, line);
   dw_free(title);
#endif
}
#endif

/* The main window procedure for Dynamic Windows, all the resizing code is done here. */
LRESULT CALLBACK _dw_wndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
   int result = -1, taskbar = FALSE;
   DWSignalHandler *tmp = Root;
   void (DWSIGNAL *windowfunc)(PVOID);
   ULONG origmsg = msg;

#ifdef DARK_MODE_TITLEBAR_MENU
   /* Expand the client area into the titlebar so we can draw our alternate dark mode button
    * which when clicked will display the window's menubar menu. Since the menubar cannot be
    * made dark, hide it and add the button to the titlebar instead.
    */
   if(msg == WM_NCCALCSIZE && mp2 && _DW_DARK_MODE_SUPPORTED && _DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC)
   {
      ColorInfo *cinfo = _dw_window_get_cinfo(hWnd);

      if(cinfo)
      {
         NCCALCSIZE_PARAMS* sz = (NCCALCSIZE_PARAMS*)mp2;

         sz->rgrc[0].left += cinfo->rect.left;
         sz->rgrc[0].right -= cinfo->rect.right;
         sz->rgrc[0].bottom -= cinfo->rect.bottom;
         return 0;
      }
   }
   else if(msg == WM_NCPAINT && _DW_DARK_MODE_SUPPORTED && _DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC)
   {
      /* Handle close/minimize/maximize/help button */
      LRESULT lResult;

      if(_DwmDefWindowProc && _DwmDefWindowProc(hWnd, msg, mp1, mp2, &lResult))
         return lResult;
   }
   else if(msg == WM_NCHITTEST && _DW_DARK_MODE_SUPPORTED && _DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC)
   {
      /* Handle close/minimize/maximize/help button */
      LRESULT lResult;

      if(_DwmDefWindowProc && _DwmDefWindowProc(hWnd, msg, mp1, mp2, &lResult))
         return lResult;

      /* Do default processing, except change the result for caption area */
      lResult = DefWindowProc(hWnd, msg, mp1, mp2);
      if(lResult == HTCLIENT)
      {
         ColorInfo *cinfo = _dw_window_get_cinfo(hWnd);

         if(cinfo)
         {
            POINT pt = { LOWORD(mp2), HIWORD(mp2) };

            ScreenToClient(hWnd, &pt);
            /* The top border should be the same as the bottom. */
            if (pt.y < cinfo->rect.bottom) return HTTOP;
            /* The caption is any part of the top besides the border */
            if (pt.y < cinfo->rect.top) 
            {
               ColorInfo *cinfo = (ColorInfo *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
               LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE);
               int pos = 32;

               if(style & WS_SYSMENU)
               {
                  if(pt.x < (pos + cinfo->rect.left))
                     return HTSYSMENU;
                  pos += 24;
               }
               if(cinfo && cinfo->hmenu)
               {
                  if(pt.x < (pos + cinfo->rect.left))
                  {
                     if(cinfo->hmenu)
                     {
                        TrackPopupMenu(cinfo->hmenu, 0, LOWORD(mp2), HIWORD(mp2), 0, hWnd, NULL);
                        return HTNOWHERE;
                     }
                  }
               }
               return HTCAPTION;
            }
         }
      }
      return lResult;
   }
   else if(msg == WM_ACTIVATE && _DW_DARK_MODE_SUPPORTED && _DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC)
   {
      RECT rect;
      GetWindowRect(hWnd, &rect);
      PostMessage(hWnd, WM_SIZE, 0, MAKELPARAM(rect.right-rect.left, rect.bottom-rect.top));
   }
   else if(msg == WM_PAINT && _DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_SUPPORTED && GetParent(hWnd) == HWND_DESKTOP)
   {
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(hWnd, &ps);
      _dw_draw_dark_mode_titlebar(hWnd, hdc);
      EndPaint(hWnd, &ps);
   }
#endif

   /* Deal with translating some messages */
   if (msg == WM_USER+2)
   {
      taskbar = TRUE;
      origmsg = msg = (UINT)mp2; /* no else here */
   }
   if (msg == WM_RBUTTONDOWN || msg == WM_MBUTTONDOWN)
      msg = WM_LBUTTONDOWN;
   else if (msg == WM_RBUTTONUP || msg == WM_MBUTTONUP)
      msg = WM_LBUTTONUP;
   else if (msg == WM_HSCROLL)
      msg = WM_VSCROLL;
   else if (msg == WM_KEYDOWN) /* && mp1 >= VK_F1 && mp1 <= VK_F24) allow ALL special keys */
      msg = WM_CHAR;

   if (result == -1)
   {
      /* Find any callbacks for this function */
      while (tmp)
      {
         if (tmp->message == msg || msg == WM_COMMAND || msg == WM_NOTIFY || tmp->message == WM_USER+1 || msg == WM_USER+102)
         {
            switch (msg)
            {
               case WM_TIMER:
                  {
                     if (!hWnd)
                     {
                        int (DWSIGNAL *timerfunc)(void *) = tmp->signalfunction;
                        if (tmp->id == (int)mp1)
                        {
                           if (!timerfunc(tmp->data))
                           {
                              dw_timer_disconnect(tmp->id);
                           }
                           tmp = NULL;
                        }
                     }
                     result = 0;
                  }
                  break;
               case WM_SETFOCUS:
                  {
                     int (DWSIGNAL *setfocusfunc)(HWND, void *) = (int (*)(HWND, void *))tmp->signalfunction;

                     if(hWnd == tmp->window)
                     {
                        result = setfocusfunc(tmp->window, tmp->data);
                        tmp = NULL;
                     }
                  }
                  break;
               case WM_SIZE:
                  {
                     int (DWSIGNAL *sizefunc)(HWND, int, int, void *) = tmp->signalfunction;
                     if(hWnd == tmp->window)
                     {
                        result = sizefunc(tmp->window, LOWORD(mp2), HIWORD(mp2), tmp->data);
                        tmp = NULL;
                     }
                  }
                  break;
               case WM_LBUTTONDOWN:
                  {
                     int (DWSIGNAL *buttonfunc)(HWND, int, int, int, void *) = (int (*)(HWND, int, int, int, void *))tmp->signalfunction;

                     if(hWnd == tmp->window)
                     {
                        int button=0;

                        switch(origmsg)
                        {
                        case WM_LBUTTONDOWN:
                           button = DW_BUTTON1_MASK;
                           break;
                        case WM_RBUTTONDOWN:
                           button = DW_BUTTON2_MASK;
                           break;
                        case WM_MBUTTONDOWN:
                           button = DW_BUTTON3_MASK;
                           break;
                        }
                        if(taskbar)
                        {
                           POINT ptl;
                           GetCursorPos(&ptl);
                           result = buttonfunc(tmp->window, ptl.x, ptl.y, button, tmp->data);
                        }
                        else
                        {
                           POINTS pts = MAKEPOINTS(mp2);
                           result = buttonfunc(tmp->window, pts.x, pts.y, button, tmp->data);
                        }
                        tmp = NULL;
                     }
                  }
                  break;
               case WM_LBUTTONUP:
                  {
                     int (DWSIGNAL *buttonfunc)(HWND, int, int, int, void *) = (int (*)(HWND, int, int, int, void *))tmp->signalfunction;

                     if(hWnd == tmp->window)
                     {
                        int button=0;

                        switch(origmsg)
                        {
                        case WM_LBUTTONUP:
                           button = DW_BUTTON1_MASK;
                           break;
                        case WM_RBUTTONUP:
                           button = DW_BUTTON2_MASK;
                           break;
                        case WM_MBUTTONUP:
                           button = DW_BUTTON3_MASK;
                           break;
                        }
                        if(taskbar)
                        {
                           POINT ptl;
                           GetCursorPos(&ptl);
                           result = buttonfunc(tmp->window, ptl.x, ptl.y, button, tmp->data);
                        }
                        else
                        {
                           POINTS pts = MAKEPOINTS(mp2);
                           result = buttonfunc(tmp->window, pts.x, pts.y, button, tmp->data);
                        }
                        tmp = NULL;
                     }
                  }
                  break;
               case WM_MOUSEMOVE:
                  {
                     POINTS pts = MAKEPOINTS(mp2);
                     int (DWSIGNAL *motionfunc)(HWND, int, int, int, void *) = (int (*)(HWND, int, int, int, void *))tmp->signalfunction;

                     if(hWnd == tmp->window)
                     {
                        int keys = 0;

                        if (mp1 & MK_LBUTTON)
                           keys = DW_BUTTON1_MASK;
                        if (mp1 & MK_RBUTTON)
                           keys |= DW_BUTTON2_MASK;
                        if (mp1 & MK_MBUTTON)
                           keys |= DW_BUTTON3_MASK;

                        result = motionfunc(tmp->window, pts.x, pts.y, keys, tmp->data);
                        tmp = NULL;
                     }
                  }
                  break;
               case WM_CHAR:
                  {
                     int (DWSIGNAL *keypressfunc)(HWND, char, int, int, void *, char *) = tmp->signalfunction;

                     if(hWnd == tmp->window || _dw_toplevel_window(hWnd) == tmp->window)
                     {
                        int special = 0;
                        char *utf8 = NULL, ch[2] = {0};
#ifdef UNICODE
                        WCHAR uc[2] = { 0 };

                        uc[0] = (WCHAR)mp1;
                        utf8 = WideToUTF8(&uc[0]);
#endif

                        if(GetAsyncKeyState(VK_SHIFT) & 0x8000)
                           special |= KC_SHIFT;
                        if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
                           special |= KC_CTRL;
                               if(mp2 & (1 << 29))
                           special |= KC_ALT;

                        if(origmsg == WM_CHAR && mp1 < 128)
                           ch[0] = (char)mp1;

                        result = keypressfunc(tmp->window, ch[0], (int)mp1, special, tmp->data, utf8 ? utf8 : ch);
                        tmp = NULL;
                     }
                  }
                  break;
               case WM_CLOSE:
                  {
                     int (DWSIGNAL *closefunc)(HWND, void *) = tmp->signalfunction;

                     if(hWnd == tmp->window)
                     {
                        result = closefunc(tmp->window, tmp->data);
                        tmp = NULL;
                     }
                  }
                  break;
               case WM_PAINT:
                  {
                     PAINTSTRUCT ps;
                     DWExpose exp;
                     int (DWSIGNAL *exposefunc)(HWND, DWExpose *, void *) = tmp->signalfunction;

                     if ( hWnd == tmp->window )
                     {
                        BeginPaint(hWnd, &ps);
                        exp.x = ps.rcPaint.left;
                        exp.y = ps.rcPaint.top;
                        exp.width = ps.rcPaint.right - ps.rcPaint.left;
                        exp.height = ps.rcPaint.bottom - ps.rcPaint.top;
                        result = exposefunc(hWnd, &exp, tmp->data);
                        EndPaint(hWnd, &ps);
                     }
                  }
                  break;
               case WM_NOTIFY:
                  {
                     if(tmp->message == TVN_SELCHANGED ||
                        tmp->message == NM_RCLICK ||
                        tmp->message == TVN_ITEMEXPANDED)
                     {
                        NMTREEVIEW FAR *tem=(NMTREEVIEW FAR *)mp2;
                        NMLISTVIEW FAR *lem=(NMLISTVIEW FAR *)mp2;
                        TCHAR tmpbuf[100] = {0};

                        GetClassName(tem->hdr.hwndFrom, tmpbuf, 99);

                        if(_tcsnicmp(tmpbuf, WC_TREEVIEW, _tcslen(WC_TREEVIEW))==0)
                        {
                           if(tem->hdr.code == TVN_SELCHANGED && tmp->message == TVN_SELCHANGED)
                           {
                              if(tmp->window == tem->hdr.hwndFrom && !dw_window_get_data(tmp->window, "_dw_select_item"))
                              {
                                 int (DWSIGNAL *treeselectfunc)(HWND, HTREEITEM, char *, void *, void *) = tmp->signalfunction;
                                 TVITEM tvi;
                                 TCHAR textbuf[1025] = {0}, *textptr = textbuf;

                                 tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT;
                                 tvi.hItem = tem->itemNew.hItem;
                                 tvi.pszText = textbuf;
                                 tvi.cchTextMax = 1024;

                                 TreeView_GetItem(tmp->window, &tvi);

                                 result = treeselectfunc(tmp->window, tem->itemNew.hItem, WideToUTF8(textptr), tmp->data, (void *)tvi.lParam);

                                 tmp = NULL;
                              }
                           }
                           else if(tem->hdr.code == TVN_ITEMEXPANDED && tmp->message == TVN_ITEMEXPANDED)
                           {
                              if(tmp->window == tem->hdr.hwndFrom && tem->action == TVE_EXPAND)
                              {
                                 int (DWSIGNAL *treeexpandfunc)(HWND, HTREEITEM, void *) = tmp->signalfunction;

                                 result = treeexpandfunc(tmp->window, tem->itemNew.hItem, tmp->data);
                                 tmp = NULL;
                              }
                           }
                           else if(tem->hdr.code == NM_RCLICK && tmp->message == NM_RCLICK)
                           {
                              if(tmp->window == tem->hdr.hwndFrom)
                              {
                                 int (DWSIGNAL *containercontextfunc)(HWND, char *, int, int, void *, void *) = tmp->signalfunction;
                                 HTREEITEM hti;
                                 TVITEM tvi;
                                 TVHITTESTINFO thi;
                                 void *itemdata = NULL;
                                 LONG x, y;
                                 TCHAR textbuf[1025] = {0}, *textptr = textbuf;

                                 dw_pointer_query_pos(&x, &y);

                                 thi.pt.x = x;
                                 thi.pt.y = y;

                                 MapWindowPoints(HWND_DESKTOP, tmp->window, &thi.pt, 1);

                                 hti = TreeView_HitTest(tmp->window, &thi);

                                 if(hti)
                                 {
                                    tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT;
                                    tvi.hItem = hti;
                                    tvi.pszText = textbuf;
                                    tvi.cchTextMax = 1024;

                                    TreeView_GetItem(tmp->window, &tvi);
                                    TreeView_SelectItem(tmp->window, hti);

                                    itemdata = (void *)tvi.lParam;
                                 }
                                 containercontextfunc(tmp->window, WideToUTF8(textptr), x, y, tmp->data, itemdata);
                                 tmp = NULL;
                              }
                           }
                        }
                        else if(_tcsnicmp(tmpbuf, WC_LISTVIEW, _tcslen(WC_LISTVIEW)+1)==0)
                        {
                           if((lem->hdr.code == LVN_ITEMCHANGED && (lem->uChanged & LVIF_STATE)) && tmp->message == TVN_SELCHANGED)
                           {
                              if(tmp->window == tem->hdr.hwndFrom)
                              {
                                 LV_ITEM lvi;
                                 int iItem;

                                 iItem = ListView_GetNextItem(tmp->window, -1, LVNI_SELECTED);

                                 memset(&lvi, 0, sizeof(LV_ITEM));

                                 if(iItem > -1)
                                 {
                                    int (DWSIGNAL *treeselectfunc)(HWND, HWND, char *, void *, void *) = tmp->signalfunction;
                                    void **params;

                                    lvi.iItem = iItem;
                                    lvi.mask = LVIF_PARAM;

                                    ListView_GetItem(tmp->window, &lvi);

                                    params = (void **)lvi.lParam;

                                    treeselectfunc(tmp->window, 0, params ? (char *)params[_DW_DATA_TYPE_STRING] : NULL, tmp->data, params ? params[_DW_DATA_TYPE_POINTER] : NULL);
                                    tmp = NULL;
                                 }
                              }
                           }
                        }
                     }
                     else if(tmp->message == TCN_SELCHANGE)
                     {
                        NMHDR FAR *tem=(NMHDR FAR *)mp2;
                        if(tmp->window == tem->hwndFrom && tem->code == tmp->message)
                        {
                           int (DWSIGNAL *switchpagefunc)(HWND, unsigned long, void *) = tmp->signalfunction;
                           unsigned long num=dw_notebook_page_get(tem->hwndFrom);
                           result = switchpagefunc(tem->hwndFrom, num, tmp->data);
                           tmp = NULL;
                        }
                     }
                     else if(tmp->message == LVN_COLUMNCLICK)
                     {
                        NMLISTVIEW FAR *tem=(NMLISTVIEW FAR *)mp2;
                        if(tmp->window == tem->hdr.hwndFrom && tem->hdr.code == tmp->message)
                        {
                           int (DWSIGNAL *columnclickfunc)(HWND, int, void *) = tmp->signalfunction;
                           result = columnclickfunc(tem->hdr.hwndFrom, tem->iSubItem, tmp->data);
                           tmp = NULL;
                        }
                     }
                     else if(tmp->message == WM_VSCROLL)
                     {
                        NMUPDOWN FAR *tem=(NMUPDOWN FAR *)mp2;
                        if(tmp->window == tem->hdr.hwndFrom && tem->hdr.code == UDN_DELTAPOS)
                        {
                           int (DWSIGNAL *valuechangefunc)(HWND, int, void *) = tmp->signalfunction;
                           result = valuechangefunc(tmp->window, tem->iPos + tem->iDelta, tmp->data);
                           tmp = NULL;
                        }
                     }
                  }
                  break;
               case WM_COMMAND:
                  {
                     int (DWSIGNAL *clickfunc)(HWND, void *) = tmp->signalfunction;
                     HWND command;
                     ULONG passthru = (ULONG)LOWORD(mp1);
                     ULONG message = (ULONG)HIWORD(mp1);

                     command = (HWND)(uintptr_t)passthru;

                     if (message == LBN_SELCHANGE || message == CBN_SELCHANGE)
                     {
                        int (DWSIGNAL *listboxselectfunc)(HWND, int, void *) = tmp->signalfunction;

                        if (tmp->message == LBN_SELCHANGE && tmp->window == (HWND)mp2)
                        {
                           result = listboxselectfunc(tmp->window, dw_listbox_selected(tmp->window), tmp->data);
                           tmp = NULL;
                        }
                     }
#ifdef TOOLBAR
                     else if (message == BN_CLICKED && tmp->message == WM_COMMAND && tmp->window == (HWND)mp2)
                     {
                        TCHAR tmpbuf[100] = {0};

                        GetClassName((HWND)mp2, tmpbuf, 99);

                        /* Make sure this isn't a button, because it will have already been handled */
                        if (_tcsnicmp(tmpbuf, BUTTONCLASSNAME, _tcslen(BUTTONCLASSNAME)+1) != 0)
                        {
                           result = clickfunc(tmp->window, tmp->data);
                           tmp = NULL;
                        }
                     }
#endif
                     else if (tmp->id && passthru == tmp->id)
                     {
                        HMENU hwndmenu = GetMenu(hWnd), menuowner = _dw_menu_owner((HMENU)tmp->window);

                        if (menuowner == hwndmenu || !menuowner)
                        {
                           _dw_toggle_checkable_menu_item( tmp->window, tmp->id );
                           /*
                            * Call the user supplied callback
                            */
                           result = clickfunc((HWND)(uintptr_t)tmp->id, tmp->data);
                           tmp = NULL;
                        }
                     } /* this fires for checkable menu items */
                     else if ( tmp->window < (HWND)65536 && command == tmp->window && tmp->message != WM_TIMER )
                     {
                        _dw_toggle_checkable_menu_item( _dw_popup ? _dw_popup : tmp->window, DW_POINTER_TO_INT(tmp->data) );
                        result = clickfunc(_dw_popup ? _dw_popup : tmp->window, tmp->data);
                        tmp = NULL;
                     }
                  }
                  break;
               case WM_HSCROLL:
               case WM_VSCROLL:
                  {
                     TCHAR tmpbuf[100] = {0};
                     HWND handle = (HWND)mp2;
                     int (DWSIGNAL *valuechangefunc)(HWND, int, void *) = tmp->signalfunction;

                     if(!GetClassName(handle, tmpbuf, 99))
                     {
                         GetClassName(hWnd, tmpbuf, 99);
                     }

                     if (_tcsnicmp(tmpbuf, TRACKBAR_CLASS, _tcslen(TRACKBAR_CLASS)+1)==0)
                     {

                        if (handle == tmp->window)
                        {
                           int value = (int)SendMessage(handle, TBM_GETPOS, 0, 0);
                           int max = (int)SendMessage(handle, TBM_GETRANGEMAX, 0, 0);
                           ULONG currentstyle = GetWindowLong(handle, GWL_STYLE);

                           if(currentstyle & TBS_VERT)
                              result = valuechangefunc(tmp->window, max - value, tmp->data);
                           else
                              result = valuechangefunc(tmp->window, value, tmp->data);
                           tmp = NULL;
                        }
                     }
                     else if(_tcsnicmp(tmpbuf, SCROLLBARCLASSNAME, _tcslen(SCROLLBARCLASSNAME)+1)==0)
                     {
                        if(handle == tmp->window)
                        {
                           int bar = (origmsg == WM_HSCROLL) ? SB_HORZ : SB_VERT;
                           int value = _dw_handle_scroller(handle, bar, (int)HIWORD(mp1), (int)LOWORD(mp1));

                           if(value > -1)
                           {
                              dw_scrollbar_set_pos(tmp->window, value);
                              result = valuechangefunc(tmp->window, value, tmp->data);
                           }
                           tmp = NULL;
                           msg = 0;
                        }
                     }
                  }
                  break;
               case WM_USER+100:
                  {
                     if(hWnd == tmp->window)
                     {
                        int (DWSIGNAL *htmlresultfunc)(HWND, int, char *, void *, void *) = tmp->signalfunction;
                        void** params = (void**)mp1;

                        return htmlresultfunc(tmp->window, DW_POINTER_TO_INT(params[1]), (char *)params[0], (void *)mp2, tmp->data);
                     }
                  }
                  break;
               case WM_USER+101:
                  {
                     if(hWnd == tmp->window)
                     {
                        int (DWSIGNAL *htmlchangedfunc)(HWND, int, char *, void *) = tmp->signalfunction;

                        return htmlchangedfunc(tmp->window, DW_POINTER_TO_INT(mp1), (char *)mp2, tmp->data);
                     }
                  }
                  break;
               case WM_USER+102:
                  {
                     if(hWnd == tmp->window)
                     {
                        int (DWSIGNAL *clickfunc)(HWND, void *) = tmp->signalfunction;

                        return clickfunc(tmp->window, tmp->data);
                     }
                  }
                  break;
            }
         }
         if(tmp)
            tmp = tmp->next;
      }
   }

   /* Now that any handlers are done... do normal processing */
   switch( msg )
   {
#ifdef AEROGLASS
   case WM_DWMCOMPOSITIONCHANGED:
      {
         ColorInfo *cinfo = _dw_window_get_cinfo(hWnd);
         
         if(_DwmIsCompositionEnabled)
            _DwmIsCompositionEnabled(&_dw_composition);
         
         /* If we are no longer compositing... */
         if(!_dw_composition)
         {
            MARGINS mar = {0};

            SetLayeredWindowAttributes(hWnd, _dw_transparencykey, 255, LWA_ALPHA);
            if(_DwmExtendFrameIntoClientArea)
               _DwmExtendFrameIntoClientArea(hWnd, &mar);
            _dw_show_margins(hWnd, mar, __LINE__);
         }
         /* If we have started compositing... */
         else
         {
            MARGINS mar = {-1,-1,-1,-1};

            if(cinfo && (cinfo->style & DW_FCF_COMPOSITED) && !IS_WIN8PLUS)
               SetLayeredWindowAttributes(hWnd, _dw_transparencykey, 0, LWA_COLORKEY);
            else
            {
#ifdef DARK_MODE_TITLEBAR_MENU
               if(cinfo)
                  mar = _dw_rect_to_margins(cinfo->rect);
               else
#endif
                  memset(&mar, 0, sizeof(MARGINS));
               SetLayeredWindowAttributes(hWnd, _dw_transparencykey, 255, LWA_ALPHA);
            }
            if(_DwmExtendFrameIntoClientArea)
               _DwmExtendFrameIntoClientArea(hWnd, &mar);
            _dw_show_margins(hWnd, mar, __LINE__);
         }
      }
      break;
   case WM_SETTINGCHANGE:
   {
      if(_DW_DARK_MODE_SUPPORTED && _dw_is_color_scheme_change_message(mp2))
      {
         _DW_DARK_MODE_ENABLED = _dw_should_apps_use_dark_mode();

         _dw_refresh_titlebar_theme_color(hWnd);
         _dw_set_child_window_theme(hWnd, 0);
         EnumChildWindows(hWnd, _dw_set_child_window_theme, 0);
      }
   }
   break;
#endif
   case WM_PAINT:
      {
         PAINTSTRUCT ps;

         BeginPaint(hWnd, &ps);
         EndPaint(hWnd, &ps);
      }
      break;
   case WM_SIZE:
      {
         int x = LOWORD(mp2), y = HIWORD(mp2);

         Box *mybox = (Box *)GetWindowLongPtr(hWnd, GWLP_USERDATA);

         if(mybox && mybox->count)
         {
            static int lastx = -1, lasty = -1;
            static HWND lasthwnd = 0;
            int xborder = 0, yborder = 0;

#ifdef DARK_MODE_TITLEBAR_MENU
            if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_SUPPORTED)
            {
               ColorInfo *cinfo = _dw_window_get_cinfo(hWnd);

               if(cinfo)
               {
                  x -= (cinfo->rect.left + cinfo->rect.right);
                  y -= (cinfo->rect.top + cinfo->rect.bottom);
                  xborder = cinfo->rect.left;
                  yborder = cinfo->rect.top;
               }
            }
#endif

            /* Simple check to prevent duplicate sizing calls */
            if(lastx != x || lasty != y || lasthwnd != hWnd)
            {
               lastx = x;
               lasty = y;
               lasthwnd = hWnd;

               ShowWindow(mybox->items[0].hwnd, SW_HIDE);
               _dw_do_resize(mybox, x, y, xborder, yborder);
               ShowWindow(mybox->items[0].hwnd, SW_SHOW);
               return 0;
            }
         }
      }
      break;
   case WM_USER:
      windowfunc = (void *)mp1;

      if(windowfunc)
         windowfunc((void *)mp2);
      break;
   case WM_USER+5:
      _dw_free_menu_data((HMENU)mp1);
      DestroyMenu((HMENU)mp1);
      break;
   case WM_NOTIFY:
      {
         NMHDR FAR *tem=(NMHDR FAR *)mp2;

         if(tem->code == TCN_SELCHANGING)
         {
            int num=TabCtrl_GetCurSel(tem->hwndFrom);
            NotebookPage **array = (NotebookPage **)dw_window_get_data(tem->hwndFrom, "_dw_array");

            if(num > -1 && array && array[num])
               SetParent(array[num]->hwnd, DW_HWND_OBJECT);

         }
         else if(tem->code == TCN_SELCHANGE)
         {
            int num=TabCtrl_GetCurSel(tem->hwndFrom);
            NotebookPage **array = (NotebookPage **)dw_window_get_data(tem->hwndFrom, "_dw_array");

            if(num > -1 && array && array[num])
               SetParent(array[num]->hwnd, tem->hwndFrom);

            _dw_resize_notebook_page(tem->hwndFrom, num);
         }
         else if(tem->code == LVN_DELETEITEM)
         {
            NMLISTVIEW FAR *lem=(NMLISTVIEW FAR *)mp2;
            LV_ITEM lvi;
            void **params;

            memset(&lvi, 0, sizeof(LV_ITEM));

            lvi.iItem = lem->iItem;
            lvi.mask = LVIF_PARAM;

            ListView_GetItem(lem->hdr.hwndFrom, &lvi);
            params = (void **)lvi.lParam;

            /* Free row data */
            if(params)
            {
               if(params[_DW_DATA_TYPE_STRING])
                  free(params[_DW_DATA_TYPE_STRING]);
               free(params);
            }
         }
      }
      break;
   case WM_HSCROLL:
   case WM_VSCROLL:
      {
         HWND handle = (HWND)mp2;
         int bar = (origmsg == WM_HSCROLL) ? SB_HORZ : SB_VERT;

         if(dw_window_get_data(handle, "_dw_scrollbar"))
         {
            int value = _dw_handle_scroller(handle, bar, (int)HIWORD(mp1), (int)LOWORD(mp1));

            if(value > -1)
               dw_scrollbar_set_pos(handle, value);
         }
         else
         {
            TCHAR tmpbuf[100] = {0};

            GetClassName( hWnd, tmpbuf, 99 );
            if ( _tcsnicmp(tmpbuf, FRAMECLASSNAME, _tcslen(FRAMECLASSNAME)+1 ) == 0 )
            {
               _dw_handle_scroller(hWnd, bar, (int)HIWORD(mp1), (int)LOWORD(mp1));
            }
         }
      }
      break;
   case WM_GETMINMAXINFO:
      {
         MINMAXINFO *info = (MINMAXINFO *)mp2;
         info->ptMinTrackSize.x = 8;
         info->ptMinTrackSize.y = 8;
         return 0;
      }
      break;
   case WM_DESTROY:
      {
         HMENU menu = GetMenu(hWnd);
         ColorInfo *cinfo = _dw_window_get_cinfo(hWnd);

         if(menu || (cinfo && (menu = cinfo->hmenu)))
            _dw_free_menu_data(menu);

         /* Free memory before destroying */
         _dw_free_window_memory(hWnd, 0);
         EnumChildWindows(hWnd, _dw_free_window_memory, 0);
      }
      break;
   case WM_MOUSEMOVE:
      {
         HCURSOR cursor;

         if((cursor = (HCURSOR)dw_window_get_data(hWnd, "_dw_cursor")) ||
            (cursor = (HCURSOR)dw_window_get_data(_dw_toplevel_window(hWnd), "_dw_cursor")))
         {
            SetCursor(cursor);
         }
      }
      break;
   case WM_CTLCOLORSTATIC:
   case WM_CTLCOLORLISTBOX:
   case WM_CTLCOLORBTN:
   case WM_CTLCOLOREDIT:
   case WM_CTLCOLORMSGBOX:
   case WM_CTLCOLORSCROLLBAR:
   case WM_CTLCOLORDLG:
      return _dw_colorwndproc(hWnd, msg, mp1, mp2);
   }
   if(result != -1)
   {
      /* Make sure any queued redraws are handled */
      _dw_redraw(0, FALSE);
      /* Then finally return */
      return result;
   }
   return DefWindowProc(hWnd, msg, mp1, mp2);
}

VOID CALLBACK _dw_timerproc(HWND hwnd, UINT msg, UINT_PTR idEvent, DWORD dwTime)
{
   _dw_wndproc(hwnd, msg, (WPARAM)idEvent, 0);
}

LRESULT CALLBACK _dw_framewndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
   switch( msg )
   {
   case WM_LBUTTONDOWN:
   case WM_MBUTTONDOWN:
   case WM_RBUTTONDOWN:
      SetActiveWindow(hWnd);
      SetFocus(hWnd);
      break;
   case WM_COMMAND:
   case WM_NOTIFY:
   case WM_MOUSEMOVE:
      _dw_wndproc(hWnd, msg, mp1, mp2);
      break;
#ifdef AEROGLASS
   case WM_THEMECHANGED:
      if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_SUPPORTED)
      {
         SetClassLongPtr(hWnd, GCLP_HBRBACKGROUND, (LONG_PTR)_dw_get_syscolor_brush(COLOR_3DFACE));
         InvalidateRect(hWnd, NULL, TRUE);
      }
      break;
   case WM_ERASEBKGND:
      if(_dw_composition)
      {
         ColorInfo *cinfo = _dw_window_get_cinfo(_dw_toplevel_window(hWnd));

         if(cinfo && cinfo->style & DW_FCF_COMPOSITED && !IS_WIN8PLUS)
         {
            static HBRUSH hbrush = 0;
            RECT rect;

            if(!hbrush)
               hbrush = CreateSolidBrush(_dw_transparencykey);

            GetClientRect(hWnd, &rect);
            FillRect((HDC)mp1, &rect, hbrush);
            return TRUE;
         }
      }
      break;
#endif
   case WM_PAINT:
      {
         ColorInfo *thiscinfo = _dw_window_get_cinfo(hWnd);

         if(thiscinfo && thiscinfo->fore != -1 && thiscinfo->back != -1)
         {
            PAINTSTRUCT ps;
            HDC hdcPaint = BeginPaint(hWnd, &ps);
            int success = FALSE;

            if(thiscinfo && thiscinfo->fore != -1 && thiscinfo->back != -1)
            {
               /* Handle foreground */
               if(thiscinfo->fore > -1 && thiscinfo->fore < 18)
               {
                  if(thiscinfo->fore != DW_CLR_DEFAULT)
                  {
                     SetTextColor((HDC)mp1, RGB(_dw_red[thiscinfo->fore],
                                          _dw_green[thiscinfo->fore],
                                          _dw_blue[thiscinfo->fore]));
                  }
               }
               else if((thiscinfo->fore & DW_RGB_COLOR) == DW_RGB_COLOR)
               {
                  SetTextColor((HDC)mp1, RGB(DW_RED_VALUE(thiscinfo->fore),
                                       DW_GREEN_VALUE(thiscinfo->fore),
                                       DW_BLUE_VALUE(thiscinfo->fore)));
               }
               /* Handle background */
               if(thiscinfo->back > -1 && thiscinfo->back < 18)
               {
                  if(thiscinfo->back != DW_CLR_DEFAULT)
                  {
                     SetBkColor((HDC)mp1, RGB(_dw_red[thiscinfo->back],
                                        _dw_green[thiscinfo->back],
                                        _dw_blue[thiscinfo->back]));
                     if(thiscinfo->hbrush)
                        DeleteObject(thiscinfo->hbrush);
                     thiscinfo->hbrush = CreateSolidBrush(RGB(_dw_red[thiscinfo->back],
                                                    _dw_green[thiscinfo->back],
                                                    _dw_blue[thiscinfo->back]));
                     SelectObject(hdcPaint, thiscinfo->hbrush);
                     Rectangle(hdcPaint, ps.rcPaint.left - 1, ps.rcPaint.top - 1, ps.rcPaint.right + 1, ps.rcPaint.bottom + 1);
                     success = TRUE;
                  }
               }
               else if((thiscinfo->back & DW_RGB_COLOR) == DW_RGB_COLOR)
               {
                  SetBkColor((HDC)mp1, RGB(DW_RED_VALUE(thiscinfo->back),
                                     DW_GREEN_VALUE(thiscinfo->back),
                                     DW_BLUE_VALUE(thiscinfo->back)));
                  if(thiscinfo->hbrush)
                     DeleteObject(thiscinfo->hbrush);
                  thiscinfo->hbrush = CreateSolidBrush(RGB(DW_RED_VALUE(thiscinfo->back),
                                                 DW_GREEN_VALUE(thiscinfo->back),
                                                 DW_BLUE_VALUE(thiscinfo->back)));
                  SelectObject(hdcPaint, thiscinfo->hbrush);
                  Rectangle(hdcPaint, ps.rcPaint.left - 1, ps.rcPaint.top - 1, ps.rcPaint.right + 1, ps.rcPaint.bottom + 1);
                  success = TRUE;
               }
            }

            EndPaint(hWnd, &ps);
            if(success)
               return FALSE;
         }

      }
      break;
   }
   return DefWindowProc(hWnd, msg, mp1, mp2);
}

LRESULT CALLBACK _dw_rendwndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
   LRESULT rcode = TRUE;

   switch( msg )
   {
   case WM_LBUTTONDOWN:
   case WM_MBUTTONDOWN:
   case WM_RBUTTONDOWN:
      SetFocus(hWnd);
      rcode = _dw_wndproc(hWnd, msg, mp1, mp2);
      break;
   case WM_MOUSEMOVE:
      /* call our standard Windows procedure */
      rcode = _dw_wndproc(hWnd, msg, mp1, mp2);
      break;
   case WM_USER+2:
   case WM_LBUTTONUP:
   case WM_MBUTTONUP:
   case WM_RBUTTONUP:
      rcode = _dw_wndproc(hWnd, msg, mp1, mp2);
      break;
   case WM_PAINT:
   case WM_SIZE:
   case WM_COMMAND:
   case WM_CHAR:
   case WM_KEYDOWN:
      rcode = _dw_wndproc(hWnd, msg, mp1, mp2);
      break;
   default:
      break;
   }
   /* only call the default Windows process if the user hasn't handled the message themselves */
   if ( rcode != 0 )
      rcode = DefWindowProc(hWnd, msg, mp1, mp2);
   return rcode;
}

LRESULT CALLBACK _spinnerwndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
   ColorInfo *cinfo = _dw_window_get_cinfo(hWnd);

   if(msg == WM_MOUSEMOVE)
      _dw_wndproc(hWnd, msg, mp1, mp2);

   if(cinfo)
   {
      switch( msg )
      {
      case WM_LBUTTONDOWN:
      case WM_MBUTTONDOWN:
      case WM_RBUTTONDOWN:
      case WM_KEYDOWN:
         {
            LRESULT ret;

            if(!cinfo || !cinfo->pOldProc)
               ret = DefWindowProc(hWnd, msg, mp1, mp2);
            else
               ret = CallWindowProc(cinfo->pOldProc, hWnd, msg, mp1, mp2);

            /* Tell the edit control that a buttonpress has
             * occured and to update it's window title.
             */
            if(cinfo && cinfo->buddy)
               SendMessage(cinfo->buddy, WM_USER+10, 0, 0);

            SetTimer(hWnd, 100, 100, (TIMERPROC)NULL);

            return ret;
         }
         break;
      case WM_LBUTTONUP:
      case WM_MBUTTONUP:
      case WM_RBUTTONUP:
      case WM_KEYUP:
         {
            LRESULT ret;

            if(!cinfo || !cinfo->pOldProc)
               ret = DefWindowProc(hWnd, msg, mp1, mp2);
            ret = CallWindowProc(cinfo->pOldProc, hWnd, msg, mp1, mp2);

            /* Tell the edit control that a buttonpress has
             * occured and to update it's window title.
             */
            if(cinfo && cinfo->buddy)
               SendMessage(cinfo->buddy, WM_USER+10, 0, 0);

            if(hWnd)
               KillTimer(hWnd, 100);

            return ret;
         }
         break;
      case WM_TIMER:
         {
            if(mp1 == 100)
            {
               LRESULT ret;

               if(cinfo && cinfo->buddy)
                  SendMessage(cinfo->buddy, WM_USER+10, 0, 0);

               if(!cinfo || !cinfo->pOldProc)
                  ret = DefWindowProc(hWnd, msg, mp1, mp2);
               ret = CallWindowProc(cinfo->pOldProc, hWnd, msg, mp1, mp2);

               /* Tell the edit control that a buttonpress has
                * occured and to update it's window title.
                */
               if(cinfo && cinfo->buddy)
                  SendMessage(cinfo->buddy, WM_USER+10, 0, 0);

               return ret;
            }
         }
         break;
      case WM_USER+10:
         {
            if(cinfo->buddy)
            {
               TCHAR tempbuf[100] = { 0 };
               long position;

               GetWindowText(cinfo->buddy, tempbuf, 99);

               position = _tstol(tempbuf);

               SendMessage(hWnd, UDM_SETPOS32, 0, (LPARAM)position);
            }
         }
         break;
      }
   }

   if(!cinfo || !cinfo->pOldProc)
      return DefWindowProc(hWnd, msg, mp1, mp2);
   return CallWindowProc(cinfo->pOldProc, hWnd, msg, mp1, mp2);
}

void _dw_click_default(HWND handle)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   /* These are the window classes which can
    * obtain input focus.
    */
   if (_tcsnicmp(tmpbuf, BUTTONCLASSNAME, _tcslen(BUTTONCLASSNAME)+1)==0
#ifdef TOOLBAR
    || _tcsnicmp(tmpbuf, TOOLBARCLASSNAME, _tcslen(TOOLBARCLASSNAME)+1) == 0
#endif
      )
   {
      /* Generate click on default item */
      DWSignalHandler *tmp = Root;

      /* Find any callbacks for this function */
      while (tmp)
      {
         if (tmp->message == WM_COMMAND)
         {
            /* Make sure it's the right window, and the right ID */
            if (tmp->window == handle)
            {
               int (DWSIGNAL *clickfunc)(HWND, void *) = tmp->signalfunction;
               clickfunc(tmp->window, tmp->data);
               tmp = NULL;
            }
         }
         if (tmp)
            tmp= tmp->next;
      }
   }
   else
      SetFocus(handle);
}

/* Subclass function that will handle setting colors on controls */
LRESULT CALLBACK _dw_colorwndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
   ColorInfo *cinfo = _dw_window_get_cinfo(hWnd);
   TCHAR tmpbuf[100] = {0};
   WNDPROC pOldProc = 0;
   LRESULT ret = -1;

   GetClassName(hWnd, tmpbuf, 99);
   if(_tcsncmp(tmpbuf, FRAMECLASSNAME, _tcslen(FRAMECLASSNAME)+1) == 0)
      cinfo = &(((Box *)cinfo)->cinfo);

   if ( msg == WM_MOUSEMOVE || msg == WM_USER+2 )
      ret = _dw_wndproc(hWnd, msg, mp1, mp2);

   if (cinfo)
   {
      pOldProc = cinfo->pOldProc;

      switch( msg )
      {
      case WM_SETFOCUS:
         if(cinfo->combo)
            ret = _dw_wndproc(cinfo->combo, msg, mp1, mp2);
         else
            ret = _dw_wndproc(hWnd, msg, mp1, mp2);
         break;
      case WM_VSCROLL:
      case WM_HSCROLL:
            ret = _dw_wndproc(hWnd, msg, mp1, mp2);
         break;
      case WM_KEYDOWN:
      case WM_KEYUP:
         {
            if (hWnd && (mp1 == VK_UP || mp1 == VK_DOWN))
            {
               if (!cinfo || !cinfo->pOldProc)
                  ret = DefWindowProc(hWnd, msg, mp1, mp2);
               ret = CallWindowProc(cinfo->pOldProc, hWnd, msg, mp1, mp2);

               /* Tell the spinner control that a keypress has
                * occured and to update it's internal value.
                */
               if (cinfo && cinfo->buddy && !cinfo->combo)
                  PostMessage(hWnd, WM_USER+10, 0, 0);

               if(msg == WM_KEYDOWN)
                  SetTimer(hWnd, 101, 100, (TIMERPROC)NULL);
               else
                  KillTimer(hWnd, 101);

               return ret;
            }
         }
         break;
      case WM_TIMER:
         {
            if(mp1 == 101)
            {
               if(!cinfo || !cinfo->pOldProc)
                  ret = DefWindowProc(hWnd, msg, mp1, mp2);
               ret = CallWindowProc(cinfo->pOldProc, hWnd, msg, mp1, mp2);

               /* Tell the spinner control that a keypress has
                * occured and to update it's internal value.
                */
               if(cinfo && cinfo->buddy && !cinfo->combo)
                  PostMessage(hWnd, WM_USER+10, 0, 0);

               return ret;
            }
         }
         break;
      case WM_CHAR:
         ret = _dw_wndproc(hWnd, msg, mp1, mp2);
         if (ret != TRUE && LOWORD(mp1) == '\t')
         {
            if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
            {
               if (cinfo->combo)
                  _dw_shift_focus(cinfo->combo, _DW_DIRECTION_BACKWARD);
               else if(cinfo->buddy)
                  _dw_shift_focus(cinfo->buddy, _DW_DIRECTION_BACKWARD);
               else
                  _dw_shift_focus(hWnd, _DW_DIRECTION_BACKWARD);
            }
            else
            {
               if (cinfo->combo)
                  _dw_shift_focus(cinfo->combo, _DW_DIRECTION_FORWARD);
               else if(cinfo->buddy)
                  _dw_shift_focus(cinfo->buddy, _DW_DIRECTION_FORWARD);
               else
                  _dw_shift_focus(hWnd, _DW_DIRECTION_FORWARD);
            }
            return FALSE;
         }
         else if(LOWORD(mp1) == '\r')
         {

            if ( cinfo->clickdefault )
            {
               _dw_click_default(cinfo->clickdefault);
               return (LRESULT)TRUE;
            }
            else
            {
               /*
                * Find the toplevel window for the current window and check if it
                * has a default click set
                */
               HWND tl = _dw_toplevel_window( hWnd );
               ColorInfo *mycinfo = _dw_window_get_cinfo(tl);

               if(mycinfo && mycinfo->clickdefault)
               {
                  _dw_click_default(mycinfo->clickdefault);
                  return (LRESULT)TRUE;
               }
            }
         }

         /* Tell the spinner control that a keypress has
          * occured and to update it's internal value.
          */
         if (cinfo->buddy && !cinfo->combo)
         {
            PostMessage(cinfo->buddy, WM_USER+10, 0, 0);
         }
         break;
      case WM_USER+10:
         {
            if(cinfo->buddy)
            {
               long val;

               val = (long)SendMessage(cinfo->buddy, UDM_GETPOS32, 0, 0);

               _sntprintf(tmpbuf, 99, TEXT("%ld"), val);
               SetWindowText(hWnd, tmpbuf);
            }
         }
         break;
      case WM_CTLCOLORSTATIC:
      case WM_CTLCOLORLISTBOX:
      case WM_CTLCOLORBTN:
      case WM_CTLCOLOREDIT:
      case WM_CTLCOLORMSGBOX:
      case WM_CTLCOLORSCROLLBAR:
      case WM_CTLCOLORDLG:
         {
            ColorInfo *thiscinfo = _dw_window_get_cinfo((HWND)mp2);

            if(msg == WM_CTLCOLORBTN)
            {
               /* Groupbox color info is on the frame window it is attached to */
               if(GetWindowLongPtr((HWND)mp2, GWL_STYLE) & BS_GROUPBOX)
               {
                  Box *framebox = (Box *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
                  if(framebox)
                     thiscinfo = &framebox->cinfo;
               }
            }

            if(thiscinfo && thiscinfo->fore != -1 && thiscinfo->back != -1)
            {
               int thisback = thiscinfo->back;

               /* Handle foreground */
               if(thiscinfo->fore != DW_CLR_DEFAULT)
               {
                  int fore = _dw_internal_color(thiscinfo->fore);

                  SetTextColor((HDC)mp1, RGB(DW_RED_VALUE(fore),
                                       DW_GREEN_VALUE(fore),
                                       DW_BLUE_VALUE(fore)));
               }
#ifdef AEROGLASS
               else if(thiscinfo->fore == DW_CLR_DEFAULT && _DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_ENABLED)
                  SetTextColor((HDC)mp1, _dw_get_syscolor(COLOR_WINDOWTEXT));
#endif
               /* Handle background */
               if(thiscinfo->back == DW_RGB_TRANSPARENT)
               {
                  ColorInfo *parentcinfo = _dw_window_get_cinfo(hWnd);

                  if(parentcinfo && parentcinfo->back != -1)
                     thisback = parentcinfo->back;
               }
               if(thisback == DW_CLR_DEFAULT)
               {
                  HBRUSH hbr = _dw_get_syscolor_brush(COLOR_3DFACE);

                  SetBkColor((HDC)mp1, _dw_get_syscolor(COLOR_3DFACE));

                  SelectObject((HDC)mp1, hbr);
                  return (LRESULT)(intptr_t)hbr;
               }
               else if(thisback != -1 && thisback != DW_RGB_TRANSPARENT)
               {
                  int back = _dw_internal_color(thisback);

                  SetBkColor((HDC)mp1, RGB(DW_RED_VALUE(back),
                                     DW_GREEN_VALUE(back),
                                     DW_BLUE_VALUE(back)));
                  if(thiscinfo->hbrush)
                     DeleteObject(thiscinfo->hbrush);
                  thiscinfo->hbrush = CreateSolidBrush(RGB(DW_RED_VALUE(back),
                                                 DW_GREEN_VALUE(back),
                                                 DW_BLUE_VALUE(back)));
                  SelectObject((HDC)mp1, thiscinfo->hbrush);
                  return (LRESULT)(intptr_t)thiscinfo->hbrush;
               }
            }
 #ifdef AEROGLASS
            /* First handle the transparent or layered cases */
            switch(msg)
            {
               case WM_CTLCOLORSTATIC:
               case WM_CTLCOLORBTN:
               case WM_CTLCOLORDLG:
                  {
                     ColorInfo *tlcinfo = _dw_window_get_cinfo(_dw_toplevel_window(hWnd));

                     if((_dw_composition && tlcinfo && (tlcinfo->style & DW_FCF_COMPOSITED)) && !IS_WIN8PLUS &&
                        (!thiscinfo || (thiscinfo && (thiscinfo->back == -1 || thiscinfo->back == DW_RGB_TRANSPARENT))))
                     {
                        if(!(msg == WM_CTLCOLORSTATIC && SendMessage((HWND)mp2, STM_GETIMAGE, IMAGE_BITMAP, 0)))
                        {
                           SetBkColor((HDC)mp1, _dw_transparencykey);
                           if(thiscinfo && thiscinfo->hbrush)
                              DeleteObject(thiscinfo->hbrush);
                           thiscinfo->hbrush = CreateSolidBrush(_dw_transparencykey);
                           SelectObject((HDC)mp1, thiscinfo->hbrush);
                           return (LRESULT)thiscinfo->hbrush;
                        }
                     }
                  }
            }
            /* Second we handle the dark mode cases */
            switch(msg)
            {
               case WM_CTLCOLORSTATIC:
               case WM_CTLCOLORLISTBOX:
               case WM_CTLCOLORBTN:
               case WM_CTLCOLOREDIT:
               case WM_CTLCOLORMSGBOX:
               case WM_CTLCOLORSCROLLBAR:
               case WM_CTLCOLORDLG:
               {
                  if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_ENABLED)
                  {
                     ColorInfo *parentcinfo = _dw_window_get_cinfo(hWnd);
                     int thisback = thiscinfo ? thiscinfo->back : -1;

                     if(thisback == DW_RGB_TRANSPARENT && parentcinfo)
                        thisback = parentcinfo->back;

                     if(!thiscinfo || (thiscinfo && (thiscinfo->fore == DW_CLR_DEFAULT || thiscinfo->fore == -1)))
                        SetTextColor((HDC)mp1, _dw_get_syscolor(COLOR_WINDOWTEXT));
                     if(!thiscinfo || (thiscinfo && (thisback == DW_CLR_DEFAULT || thisback == -1 || thisback == DW_RGB_TRANSPARENT)))
                     {
                        HBRUSH hbr = _dw_get_syscolor_brush(COLOR_3DFACE);

                        SetBkColor((HDC)mp1, _dw_get_syscolor(COLOR_3DFACE));

                        SelectObject((HDC)mp1, hbr);
                        return (LRESULT)(intptr_t)hbr;
                     }
                  }
               }
            }
#endif
         }
         break;
      }
   }

   if(ret != TRUE)
   {
       if(!pOldProc)
          return DefWindowProc(hWnd, msg, mp1, mp2);
       return CallWindowProc(pOldProc, hWnd, msg, mp1, mp2);
   }
   return ret;
}

/* Window procedure for container/listview */
LRESULT CALLBACK _dw_containerwndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
   ContainerInfo *continfo = (ContainerInfo *)GetWindowLongPtr(hWnd, GWLP_USERDATA);

   switch( msg )
   {
   case WM_COMMAND:
   case WM_NOTIFY:
   case WM_MOUSEMOVE:
      _dw_wndproc(hWnd, msg, mp1, mp2);
      break;
#ifdef AEROGLASS
   case WM_THEMECHANGED:
      if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_SUPPORTED)
      {
         if(!continfo || continfo->cinfo.back == -1 || continfo->cinfo.back == DW_CLR_DEFAULT)
         {
            COLORREF bk = _dw_get_syscolor(COLOR_WINDOW);

            ListView_SetBkColor(hWnd, bk);
            ListView_SetTextBkColor(hWnd, bk);
         }
         if(!continfo || continfo->cinfo.fore == -1 || continfo->cinfo.fore == DW_CLR_DEFAULT)
            ListView_SetTextColor(hWnd, _dw_get_syscolor(COLOR_WINDOWTEXT));
      }
      break;
#endif
   case WM_PAINT:
       if(continfo->cinfo.pOldProc && (continfo->even != DW_RGB_TRANSPARENT || continfo->odd != DW_RGB_TRANSPARENT))
       {
            RECT rectUpd, rectDestin, rectThis, *rect = &rectThis;
            int iItems, iTop, i;
            COLORREF c, odd, even;
            unsigned long temp = _dw_internal_color(continfo->odd);

            /* Create the colors based on the current theme */
            if(continfo->odd == DW_CLR_DEFAULT)
            {
#ifdef AEROGLASS
               if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_ENABLED)
                  odd = RGB(100,100,100);
               else
#endif
                  odd = RGB(230, 230, 230);
            }
            else
                  odd = RGB(DW_RED_VALUE(temp), DW_GREEN_VALUE(temp), DW_BLUE_VALUE(temp));
            temp = _dw_internal_color(continfo->even);
            if(continfo->even == DW_CLR_DEFAULT)
               even = DW_RGB_TRANSPARENT;
            else
               even = RGB(DW_RED_VALUE(temp), DW_GREEN_VALUE(temp), DW_BLUE_VALUE(temp));

            /* Load the default background color for the first pass */
            ListView_SetTextBkColor(hWnd, continfo->cinfo.back != -1 ? continfo->cinfo.back : ListView_GetBkColor(hWnd));
            /* get the rectangle to be updated */
            GetUpdateRect(hWnd, &rectUpd, FALSE);
            /* allow default processing first */
            CallWindowProc(continfo->cinfo.pOldProc, hWnd, msg, 0, 0);
            /* number of displayed rows */
            iItems = ListView_GetCountPerPage(hWnd);
            /* first visible row */
            iTop = ListView_GetTopIndex(hWnd);

            for(i=iTop; i<=(iTop+iItems+1); i++)
            {
                /* if row rectangle intersects update rectangle then it requires re-drawing */
                if(ListView_GetItemRect(hWnd, i, rect, LVIR_BOUNDS) && IntersectRect(&rectDestin, &rectUpd, rect))
                {
                    /* change text background colour accordingly */
                    c = (i % 2) ? odd : even;

                    if(c != DW_RGB_TRANSPARENT)
                    {
                        ListView_SetTextBkColor(hWnd, c);
                        /* invalidate the row rectangle then... */
                        InvalidateRect(hWnd, &rectDestin, FALSE);
                        /* ...force default processing */
                        CallWindowProc(continfo->cinfo.pOldProc, hWnd, msg, 0, 0);
                    }
                }
            }
       }
       break;
   case WM_LBUTTONDBLCLK:
   case WM_CHAR:
      {
         LV_ITEM lvi;
         int iItem;
         void **params = NULL;

         if(LOWORD(mp1) == '\t')
         {
            if(GetAsyncKeyState(VK_SHIFT) & 0x8000)
               _dw_shift_focus(hWnd, _DW_DIRECTION_BACKWARD);
            else
               _dw_shift_focus(hWnd, _DW_DIRECTION_FORWARD);
            return FALSE;
         }

         if(msg == WM_CHAR && (char)mp1 != '\r')
            break;

         iItem = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED);

         memset(&lvi, 0, sizeof(LV_ITEM));

         if(iItem > -1)
         {
            lvi.iItem = iItem;
            lvi.mask = LVIF_PARAM;

            ListView_GetItem(hWnd, &lvi);
            params = (void **)lvi.lParam;
         }

         {
            DWSignalHandler *tmp = Root;

            while(tmp)
            {
               if(tmp->message == NM_DBLCLK && tmp->window == hWnd)
               {
                  int (DWSIGNAL *containerselectfunc)(HWND, char *, void *, void *) = tmp->signalfunction;

                  containerselectfunc(tmp->window, params ? params[_DW_DATA_TYPE_STRING] : NULL, tmp->data, params ? params[_DW_DATA_TYPE_POINTER] : NULL);
                  tmp = NULL;
               }
               if(tmp)
                  tmp = tmp->next;
            }
         }
      }
      break;
   case WM_CONTEXTMENU:
      {
         DWSignalHandler *tmp = Root;
         void **params = NULL;
         while(tmp)
         {
            if(tmp->message == NM_RCLICK && tmp->window == hWnd)
            {
               int (DWSIGNAL *containercontextfunc)(HWND, char *, int, int, void *, void *) = tmp->signalfunction;
               LONG x,y;
               LV_ITEM lvi;
               int iItem;
               LVHITTESTINFO lhi;
               TCHAR textbuf[1025] = {0};

               dw_pointer_query_pos(&x, &y);

               lhi.pt.x = x;
               lhi.pt.y = y;

               MapWindowPoints(HWND_DESKTOP, tmp->window, &lhi.pt, 1);

               iItem = ListView_HitTest(tmp->window, &lhi);

               memset(&lvi, 0, sizeof(LV_ITEM));

               if(iItem > -1)
               {
                  lvi.iItem = iItem;
                  lvi.pszText = textbuf;
                  lvi.cchTextMax = 1024;
                  lvi.mask = LVIF_PARAM | LVIF_TEXT;

                  ListView_GetItem(tmp->window, &lvi);
                  ListView_SetSelectionMark(tmp->window, iItem);
                  params = (void **)lvi.lParam;
               }

               containercontextfunc(tmp->window, params ? params[_DW_DATA_TYPE_STRING] : NULL, x, y, tmp->data, params ? params[_DW_DATA_TYPE_POINTER] : NULL);
               tmp = NULL;
            }
            if(tmp)
               tmp = tmp->next;
         }
      }
      break;
   }

   if(!continfo || !continfo->cinfo.pOldProc)
      return DefWindowProc(hWnd, msg, mp1, mp2);
   return CallWindowProc(continfo->cinfo.pOldProc, hWnd, msg, mp1, mp2);
}

LRESULT CALLBACK _dw_simplewndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
   ContainerInfo *cinfo;
   LRESULT ret = -1;

   cinfo = (ContainerInfo *)GetWindowLongPtr(hWnd, GWLP_USERDATA);

   switch( msg )
   {
   case WM_MOUSEMOVE:
      ret = _dw_wndproc(hWnd, msg, mp1, mp2);
      break;
   case WM_CHAR:
      ret = _dw_wndproc(hWnd, msg, mp1, mp2);
      if(ret != TRUE && LOWORD(mp1) == '\t')
      {
         if(GetAsyncKeyState(VK_SHIFT) & 0x8000)
            _dw_shift_focus(hWnd, _DW_DIRECTION_BACKWARD);
         else
            _dw_shift_focus(hWnd, _DW_DIRECTION_FORWARD);
         return FALSE;
      }
      break;
   }

   if(ret != TRUE)
   {
       if(!cinfo || !cinfo->cinfo.pOldProc)
          return DefWindowProc(hWnd, msg, mp1, mp2);
       return CallWindowProc(cinfo->cinfo.pOldProc, hWnd, msg, mp1, mp2);
   }
   return ret;
}

#ifdef RICHEDIT
#define ENTRY_CUT    60901
#define ENTRY_COPY   60902
#define ENTRY_PASTE  60903
#define ENTRY_DELETE 60904
#define ENTRY_UNDO   60905
#define ENTRY_SALL   60906


/* Special WM_NOTIFY handler for Rich Edit controls, to implement the context menu. */
LRESULT CALLBACK _dw_richeditwndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
    switch(msg)
    {    
        case WM_CONTEXTMENU:
        {
            HMENUI hwndMenu = dw_menu_new(0L);
            long x, y;
            unsigned long style = 0L;

            /* When readonly, disable: Undo, Cut, Paste, Delete */
            if(GetWindowLongPtr(hWnd, GWL_STYLE) & ES_READONLY)
                style = DW_MIS_DISABLED;

            dw_menu_append_item(hwndMenu, "Undo", ENTRY_UNDO, style, TRUE, -1, 0L);
            dw_menu_append_item(hwndMenu, "", 0L, 0L, TRUE, -1, 0L);
            dw_menu_append_item(hwndMenu, "Cut", ENTRY_CUT, style, TRUE, -1, 0L);
            dw_menu_append_item(hwndMenu, "Copy", ENTRY_COPY, 0L, TRUE, -1, 0L);
            dw_menu_append_item(hwndMenu, "Paste", ENTRY_PASTE, style, TRUE, -1, 0L);
            dw_menu_append_item(hwndMenu, "Delete", ENTRY_DELETE, style, TRUE, -1, 0L);
            dw_menu_append_item(hwndMenu, "", 0L, 0L, TRUE, -1, 0L);
            dw_menu_append_item(hwndMenu, "Select All", ENTRY_SALL, 0L, TRUE, -1, 0L);

            dw_pointer_query_pos(&x, &y);
            dw_menu_popup(&hwndMenu, hWnd, x, y);
            return TRUE;
        }
        break;
        case WM_COMMAND:
        {
            if(HIWORD(mp1) == 0)
            {
                switch(LOWORD(mp1))
                {
                    case ENTRY_CUT:
                    SendMessage(hWnd, WM_CUT, 0, 0);
                    break;
                    case ENTRY_COPY:
                    SendMessage(hWnd, WM_COPY, 0, 0);
                    break;
                    case ENTRY_PASTE:
                    SendMessage(hWnd, WM_PASTE, 0, 0);
                    break;
                    case ENTRY_DELETE:
                    SendMessage(hWnd, WM_CLEAR, 0, 0);
                    break;
                    case ENTRY_UNDO:
                    SendMessage(hWnd, EM_UNDO, 0, 0);
                    break;
                    case ENTRY_SALL:
                    SendMessage(hWnd, EM_SETSEL, 0, (LPARAM)-1);
                    break;
                }
            }
        }
        break;
    }
    return _dw_simplewndproc(hWnd, msg, mp1, mp2);
}
#endif


LRESULT CALLBACK _dw_treewndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
#ifdef AEROGLASS
   if(msg == WM_THEMECHANGED)
   {
      if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_SUPPORTED)
      {
         ContainerInfo *continfo = (ContainerInfo *)GetWindowLongPtr(hWnd, GWLP_USERDATA);

         if(!continfo || continfo->cinfo.back == -1 || continfo->cinfo.back == DW_CLR_DEFAULT)
            TreeView_SetBkColor(hWnd, _dw_get_syscolor(COLOR_WINDOW));
         if(!continfo || continfo->cinfo.fore == -1 || continfo->cinfo.fore == DW_CLR_DEFAULT)
            TreeView_SetTextColor(hWnd, _dw_get_syscolor(COLOR_WINDOWTEXT));
      }
   }
#endif
   return _dw_simplewndproc(hWnd, msg, mp1, mp2);
}

void _dw_change_box(Box *thisbox, int percent, int type)
{
   int z;

   for(z=0;z<thisbox->count;z++)
   {
      if(thisbox->items[z].type == _DW_TYPE_BOX)
      {
         Box *tmp = (Box*)GetWindowLongPtr(thisbox->items[z].hwnd, GWLP_USERDATA);
         _dw_change_box(tmp, percent, type);
      }
      else
      {
         if(type == DW_HORZ)
         {
            if(thisbox->items[z].hsize == _DW_SIZE_EXPAND)
               thisbox->items[z].width = (int)(((float)thisbox->items[z].origwidth) * (((float)percent)/((float)100.0)));
         }
         else
         {
            if(thisbox->items[z].vsize == _DW_SIZE_EXPAND)
               thisbox->items[z].height = (int)(((float)thisbox->items[z].origheight) * (((float)percent)/((float)100.0)));
         }
      }
   }
}

void _dw_handle_splitbar_resize(HWND hwnd, float percent, int type, int x, int y)
{
   HWND handle1 = (HWND)dw_window_get_data(hwnd, "_dw_topleft");
   HWND handle2 = (HWND)dw_window_get_data(hwnd, "_dw_bottomright");

   ShowWindow(handle1, SW_HIDE);
   ShowWindow(handle2, SW_HIDE);

   if(type == DW_HORZ)
   {
      int newx = x;
      float ratio = (float)percent/(float)100.0;
      Box *tmp = (Box *)GetWindowLongPtr(handle1, GWLP_USERDATA);

      newx = (int)((float)newx * ratio) - (_DW_SPLITBAR_WIDTH/2);

      MoveWindow(handle1, 0, 0, newx, y, FALSE);
      _dw_do_resize(tmp, newx - 1, y - 1, 0, 0);

      tmp = (Box *)GetWindowLongPtr(handle2, GWLP_USERDATA);

      newx = x - newx - _DW_SPLITBAR_WIDTH;

      MoveWindow(handle2, x - newx, 0, newx, y, FALSE);
      _dw_do_resize(tmp, newx - 1, y - 1, 0, 0);

      dw_window_set_data(hwnd, "_dw_start", DW_INT_TO_POINTER(newx));
   }
   else
   {
      int newy = y;
      float ratio = (float)(100.0-percent)/(float)100.0;
      Box *tmp = (Box *)GetWindowLongPtr(handle2, GWLP_USERDATA);

      newy = (int)((float)newy * ratio) - (_DW_SPLITBAR_WIDTH/2);

      MoveWindow(handle2, 0, y - newy, x, newy, FALSE);
      _dw_do_resize(tmp, x - 1, newy - 1, 0, 0);

      tmp = (Box *)GetWindowLongPtr(handle1, GWLP_USERDATA);

      newy = y - newy - _DW_SPLITBAR_WIDTH;

      MoveWindow(handle1, 0, 0, x, newy, FALSE);
      _dw_do_resize(tmp, x - 1, newy - 1, 0, 0);

      dw_window_set_data(hwnd, "_dw_start", DW_INT_TO_POINTER(newy));
   }

   ShowWindow(handle1, SW_SHOW);
   ShowWindow(handle2, SW_SHOW);
}

/* This handles any activity on the scrollbox */
LRESULT CALLBACK _dw_scrollwndproc(HWND hwnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
   switch (msg)
   {
       case WM_HSCROLL:
       case WM_VSCROLL:
       {
            ColorInfo *cinfo = _dw_window_get_cinfo(hwnd);
            SCROLLINFO hsi, vsi, *si = &hsi;
            int bar = SB_HORZ;
            int which = LOWORD(mp1);

            /* Initialize the scroll info structs */
            vsi.cbSize = hsi.cbSize = sizeof(SCROLLINFO);
            vsi.fMask = hsi.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;

            /* Save the current scroll positions */
            GetScrollInfo(hwnd, SB_HORZ, &hsi);
            GetScrollInfo(hwnd, SB_VERT, &vsi);

            if(msg == WM_VSCROLL)
            {
                bar = SB_VERT;
                si = &vsi;
            }

            switch(which)
            {
            case SB_THUMBTRACK:
                si->nPos = HIWORD(mp1);
                break;
            /*case SB_PAGEDOWN:*/
            case SB_PAGELEFT:
                si->nPos = si->nPos - si->nPage;
                if(si->nPos < 0)
                    si->nPos = 0;
                break;
            /*case SB_PAGEUP:*/
            case SB_PAGERIGHT:
                si->nPos = si->nPos + si->nPage;
                if(si->nPos > (int)(si->nMax - si->nPage) + 1)
                    si->nPos = (si->nMax - si->nPage) + 1;
                break;
            /*case SB_LINEDOWN:*/
            case SB_LINELEFT:
                si->nPos = si->nPos - 1;
                if(si->nPos < si->nMin)
                    si->nPos = si->nMin;
                break;
            /*case SB_LINEUP:*/
            case SB_LINERIGHT:
                si->nPos = si->nPos + 1;
                if(si->nPos > (int)(si->nMax - si->nPage) + 1)
                    si->nPos = (si->nMax - si->nPage) + 1;
                break;
            }

            /* Position the scrolled box */
            vsi.fMask = hsi.fMask = SIF_POS | SIF_DISABLENOSCROLL;
            SetWindowPos(cinfo->combo, 0, -hsi.nPos, -vsi.nPos, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
            SetScrollInfo(hwnd, bar, si, TRUE);
       }
       break;
   }
   return DefWindowProc(hwnd, msg, mp1, mp2);
}

/* This handles any activity on the splitbars (sizers) */
LRESULT CALLBACK _dw_splitwndproc(HWND hwnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
   switch (msg)
   {
   case WM_ACTIVATE:
   case WM_SETFOCUS:
      return FALSE;
#ifdef AEROGLASS
   case WM_THEMECHANGED:
      if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_SUPPORTED)
         InvalidateRect(hwnd, NULL, TRUE);
      break;
#endif
   case WM_PAINT:
      {
         PAINTSTRUCT ps;
         HDC hdcPaint;
         int type = DW_POINTER_TO_INT(dw_window_get_data(hwnd, "_dw_type"));
         int start = DW_POINTER_TO_INT(dw_window_get_data(hwnd, "_dw_start"));

         BeginPaint(hwnd, &ps);

         if((hdcPaint = GetDC(hwnd)) != NULL)
         {
            unsigned long cx, cy;
            HBRUSH oldBrush = SelectObject(hdcPaint, _dw_get_syscolor_brush(COLOR_3DFACE));
            HPEN oldPen = SelectObject(hdcPaint, CreatePen(PS_SOLID, 1, _dw_get_syscolor(COLOR_3DFACE)));

            dw_window_get_pos_size(hwnd, NULL, NULL, &cx, &cy);

            if(type == DW_HORZ)
               Rectangle(hdcPaint, cx - start - _DW_SPLITBAR_WIDTH, 0, cx - start, cy);
            else
               Rectangle(hdcPaint, 0, start, cx, start + _DW_SPLITBAR_WIDTH);

            SelectObject(hdcPaint, oldBrush);
            DeleteObject(SelectObject(hdcPaint, oldPen));
            ReleaseDC(hwnd, hdcPaint);
         }
         EndPaint(hwnd, &ps);
      }
      break;
   case WM_LBUTTONDOWN:
      {
         SetCapture(hwnd);
         break;
      }
   case WM_LBUTTONUP:
      {
            if(GetCapture() == hwnd)
            ReleaseCapture();
      }
      break;
   case WM_MOUSEMOVE:
      {
         float *percent = (float *)dw_window_get_data(hwnd, "_dw_percent");
         int type = DW_POINTER_TO_INT(dw_window_get_data(hwnd, "_dw_type"));
         int start;

         if(type == DW_HORZ)
            SetCursor(LoadCursor(NULL, IDC_SIZEWE));
         else
            SetCursor(LoadCursor(NULL, IDC_SIZENS));

         if(GetCapture() == hwnd && percent)
         {
            POINT point;
            RECT rect;
            static POINT lastpoint;

            GetCursorPos(&point);
            GetWindowRect(hwnd, &rect);

            if(memcmp(&point, &lastpoint, sizeof(POINT)))
            {
               if(PtInRect(&rect, point))
               {
                  int width = (rect.right - rect.left);
                  int height = (rect.bottom - rect.top);

                  if(type == DW_HORZ)
                  {
                     start = point.x - rect.left;
                     if(width - _DW_SPLITBAR_WIDTH > 1 && start < width - _DW_SPLITBAR_WIDTH)
                        *percent = ((float)start / (float)(width - _DW_SPLITBAR_WIDTH)) * (float)100.0;
                  }
                  else
                  {
                     start = point.y - rect.top;
                     if(height - _DW_SPLITBAR_WIDTH > 1 && start < height - _DW_SPLITBAR_WIDTH)
                        *percent = ((float)start / (float)(height - _DW_SPLITBAR_WIDTH)) * (float)100.0;
                  }
                  _dw_handle_splitbar_resize(hwnd, *percent, type, width, height);
               }
               memcpy(&lastpoint, &point, sizeof(POINT));
            }
         }
         break;
      }
   }
   return DefWindowProc(hwnd, msg, mp1, mp2);
}

/* This handles drawing the status text areas */
LRESULT CALLBACK _dw_statuswndproc(HWND hwnd, UINT msg, WPARAM mp1, LPARAM mp2)
{
   switch (msg)
   {
   case WM_MOUSEMOVE:
      _dw_wndproc(hwnd, msg, mp1, mp2);
      break;
   case WM_SETTEXT:
      {
         /* Make sure the control redraws when there is a text change */
         int ret = (int)DefWindowProc(hwnd, msg, mp1, mp2);

         InvalidateRgn(hwnd, NULL, TRUE);
         return ret;
      }
#ifdef AEROGLASS
   case WM_THEMECHANGED:
      if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_SUPPORTED)
         InvalidateRect(hwnd, NULL, TRUE);
      break;
#endif
   case WM_PAINT:
      {
         HDC hdcPaint;
         PAINTSTRUCT ps;
         RECT rc;
         unsigned long cx, cy;
         TCHAR tempbuf[1025] = { 0 };
         ColorInfo *cinfo = _dw_window_get_cinfo(hwnd);
         HFONT hfont = _dw_acquire_font(hwnd, cinfo ? cinfo->fontname : NULL);
         HFONT oldfont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);

         dw_window_get_pos_size(hwnd, NULL, NULL, &cx, &cy);
         GetWindowText(hwnd, tempbuf, 1024);

         hdcPaint = BeginPaint(hwnd, &ps);
         if(hfont)
            oldfont = (HFONT)SelectObject(hdcPaint, hfont);

         SetRect(&rc, 0, 0, cx, cy);

         /* If we are in full dark mode, or we have custom colors selected...
          * we will draw the status window ourselves... otherwise DrawStatusText()
          */
         if(
#ifdef AEROGLASS
            (_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_ENABLED) ||
#endif
            (cinfo && cinfo->fore != -1 && cinfo->fore != DW_CLR_DEFAULT &&
             cinfo->back !=- -1 && cinfo->back != DW_CLR_DEFAULT))
         {
            ULONG fore = (cinfo && (cinfo->fore != -1 && cinfo->fore != DW_CLR_DEFAULT)) ? _dw_internal_color(cinfo->fore) : DW_RGB_TRANSPARENT;
            ULONG back = (cinfo && (cinfo->back != -1 && cinfo->back != DW_CLR_DEFAULT)) ? _dw_internal_color(cinfo->back) : DW_RGB_TRANSPARENT;
            HBRUSH hbrback, hbrfore;
            hbrfore = fore != DW_RGB_TRANSPARENT ? CreateSolidBrush(RGB(DW_RED_VALUE(fore), DW_GREEN_VALUE(fore), DW_BLUE_VALUE(fore))) : NULL;
            hbrback = back != DW_RGB_TRANSPARENT ? CreateSolidBrush(RGB(DW_RED_VALUE(back), DW_GREEN_VALUE(back), DW_BLUE_VALUE(back))) : NULL;
            /* Fill with the background color */
            FillRect(hdcPaint, &rc, hbrback ? hbrback : _dw_get_syscolor_brush(COLOR_3DFACE));
            /* Dwaw a border around it */
            FrameRect(hdcPaint, &rc, hbrfore ? hbrfore : _dw_get_syscolor_brush(COLOR_WINDOWTEXT));
            SetRect(&rc, 3, 1, cx -1, cy - 1);
            SetTextColor(hdcPaint, fore != DW_RGB_TRANSPARENT ? RGB(DW_RED_VALUE(fore), DW_GREEN_VALUE(fore), DW_BLUE_VALUE(fore)) : _dw_get_syscolor(COLOR_WINDOWTEXT));
            SetBkMode(hdcPaint, TRANSPARENT);
            /* Draw the text in the middle */
            ExtTextOut(hdcPaint, 3, 1, ETO_CLIPPED, &rc, tempbuf, (UINT)_tcslen(tempbuf), NULL);
            if(hbrfore)
               DeleteObject(hbrfore);
            if(hbrback)
               DeleteObject(hbrback);
         }
         else
            DrawStatusText(hdcPaint, &rc, tempbuf, 0);
         if(hfont && oldfont)
            SelectObject(hdcPaint, oldfont);
         if(hfont)
            DeleteObject(hfont);
         EndPaint(hwnd, &ps);
      }
      return FALSE;
   }
   return DefWindowProc(hwnd, msg, mp1, mp2);
}

#ifdef AEROGLASS
/* Window procedure to handle drawing themed text when in composited mode */
LRESULT CALLBACK _dw_staticwndproc(HWND hwnd, ULONG msg, WPARAM mp1, LPARAM mp2)
{
   ColorInfo *parentcinfo, *tlcinfo, *cinfo = _dw_window_get_cinfo(hwnd);
   WNDPROC pOldProc;

   if (!cinfo)
      return DefWindowProc(hwnd, msg, mp1, mp2);

   /* Need the parent to do the check completely */
   parentcinfo = _dw_window_get_cinfo(GetParent(hwnd));
   tlcinfo = _dw_window_get_cinfo(_dw_toplevel_window(hwnd));

   /* If we don't require themed drawing */
   if(((cinfo->back != -1 && cinfo->back != DW_RGB_TRANSPARENT) || (parentcinfo && parentcinfo->back != -1))
       || !_dw_composition || !tlcinfo || !(tlcinfo->style & DW_FCF_COMPOSITED) || IS_WIN8PLUS)
      return _dw_colorwndproc(hwnd, msg, mp1, mp2);

   pOldProc = cinfo->pOldProc;

   switch(msg)
   {
      case WM_PAINT:
      {
         PAINTSTRUCT ps;
         HDC hdc = BeginPaint(hwnd, &ps);

         if(hdc)
         {
            /* Figure out how to draw */
            HDC hdcPaint = NULL;
            RECT rcClient;
            LONG_PTR dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
            LONG_PTR dwStyleEx = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
            HTHEME hTheme = _OpenThemeData(NULL, L"ControlPanelStyle");

            GetClientRect(hwnd, &rcClient);

            if(hTheme)
            {
               /* Create an in memory image to draw to */
               HPAINTBUFFER hBufferedPaint = _BeginBufferedPaint(hdc, &rcClient, BPBF_TOPDOWNDIB, NULL, &hdcPaint);

               if(hdcPaint)
               {
                  LONG_PTR dwStaticStyle = dwStyle & 0x1F;
                  HFONT hFontOld = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);
                  int iLen = GetWindowTextLength(hwnd), fore = _dw_internal_color(cinfo->fore);
                  DTTOPTS DttOpts = { sizeof(DTTOPTS) };
                  static HBRUSH hbrush = 0;

                  /* Make sure we have a transparency brush */
                  if(!hbrush)
                     hbrush = CreateSolidBrush(_dw_transparencykey);

                  /* Fill the background with the transparency color */
                  FillRect(hdcPaint, &rcClient, hbrush);
                  _BufferedPaintSetAlpha(hBufferedPaint, &ps.rcPaint, 0x00);

                  /* Setup how we will draw the text */
                  DttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE | DTT_TEXTCOLOR;
                  DttOpts.crText = cinfo->fore == -1 ? RGB(255, 255, 255) : RGB(DW_RED_VALUE(fore), DW_GREEN_VALUE(fore), DW_BLUE_VALUE(fore));
                  DttOpts.iGlowSize = 12;

                  SetBkMode(hdcPaint, TRANSPARENT);

                  if(hFontOld)
                     hFontOld = (HFONT)SelectObject(hdcPaint, hFontOld);

                  /* Make sure there is text to draw */
                  if(iLen)
                  {
                     LPWSTR szText = (LPWSTR)_alloca(sizeof(WCHAR)*(iLen+5));

                     if(szText)
                     {
                        iLen = GetWindowTextW(hwnd, szText, iLen+5);
                        if(iLen)
                        {
                           DWORD dwFlags = DT_WORDBREAK;

                           switch (dwStaticStyle)
                           {
                              case SS_CENTER:
                                 dwFlags |= DT_CENTER;
                                 break;
                              case SS_RIGHT:
                                 dwFlags |= DT_RIGHT;
                                 break;
                              case SS_LEFTNOWORDWRAP:
                                 dwFlags &= ~DT_WORDBREAK;
                                 break;
                           }

                           if(dwStyle & SS_CENTERIMAGE)
                           {
                              dwFlags |= DT_VCENTER;
                              dwFlags &= ~DT_WORDBREAK;
                           }


                           if(dwStyle & SS_ENDELLIPSIS)
                              dwFlags |= DT_END_ELLIPSIS|DT_MODIFYSTRING;
                           else if(dwStyle & SS_PATHELLIPSIS)
                              dwFlags |= DT_PATH_ELLIPSIS|DT_MODIFYSTRING;
                           else if(dwStyle & SS_WORDELLIPSIS)
                              dwFlags |= DT_WORD_ELLIPSIS|DT_MODIFYSTRING;

                           if (dwStyleEx&WS_EX_RIGHT)
                              dwFlags |= DT_RIGHT;

                           if(dwStyle & SS_NOPREFIX)
                              dwFlags |= DT_NOPREFIX;

                           /* Draw the text! */
                           _DrawThemeTextEx(hTheme, hdcPaint, 0, 0,
                                            szText, -1, dwFlags, &rcClient, &DttOpts);
                        }
                     }
                  }

                  /* Cleanup */
                  if (hFontOld)
                     SelectObject(hdcPaint, hFontOld);
                  _EndBufferedPaint(hBufferedPaint, TRUE);
               }
               _CloseThemeData(hTheme);
            }
         }

         EndPaint(hwnd, &ps);
         return TRUE;
      }
      break;
   }

   if ( !pOldProc )
      return DefWindowProc(hwnd, msg, mp1, mp2);
   return CallWindowProc(pOldProc, hwnd, msg, mp1, mp2);
}
#endif

/* Function: _dw_buttonwndproc
 * Abstract: Subclass procedure for buttons
 */

LRESULT CALLBACK _dw_buttonwndproc(HWND hwnd, ULONG msg, WPARAM mp1, LPARAM mp2)
{
   ColorInfo *cinfo = _dw_window_get_cinfo(hwnd);
   WNDPROC pOldProc;
   int retval = -1;

   if ( !cinfo )
      return DefWindowProc(hwnd, msg, mp1, mp2);

   /* We must save a pointer to the old
    * window procedure because if a signal
    * handler attached here destroys this
    * window it will then be invalid.
    */
   pOldProc = cinfo->pOldProc;

   switch(msg)
   {
   case WM_CTLCOLORSTATIC:
   case WM_CTLCOLORLISTBOX:
   case WM_CTLCOLORBTN:
   case WM_CTLCOLOREDIT:
   case WM_CTLCOLORMSGBOX:
   case WM_CTLCOLORSCROLLBAR:
   case WM_CTLCOLORDLG:
      return _dw_colorwndproc(hwnd, msg, mp1, mp2);
   case WM_SETFOCUS:
      _dw_wndproc(hwnd, msg, mp1, mp2);
      break;
   case WM_LBUTTONUP:
      {
         DWSignalHandler *tmp = Root;

         /* Find any callbacks for this function */
         while(tmp)
         {
            if(tmp->message == WM_COMMAND)
            {
               int (DWSIGNAL *clickfunc)(HWND, void *) = tmp->signalfunction;

               /* Make sure it's the right window, and the right ID */
               if(tmp->window == hwnd)
               {
                  int checkbox = DW_POINTER_TO_INT(dw_window_get_data(hwnd, "_dw_checkbox"));

                  if(checkbox)
                     in_checkbox_handler = 1;

                  retval = clickfunc(tmp->window, tmp->data);

                  if(checkbox)
                     in_checkbox_handler = 0;
                  tmp = NULL;
               }
            }
            if(tmp)
               tmp= tmp->next;
         }
      }
      break;
   case WM_CHAR:
      {
         /* A button press should also occur for an ENTER or SPACE press
          * while the button has the active input focus.
          */
         if(LOWORD(mp1) == '\r' || LOWORD(mp1) == ' ')
         {
            DWSignalHandler *tmp = Root;

            /* Find any callbacks for this function */
            while(tmp)
            {
               if(tmp->message == WM_COMMAND)
               {
                  int (DWSIGNAL *clickfunc)(HWND, void *) = tmp->signalfunction;

                  /* Make sure it's the right window, and the right ID */
                  if(tmp->window == hwnd)
                  {
                     retval = clickfunc(tmp->window, tmp->data);
                     tmp = NULL;
                  }
               }
               if(tmp)
                  tmp= tmp->next;
            }
         }
         if(LOWORD(mp1) == '\t')
         {
            if(GetAsyncKeyState(VK_SHIFT) & 0x8000)
               _dw_shift_focus(hwnd, _DW_DIRECTION_BACKWARD);
            else
               _dw_shift_focus(hwnd, _DW_DIRECTION_FORWARD);
            return FALSE;
         }
      }
      break;
   case WM_KEYDOWN:
      if(mp1 == VK_LEFT || mp1 == VK_UP)
         _dw_shift_focus(hwnd, _DW_DIRECTION_BACKWARD);
      if(mp1 == VK_RIGHT || mp1 == VK_DOWN)
         _dw_shift_focus(hwnd, _DW_DIRECTION_FORWARD);
      break;
   }

   /* Make sure windows are up-to-date */
   if(retval != -1)
      _dw_redraw(0, FALSE);
   if ( !pOldProc )
      return DefWindowProc(hwnd, msg, mp1, mp2);
   return CallWindowProc(pOldProc, hwnd, msg, mp1, mp2);
}

/* This function recalculates a notebook page for example
 * during switching of notebook pages.
 */
void _dw_resize_notebook_page(HWND handle, int pageid)
{
   RECT rect;
   NotebookPage **array = (NotebookPage **)dw_window_get_data(handle, "_dw_array");

   if(array && array[pageid])
   {
      Box *box = (Box *)GetWindowLongPtr(array[pageid]->hwnd, GWLP_USERDATA);

      GetClientRect(handle,&rect);
      TabCtrl_AdjustRect(handle,FALSE,&rect);
      MoveWindow(array[pageid]->hwnd, rect.left, rect.top,
               rect.right - rect.left, rect.bottom-rect.top, TRUE);
      if(box && box->count)
      {
         ShowWindow(box->items[0].hwnd, SW_HIDE);
         _dw_do_resize(box, rect.right - rect.left, rect.bottom - rect.top, 0, 0);
         ShowWindow(box->items[0].hwnd, SW_SHOW);
      }

      ShowWindow(array[pageid]->hwnd, SW_SHOWNORMAL);
   }
}

void _dw_create_tooltip(HWND handle, const char *text)
{
    TOOLINFO ti = { 0 };

    ti.cbSize = sizeof(TOOLINFO);
    ti.hwnd = handle;
    ti.hinst = _DWInstance;

    SendMessage(_dw_tooltip, TTM_DELTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
    if(text)
    {
        /* Set up "tool" information.
         * In this case, the "tool" is the entire parent window.
         */
        ti.uFlags = TTF_SUBCLASS;
        ti.lpszText = UTF8toWide(text);
        ti.rect.right = ti.rect.bottom = 2000;

        /* Associate the tooltip with the "tool" window. */
        SendMessage(_dw_tooltip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
    }
}

#ifndef GDIPLUS
/* This function determines the handle for a supplied image filename
 */
int _dw_get_image_handle(char *filename, HANDLE *icon, HBITMAP *hbitmap)
{
   int len, windowtype = 0;
   char *file = malloc(strlen(filename) + 5);

   *hbitmap = 0;
   *icon = 0;

   if (!file)
   {
      return 0;
   }

   strcpy(file, filename);

   /* check if we can read from this file (it exists and read permission) */
   if (access(file, 04) == 0)
   {
      len = strlen( file );
      if ( len < 4 )
      {
         free(file);
         return 0;
      }
      if ( stricmp( file + len - 4, ".ico" ) == 0 )
      {
         *icon = LoadImage(NULL, UTF8toWide(file), IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
         windowtype = BS_ICON;
      }
      else if ( stricmp( file + len - 4, ".bmp" ) == 0 )
      {
         *hbitmap = (HBITMAP)LoadImage(NULL, UTF8toWide(file), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
         windowtype = BS_BITMAP;
      }
      else if ( stricmp( file + len - 4, ".png" ) == 0 )
      {
         *hbitmap = (HBITMAP)LoadImage(NULL, UTF8toWide(file), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
         windowtype = BS_BITMAP;
      }
      free(file);
   }
   else
   {
      /* Try with .ico extension first...*/
      strcat(file, ".ico");
      if (access(file, 04) == 0)
      {
         *icon = LoadImage(NULL, UTF8toWide(file), IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
         windowtype = BS_ICON;
      }
      else
      {
         strcpy(file, filename);
         strcat(file, ".bmp");
         if (access(file, 04) == 0)
         {
            *hbitmap = (HBITMAP)LoadImage(NULL, UTF8toWide(file), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
            windowtype = BS_BITMAP;
         }
      }
      free(file);
   }
   return windowtype;
}
#endif

/*
 * Initializes the Dynamic Windows engine.
 * Parameters:
 *           newthread: True if this is the only thread.
 *                      False if there is already a message loop running.
 */
int API dw_init(int newthread, int argc, char *argv[])
{
   WNDCLASS wc;
   int z;
   INITCOMMONCONTROLSEX icc;
   char *alttmpdir;
#ifdef GDIPLUS
   struct GdiplusStartupInput si;
#endif

   /* Setup the private data directory */
   if(argc > 0 && argv[0])
   {
      char *pos = strrchr(argv[0], '\\');

      /* Just to be safe try the unix style */
      if(!pos)
         pos = strrchr(argv[0], '/');

      if(pos)
      {
         strncpy(_dw_exec_dir, argv[0], (size_t)(pos - argv[0]));
         if((pos++) && !_dw_app_id[0])
         {
            /* If we have a binary name, use that for the Application ID instead. */
            _snprintf(_dw_app_id, _DW_APP_ID_SIZE, "%s.%s", DW_APP_DOMAIN_DEFAULT, pos);
            strncpy(_dw_app_name, pos, _DW_APP_ID_SIZE);
         }
      }
   }
   /* If that failed... just get the current directory */
   if(!_dw_exec_dir[0])
      GetCurrentDirectoryA(MAX_PATH, _dw_exec_dir);

   /* Initialize our thread local storage */
   _dw_foreground = TlsAlloc();
   _dw_background = TlsAlloc();
   _dw_hpen = TlsAlloc();
   _dw_hbrush = TlsAlloc();
#ifdef GDIPLUS
   _dw_gppen = TlsAlloc();
   _dw_gpbrush = TlsAlloc();
#endif

   icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
   icc.dwICC = ICC_WIN95_CLASSES|ICC_DATE_CLASSES;

   InitCommonControlsEx(&icc);

   memset(_dw_icon_lookup, 0, sizeof(HICON) * ICON_INDEX_LIMIT);

   /* We need the version to check capability like up-down controls */
   _dwVersion = GetVersion();
   _dwComctlVer = _dw_get_dll_version(TEXT("comctl32.dll"));

   /* We need to initialize dark mode, and thus the aero/theme subsystems before registering our window classes */
   if((huxtheme = LoadLibrary(TEXT("uxtheme"))))
      _SetWindowTheme = (HRESULT (WINAPI *)(HWND, LPCWSTR, LPCWSTR ))GetProcAddress(huxtheme, "SetWindowTheme");
#ifdef AEROGLASS
   /* Attempt to load the Desktop Window Manager and Theme library */
   if(huxtheme && (hdwm = LoadLibrary(TEXT("dwmapi"))))
   {
      _DwmExtendFrameIntoClientArea = (HRESULT (WINAPI *)(HWND, const MARGINS *))GetProcAddress(hdwm, "DwmExtendFrameIntoClientArea");
      _DwmSetWindowAttribute = (HRESULT (WINAPI *)(HWND, DWORD, LPCVOID, DWORD))GetProcAddress(hdwm, "DwmSetWindowAttribute");
      _DwmDefWindowProc = (BOOL (WINAPI *)(HWND, UINT, WPARAM, LPARAM, LRESULT *))GetProcAddress(hdwm, "DwmDefWindowProc");
      if((_DwmIsCompositionEnabled = (HRESULT (WINAPI *)(BOOL *))GetProcAddress(hdwm, "DwmIsCompositionEnabled")))
         _DwmIsCompositionEnabled(&_dw_composition);
      _OpenThemeData = (HTHEME (WINAPI *)(HWND, LPCWSTR))GetProcAddress(huxtheme, "OpenThemeData");
      _GetThemeSysFont = (HRESULT (WINAPI *)(HTHEME, int, LOGFONTW *))GetProcAddress(huxtheme, "GetThemeSysFont");
      _GetImmersiveColorFromColorSetEx = (DWORD (WINAPI *)(UINT, UINT, BOOL, UINT))GetProcAddress(huxtheme, MAKEINTRESOURCEA(95));
      _GetImmersiveColorTypeFromName = (int (WINAPI *)(const WCHAR *))GetProcAddress(huxtheme, MAKEINTRESOURCEA(96));
      _GetImmersiveUserColorSetPreference = (int (WINAPI *)(BOOL, BOOL))GetProcAddress(huxtheme, MAKEINTRESOURCEA(98));
      _BeginBufferedPaint = (HPAINTBUFFER (WINAPI *)(HDC, const RECT *, BP_BUFFERFORMAT, BP_PAINTPARAMS *, HDC *))GetProcAddress(huxtheme, "BeginBufferedPaint");
      _BufferedPaintSetAlpha = (HRESULT (WINAPI *)(HPAINTBUFFER, const RECT *, BYTE))GetProcAddress(huxtheme, "BufferedPaintSetAlpha");
      _DrawThemeTextEx = (HRESULT (WINAPI *)(HTHEME, HDC, int, int, LPCWSTR, int, DWORD, LPRECT, const DTTOPTS *))GetProcAddress(huxtheme, "DrawThemeTextEx");
      _EndBufferedPaint = (HRESULT (WINAPI *)(HPAINTBUFFER, BOOL))GetProcAddress(huxtheme, "EndBufferedPaint");
      _CloseThemeData = (HRESULT (WINAPI *)(HTHEME))GetProcAddress(huxtheme, "CloseThemeData");
      _dw_init_dark_mode();
   }
   /* In case of error close the library if needed */
   else if(hdwm)
   {
      FreeLibrary(hdwm);
      hdwm = 0;
   }
#endif

   /* Register the generic Dynamic Windows class */
   memset(&wc, 0, sizeof(WNDCLASS));
   wc.style = CS_DBLCLKS;
   wc.lpfnWndProc = (WNDPROC)_dw_wndproc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 32;
   wc.hbrBackground = NULL;
   wc.lpszMenuName = NULL;
   wc.lpszClassName = ClassName;

   RegisterClass(&wc);

   /* Register the splitbar control */
   memset(&wc, 0, sizeof(WNDCLASS));
   wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = (WNDPROC)_dw_splitwndproc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hbrBackground = NULL;
   wc.lpszMenuName = NULL;
   wc.lpszClassName = SplitbarClassName;

   RegisterClass(&wc);

   /* Register the scroller control */
   memset(&wc, 0, sizeof(WNDCLASS));
   wc.style = CS_DBLCLKS;
   wc.lpfnWndProc = (WNDPROC)_dw_scrollwndproc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 32;
   wc.hbrBackground = NULL;
   wc.lpszMenuName = NULL;
   wc.lpszClassName = ScrollClassName;

   RegisterClass(&wc);

   /* Register a frame control like on OS/2 */
   memset(&wc, 0, sizeof(WNDCLASS));
   wc.style = CS_DBLCLKS;
   wc.lpfnWndProc = (WNDPROC)_dw_framewndproc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 32;
   wc.hbrBackground = (HBRUSH)_dw_get_syscolor_brush(COLOR_3DFACE);
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.lpszMenuName = NULL;
   wc.lpszClassName = FRAMECLASSNAME;

   RegisterClass(&wc);

   /* Register a status bar control */
   memset(&wc, 0, sizeof(WNDCLASS));
   wc.style = CS_DBLCLKS;
   wc.lpfnWndProc = (WNDPROC)_dw_statuswndproc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 32;
   wc.hbrBackground = NULL;
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.lpszMenuName = NULL;
   wc.lpszClassName = StatusbarClassName;

   RegisterClass(&wc);

   /* Create a set of brushes using the default OS/2 and DOS colors */
   for(z=0;z<18;z++)
      _dw_colors[z] = CreateSolidBrush(RGB(_dw_red[z],_dw_green[z],_dw_blue[z]));

   /* Register an Object Windows class like OS/2 and Win2k+
    * so similar functionality can be used on earlier releases
    * of Windows.
    */
   memset(&wc, 0, sizeof(WNDCLASS));
   wc.style = 0;
   wc.lpfnWndProc = (WNDPROC)_dw_wndproc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hbrBackground = NULL;
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.lpszMenuName = NULL;
   wc.lpszClassName = ObjectClassName;

   RegisterClass(&wc);

   /* Since Windows 95/98/NT don't have a HWND_OBJECT class
    * also known as a input only window, I will create a
    * temporary window that isn't visible and really does nothing
    * except temporarily hold the child windows before they are
    * packed into their correct parent.
    */

   DW_HWND_OBJECT = CreateWindow(ObjectClassName, TEXT("HWND_OBJECT"), 0, 0, 0,
                          0, 0, HWND_DESKTOP, NULL, _DWInstance, NULL);

   if(!DW_HWND_OBJECT)
   {
      dw_messagebox("Dynamic Windows", DW_MB_OK|DW_MB_ERROR, "Could not initialize the object window. error code %d", GetLastError());
      exit(1);
   }

   if(!_dw_app_id[0])
   {
      /* Generate an Application ID based on the PID if all else fails. */
      _snprintf(_dw_app_id, _DW_APP_ID_SIZE, "%s.pid.%d", DW_APP_DOMAIN_DEFAULT, _getpid());
   }
   if(!_dw_app_name[0])
   {
      /* If we still don't have an app name, get the executable name */
      char fullpath[MAX_PATH+1] = {0}, *pos;
      GetModuleFileNameA(_DWInstance, fullpath, MAX_PATH);
      pos = strrchr(fullpath, '\\');
      if(pos)
         pos++;
      strncpy(_dw_app_name, pos ? pos : fullpath, _DW_APP_ID_SIZE);
   }
    
#if (defined(BUILD_DLL) || defined(BUILD_HTML))
   /* Register HTML renderer class */
   memset(&wc, 0, sizeof(WNDCLASS));
   wc.lpszClassName = BrowserClassName;
   wc.style = CS_HREDRAW | CS_VREDRAW;
#ifdef BUILD_EDGE
   /* Check if Microsoft Edge (Chromium) is installed */
   if ((_DW_EDGE_DETECTED = _dw_edge_detect(UTF8toWide(_dw_app_id))))
   {
      wc.lpfnWndProc = (WNDPROC)_dw_edgewndproc;
      wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
   }
   else
#endif
   {
      wc.lpfnWndProc = (WNDPROC)_dw_browserwndproc;
   }
   RegisterClass(&wc);
#endif

   /* Create a tooltip. */
   _dw_tooltip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
                  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, DW_HWND_OBJECT, NULL, _DWInstance, NULL);

   SetWindowPos(_dw_tooltip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

   /* Create empty box data */
   SetWindowLongPtr(DW_HWND_OBJECT, GWLP_USERDATA, (LONG_PTR)calloc(sizeof(Box), 1));

   /* Initialize Security for named events and memory */
   InitializeSecurityDescriptor(&_dwsd, SECURITY_DESCRIPTOR_REVISION);
   SetSecurityDescriptorDacl(&_dwsd, TRUE, (PACL) NULL, FALSE);

   OleInitialize(NULL);

   /*
    * Get an alternate temporary directory in case TMP doesn't exist
    */
   if ( (alttmpdir = getenv( "TEMP" ) ) == NULL )
   {
      strcpy( _dw_alternate_temp_dir, "c:\\tmp" );
   }
   else
   {
      strncpy( _dw_alternate_temp_dir, alttmpdir, MAX_PATH );
   }

#ifdef GDIPLUS
   /* Setup GDI+ */
   si.GdiplusVersion = 1;
   si.DebugEventCallback = NULL;
   si.SuppressBackgroundThread = FALSE;
   si.SuppressExternalCodecs = FALSE;
   GdiplusStartup(&gdiplusToken, &si, NULL);
#endif

   /* GDI+ Needs to be initialized before calling _dw_init_thread(); */
   _dw_init_thread();

#ifdef RICHEDIT
   /* Attempt to load rich edit library: 4.1, 3/2.0 and 1.0 */
   if(!(hmsftedit = LoadLibrary(TEXT("msftedit"))))
   {
      if(!(hrichedit = LoadLibrary(TEXT("riched20"))))
         hrichedit = LoadLibrary(TEXT("riched32"));
   }
   /* Enable Rich Edit by default if supported */
   if(_DW_MLE_RICH_EDIT == DW_FEATURE_UNSUPPORTED && (hmsftedit || hrichedit))
      _DW_MLE_RICH_EDIT = DW_FEATURE_ENABLED;
#endif
#ifdef BUILD_TOAST
   _dw_toast_init(UTF8toWide(_dw_app_name), UTF8toWide(_dw_app_id));
#endif
   return 0;
}

static int _dw_main_running = FALSE;

/*
 * Runs a message loop for Dynamic Windows.
 */
void API dw_main(void)
{
   MSG msg;

   _dwtid = dw_thread_id();
   /* Make sure any queued redraws are handled */
   _dw_redraw(0, FALSE);

   /* Set the running flag to TRUE */
   _dw_main_running = TRUE;

   /* Run the loop until the flag is unset... or error */
   while(_dw_main_running && GetMessage(&msg, NULL, 0, 0))
   {
      if(msg.hwnd == NULL && msg.message == WM_TIMER)
         _dw_wndproc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
      else
      {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
   }
}

/*
 * Causes running dw_main() to return.
 */
void API dw_main_quit(void)
{
    _dw_main_running = FALSE;
}

/*
 * Runs a message loop for Dynamic Windows, for a period of milliseconds.
 * Parameters:
 *           milliseconds: Number of milliseconds to run the loop for.
 */
void API dw_main_sleep(int milliseconds)
{
   MSG msg;
   double start = (double)clock();

   while(((clock() - start) / (CLOCKS_PER_SEC/1000)) <= milliseconds)
   {
      if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
      {
         GetMessage(&msg, NULL, 0, 0);
         if(msg.hwnd == NULL && msg.message == WM_TIMER)
            _dw_wndproc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
         else
         {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
         }
      }
      else
         Sleep(1);
   }
}

/*
 * Processes a single message iteration and returns.
 */
void API dw_main_iteration(void)
{
   MSG msg;

   _dwtid = dw_thread_id();

   if(GetMessage(&msg, NULL, 0, 0))
   {
      if(msg.hwnd == NULL && msg.message == WM_TIMER)
         _dw_wndproc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
      else
      {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
   }
}

/*
 * Free's memory allocated by dynamic windows.
 * Parameters:
 *           ptr: Pointer to dynamic windows allocated
 *                memory to be free()'d.
 */
void API dw_free(void *ptr)
{
   free(ptr);
}

/*
 * Allocates and initializes a dialog struct.
 * Parameters:
 *           data: User defined data to be passed to functions.
 */
DWDialog * API dw_dialog_new(void *data)
{
   DWDialog *tmp = malloc(sizeof(DWDialog));

   tmp->eve = dw_event_new();
   dw_event_reset(tmp->eve);
   tmp->data = data;
   tmp->done = FALSE;
   tmp->result = NULL;

    return tmp;
}

/*
 * Accepts a dialog struct and returns the given data to the
 * initial called of dw_dialog_wait().
 * Parameters:
 *           dialog: Pointer to a dialog struct aquired by dw_dialog_new).
 *           result: Data to be returned by dw_dialog_wait().
 */
int API dw_dialog_dismiss(DWDialog *dialog, void *result)
{
   dialog->result = result;
   dw_event_post(dialog->eve);
   dialog->done = TRUE;
   return 0;
}

/*
 * Accepts a dialog struct waits for dw_dialog_dismiss() to be
 * called by a signal handler with the given dialog struct.
 * Parameters:
 *           dialog: Pointer to a dialog struct aquired by dw_dialog_new).
 */
void * API dw_dialog_wait(DWDialog *dialog)
{
   MSG msg;
   void *tmp;

   if(!dialog)
      return NULL;

   while (GetMessage(&msg,NULL,0,0))
   {
      if(msg.hwnd == NULL && msg.message == WM_TIMER)
         _dw_wndproc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
      else
      {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
      if(dialog->done)
         break;
   }
   dw_event_close(&dialog->eve);
   tmp = dialog->result;
   free(dialog);
   return tmp;
}

/*
 * Displays a debug message on the console...
 * Parameters:
 *           format: printf style format string.
 *           ...: Additional variables for use in the format.
 */
void API dw_debug(const char *format, ...)
{
   va_list args;

   va_start(args, format);
   dw_vdebug(format, args);
   va_end(args);
}

void API dw_vdebug(const char *format, va_list args)
{
   char outbuf[1025] = {0}, *thisbuf = outbuf;

   vsnprintf(outbuf, 1024, format, args);
   OutputDebugString(UTF8toWide(thisbuf));
}

/*
 * Displays a Message Box with given text and title..
 * Parameters:
 *           title: The title of the message box.
 *           format: printf style format string.
 *           ...: Additional variables for use in the format.
 */
int API dw_messagebox(const char *title, int flags, const char *format, ...)
{
   va_list args;
   int rc;

   va_start(args, format);
   rc = dw_vmessagebox(title, flags, format, args);
   va_end(args);
   return rc;
}

int API dw_vmessagebox(const char *title, int flags, const char *format, va_list args)
{
   char outbuf[1025] = { 0 }, *thisbuf = outbuf;
   int rc;

   vsnprintf(outbuf, 1024, format, args);

   rc = MessageBox(HWND_DESKTOP, UTF8toWide(thisbuf), UTF8toWide(title), flags);
   if(rc == IDOK)
      return DW_MB_RETURN_OK;
   else if(rc == IDYES)
      return DW_MB_RETURN_YES;
   else if(rc == IDNO)
      return DW_MB_RETURN_NO;
   else if(rc == IDCANCEL)
      return DW_MB_RETURN_CANCEL;
   else return 0;
}

/*
 * Minimizes or Iconifies a top-level window.
 * Parameters:
 *           handle: The window handle to minimize.
 */
int API dw_window_minimize(HWND handle)
{
   return ShowWindow(handle, SW_MINIMIZE);
}

/*
 * Makes the window topmost.
 * Parameters:
 *           handle: The window handle to make topmost.
 */
int API dw_window_raise(HWND handle)
{
   return SetWindowPos(handle, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
}

/*
 * Makes the window bottommost.
 * Parameters:
 *           handle: The window handle to make bottommost.
 */
int API dw_window_lower(HWND handle)
{
   return SetWindowPos(handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
}

/*
 * Makes the window visible.
 * Parameters:
 *           handle: The window handle to make visible.
 */
int API dw_window_show(HWND handle)
{
   int rc;
   RECT rect;

#ifdef AEROGLASS
   if(_DW_DARK_MODE_SUPPORTED)
   {
      /* Try to enable dark mode support if our OS supports it */
      _dw_set_child_window_theme(handle, 0);
      EnumChildWindows(handle, _dw_set_child_window_theme, 0);
      if(GetParent(handle) == HWND_DESKTOP)
         _dw_refresh_titlebar_theme_color(handle);
   }
#endif

   GetClientRect(handle, &rect);

   /* If the client area is 0x0 then call the autosize routine */
   if((rect.bottom - rect.top) == 0 || (rect.right - rect.left) == 0)
      dw_window_set_size(handle, 0, 0);

   rc = ShowWindow(handle, SW_SHOW);
   SetFocus(handle);
   _dw_initial_focus(handle);
   return rc;
}

/*
 * Makes the window invisible.
 * Parameters:
 *           handle: The window handle to make visible.
 */
int API dw_window_hide(HWND handle)
{
   return ShowWindow(handle, SW_HIDE);
}

/*
 * Destroys a window and all of it's children.
 * Parameters:
 *           handle: The window handle to destroy.
 */
int API dw_window_destroy(HWND handle)
{
   HWND parent;
   HMENU menu;

   /* Handle special case for menu handle */
   if(handle < (HWND)65536)
   {
      char buffer[31] = {0};
      ULONG id = (ULONG)(uintptr_t)handle;

      _snprintf(buffer, 30, "_dw_id%ld", id);
      menu = (HMENU)dw_window_get_data(DW_HWND_OBJECT, buffer);

      if(menu && IsMenu(menu))
         return dw_menu_delete_item((HMENUI)menu, id);
      return DW_ERROR_UNKNOWN;
   }

   parent = GetParent(handle);
   menu = GetMenu(handle);

   if(menu)
      _dw_free_menu_data(menu);

   /* If it is a desktop window let WM_DESTROY handle it */
   if(parent != HWND_DESKTOP)
   {
      dw_box_unpack(handle);
      _dw_free_window_memory(handle, 0);
      EnumChildWindows(handle, _dw_free_window_memory, 0);
   }
   return DestroyWindow(handle);
}

/* Causes entire window to be invalidated and redrawn.
 * Parameters:
 *           handle: Toplevel window handle to be redrawn.
 */
void API dw_window_redraw(HWND handle)
{
   Box *mybox = (Box *)GetWindowLongPtr(handle, GWLP_USERDATA);

   if(mybox)
   {
      RECT rect;
      int istoplevel = (GetParent(handle) == HWND_DESKTOP);

      GetClientRect(handle, &rect);

      ShowWindow(istoplevel ? mybox->items[0].hwnd : handle, SW_HIDE);
      _dw_do_resize(mybox, rect.right - rect.left, rect.bottom - rect.top, 0, 0);
      ShowWindow(istoplevel ? mybox->items[0].hwnd : handle, SW_SHOW);
   }
}

/*
 * Changes a window's parent to newparent.
 * Parameters:
 *           handle: The window handle to destroy.
 *           newparent: The window's new parent window.
 */
void API dw_window_reparent(HWND handle, HWND newparent)
{
   SetParent(handle, newparent);
}

LOGFONT _get_logfont(HDC hdc, const char *fontname)
{
   char  *Italic, *Bold, *myFontName = strchr(fontname, '.');
   int size = atoi(fontname);
   LOGFONT lf = {0};

   /* If we found a '.' use the location after the . */
   if(myFontName)
       myFontName = _strdup(++myFontName);
   else /* Otherwise use the whole fontname and default size of 9 */
       myFontName = _strdup(fontname);

   lf.lfHeight = -MulDiv(size ? size : 9, GetDeviceCaps(hdc, LOGPIXELSY), 72);
   Italic = strstr(myFontName, " Italic");
   Bold = strstr(myFontName, " Bold");
   lf.lfWidth = 0;
   lf.lfEscapement = 0;
   lf.lfOrientation = 0;
   lf.lfItalic = Italic ? TRUE : FALSE;
   lf.lfUnderline = 0;
   lf.lfStrikeOut = 0;
   lf.lfWeight = Bold ? FW_BOLD : FW_NORMAL;
   lf.lfCharSet = DEFAULT_CHARSET;
   lf.lfOutPrecision = 0;
   lf.lfClipPrecision = 0;
   lf.lfQuality = DEFAULT_QUALITY;
   lf.lfPitchAndFamily = DEFAULT_PITCH | FW_DONTCARE;
   if(Italic)
      *Italic = 0;
   if(Bold)
      *Bold = 0;
   _tcsncpy(lf.lfFaceName, UTF8toWide(myFontName), (sizeof(lf.lfFaceName)/sizeof(TCHAR))-1);
   free(myFontName);
   return lf;
}

/* Create a duplicate of an existing font handle
 * that is safe to call DeleteObject() on.
 */
HFONT _dw_dup_font_handle(HFONT hfont)
{
    LOGFONT lf = {0};
    return GetObject(hfont, sizeof(lf), &lf) ? CreateFontIndirect(&lf) : NULL;
}

/* Create a font handle from specified font name..
 * or return a handle to the default font.
 */
HFONT _dw_acquire_font2(HDC hdc, const char *fontname)
{
   HFONT hfont = 0;

   if(fontname != DefaultFont && fontname[0])
   {
      LOGFONT lf = _get_logfont(hdc, fontname);
      hfont = CreateFontIndirect(&lf);
   }
   if(!hfont && _DWDefaultFont)
      hfont = _dw_dup_font_handle(_DWDefaultFont);
   if(!hfont)
      hfont = GetStockObject(DEFAULT_GUI_FONT);
   return hfont;
}

/* Create a font handle from specified font name..
 * or return a handle to the default font.
 */
HFONT _dw_acquire_font(HWND handle, const char *fontname)
{
   HDC hdc = GetDC(handle);
   HFONT hfont = _dw_acquire_font2(hdc, fontname);
   ReleaseDC(handle, hdc);
   return hfont;
}

/*
 * Sets the default font used on text based widgets.
 * Parameters:
 *           fontname: Font name in Dynamic Windows format.
 */
void API dw_font_set_default(const char *fontname)
{
    HFONT oldfont = _DWDefaultFont;

    _DWDefaultFont = _dw_acquire_font(HWND_DESKTOP, fontname);
    if(oldfont)
    {
        DeleteObject(oldfont);
    }
}

/* Internal function to return a pointer to an item struct
 * with information about the packing information regarding object.
 */
Item *_dw_box_item(HWND handle)
{
   HWND parent = GetParent(handle);
   Box *thisbox = (Box *)GetWindowLongPtr(parent, GWLP_USERDATA);

   /* If it is a desktop window let WM_DESTROY handle it */
   if(parent != HWND_DESKTOP)
   {
      if(thisbox && thisbox->count)
      {
         int z;
         Item *thisitem = thisbox->items;

         for(z=0;z<thisbox->count;z++)
         {
            if(thisitem[z].hwnd == handle)
               return &thisitem[z];
         }
      }
   }
   return NULL;
}

/* Internal function to calculate the widget's required size..
 * These are the general rules for widget sizes:
 *
 * Render/Unspecified: 1x1
 * Scrolled(Container,Tree,MLE): Guessed size clamped to min and max in dw.h
 * Entryfield/Combobox/Spinbutton: 150x(maxfontheight)
 * Spinbutton: 50x(maxfontheight)
 * Text/Status: (textwidth)x(textheight)
 * Ranged: 100x14 or 14x100 for vertical.
 * Buttons/Bitmaps: Size of text or image and border.
 */
void _dw_control_size(HWND handle, int *width, int *height)
{
   int thiswidth = 1, thisheight = 1, extrawidth = 0, extraheight = 0;
   TCHAR tmpbuf[100] = {0};
   static char testtext[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
   HBITMAP hbm = 0;
   HICON hic = 0;
   ICONINFO ii = {0};

   GetClassName(handle, tmpbuf, 99);

   /* Attempt to get icon from classes that can have them */
   if(_tcsnicmp(tmpbuf, STATICCLASSNAME, _tcslen(STATICCLASSNAME)+1) == 0)
      hic = (HICON)SendMessage(handle, STM_GETIMAGE, IMAGE_ICON, 0);
   else if(_tcsnicmp(tmpbuf, BUTTONCLASSNAME, _tcslen(BUTTONCLASSNAME)+1) == 0)
      hic = (HICON)SendMessage(handle, BM_GETIMAGE, IMAGE_ICON, 0);

   /* If we got an icon, pull out the internal bitmap */
   if(hic)
   {
      if(GetIconInfo(hic, &ii))
         hbm = ii.hbmMask ? ii.hbmMask : ii.hbmColor;
   }

   /* If we weren't able to get the bitmap from the icon... */
   if(!hbm)
   {
       /* Attempt to get bitmap from classes that can have them */
      if(_tcsnicmp(tmpbuf, STATICCLASSNAME, _tcslen(STATICCLASSNAME)+1) == 0)
         hbm = (HBITMAP)SendMessage(handle, STM_GETIMAGE, IMAGE_BITMAP, 0);
      else if(_tcsnicmp(tmpbuf, BUTTONCLASSNAME, _tcslen(BUTTONCLASSNAME)+1) == 0)
         hbm = (HBITMAP)SendMessage(handle, BM_GETIMAGE, IMAGE_BITMAP, 0);
   }

   /* If we got an image... set the sizes appropriately */
   if(hbm)
   {
      BITMAP bmi = { 0 };

      GetObject(hbm, sizeof(BITMAP), &bmi);
      thiswidth = bmi.bmWidth;
      thisheight = bmi.bmHeight;
   }
   else
   {
      char *buf = dw_window_get_text(handle);

      /* If we have a string...
       * calculate the size with the current font.
       */
      if(buf)
      {
         if(*buf)
            dw_font_text_extents_get(handle, NULL, buf, &thiswidth, &thisheight);
         else if(_tcsnicmp(tmpbuf, STATICCLASSNAME, _tcslen(STATICCLASSNAME)+1) == 0 ||
                 _tcsnicmp(tmpbuf, StatusbarClassName, _tcslen(StatusbarClassName)+1) == 0)
            dw_font_text_extents_get(handle, NULL, testtext, NULL, &thisheight);
         dw_free(buf);
      }
   }

   /* Combobox */
   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1) == 0)
   {
      dw_font_text_extents_get(handle, NULL, testtext, NULL, &thisheight);
      thiswidth = 150;
      extraheight = 4;
      if(thisheight < 18)
        thisheight = 18;
   }
   /* Ranged: Percent, Slider, Scrollbar */
   else if(_tcsnicmp(tmpbuf, PROGRESS_CLASS, _tcslen(PROGRESS_CLASS)+1) == 0 ||
           _tcsnicmp(tmpbuf, TRACKBAR_CLASS, _tcslen(TRACKBAR_CLASS)+1) == 0 ||
           _tcsnicmp(tmpbuf, SCROLLBARCLASSNAME, _tcslen(SCROLLBARCLASSNAME)+1) == 0)
   {
      if(_tcsnicmp(tmpbuf, SCROLLBARCLASSNAME, _tcslen(SCROLLBARCLASSNAME)+1) == 0 &&
        GetWindowLong(handle, GWL_STYLE) & SBS_VERT)
      {
         /* Vertical */
         thiswidth = GetSystemMetrics(SM_CXVSCROLL);
         thisheight = 100;
      }
      else
      {
         /* Horizontal */
         thiswidth = 100;
         thisheight = GetSystemMetrics(SM_CYHSCROLL);
      }
   }
   /* Spinbuttons */
   else if(_tcsnicmp(tmpbuf, UPDOWN_CLASS, _tcslen(UPDOWN_CLASS)+1) == 0)
   {
      dw_font_text_extents_get(handle, NULL, testtext, NULL, &thisheight);
      thiswidth = 50;
      extraheight = 6;
   }
#ifdef TOOLBAR
   /* Bitmap Buttons */
   else if(_tcsnicmp(tmpbuf, TOOLBARCLASSNAME, _tcslen(TOOLBARCLASSNAME)+1) == 0)
   {
      HIMAGELIST imlist = (HIMAGELIST)SendMessage(handle, TB_GETIMAGELIST, 0, 0);
      int minsize = 24;

      if(imlist)
         ImageList_GetIconSize(imlist, &thiswidth, &thisheight);

      /* If we are flat the size can be smaller */
      if(GetWindowLong(handle, GWL_STYLE) & TBSTYLE_FLAT)
         minsize = 20;
      else
      {
         thiswidth += 4;
         thisheight += 4;
      }

      if(thiswidth < minsize)
         thiswidth = minsize;
      if(thisheight < minsize)
         thisheight = minsize;
   }
#endif
   /* Listbox */
   else if(_tcsnicmp(tmpbuf, LISTBOXCLASSNAME, _tcslen(LISTBOXCLASSNAME)+1) == 0)
   {
      char buf[1025] = {0};
      int x, count = dw_listbox_count(handle);
      int basicwidth = thiswidth = GetSystemMetrics(SM_CXVSCROLL) + 8;

      thisheight = 8;

      for(x=0;x<count;x++)
      {
         int height, width = 0;

         dw_listbox_get_text(handle, x, buf, 1024);

         if(strlen(buf))
            dw_font_text_extents_get(handle, NULL, buf, &width, &height);
         else
            dw_font_text_extents_get(handle, NULL, testtext, NULL, &height);

         width += basicwidth;

         if(width > thiswidth)
            thiswidth = width > _DW_SCROLLED_MAX_WIDTH ? _DW_SCROLLED_MAX_WIDTH : width;
         thisheight += height;
      }

      if(thiswidth < _DW_SCROLLED_MIN_WIDTH)
         thiswidth = _DW_SCROLLED_MIN_WIDTH;
      if(thisheight < _DW_SCROLLED_MIN_HEIGHT)
         thisheight = _DW_SCROLLED_MIN_HEIGHT;
      if(thisheight > _DW_SCROLLED_MAX_HEIGHT)
         thisheight = _DW_SCROLLED_MAX_HEIGHT;
   }
   /* Entryfields and MLE */
   else if(_tcsnicmp(tmpbuf, EDITCLASSNAME, _tcslen(EDITCLASSNAME)+1) == 0 ||
           _tcsnicmp(tmpbuf, RICHEDIT_CLASS, _tcslen(RICHEDIT_CLASS)+1) == 0 ||
           _tcsnicmp(tmpbuf, MSFTEDIT_CLASS, _tcslen(MSFTEDIT_CLASS)+1) == 0)
   {
      LONG style = GetWindowLong(handle, GWL_STYLE);
      if((style & ES_MULTILINE))
      {
         unsigned long bytes;
         int height, width;
         char *buf, *ptr;
         int basicwidth;

         if(style & ES_AUTOHSCROLL)
            thisheight = GetSystemMetrics(SM_CYHSCROLL) + 8;
         else
            thisheight = 8;
         basicwidth = thiswidth = GetSystemMetrics(SM_CXVSCROLL) + 8;

         dw_mle_get_size(handle, &bytes, NULL);

         ptr = buf = _alloca(bytes + 2);
         dw_mle_export(handle, buf, 0, (int)bytes);
         buf[bytes] = 0;
         strcat(buf, "\n");

         /* MLE */
         while((ptr = strstr(buf, "\n")))
         {
            ptr[0] = 0;
            width = 0;
            if(strlen(buf))
               dw_font_text_extents_get(handle, NULL, buf, &width, &height);
            else
               dw_font_text_extents_get(handle, NULL, testtext, NULL, &height);

            width += basicwidth;

            if(!(style & ES_AUTOHSCROLL) && width > _DW_SCROLLED_MAX_WIDTH)
            {
               thiswidth = _DW_SCROLLED_MAX_WIDTH;
               thisheight += height * (width / _DW_SCROLLED_MAX_WIDTH);
            }
            else
            {
               if(width > thiswidth)
                  thiswidth = width > _DW_SCROLLED_MAX_WIDTH ? _DW_SCROLLED_MAX_WIDTH : width;
            }
            thisheight += height;
            buf = &ptr[1];
         }

         if(thiswidth < _DW_SCROLLED_MIN_WIDTH)
            thiswidth = _DW_SCROLLED_MIN_WIDTH;
         if(thisheight < _DW_SCROLLED_MIN_HEIGHT)
            thisheight = _DW_SCROLLED_MIN_HEIGHT;
         if(thisheight > _DW_SCROLLED_MAX_HEIGHT)
            thisheight = _DW_SCROLLED_MAX_HEIGHT;
      }
      else
      {
         /* Entryfield */
         dw_font_text_extents_get(handle, NULL, testtext, NULL, &thisheight);
         thiswidth = 150;
         extraheight = 6;
      }
   }
   /* Container */
   else if(_tcsnicmp(tmpbuf, WC_LISTVIEW, _tcslen(WC_LISTVIEW)+1)== 0)
   {
      DWORD result = ListView_ApproximateViewRect(handle, _DW_SCROLLED_MAX_WIDTH, _DW_SCROLLED_MAX_HEIGHT, -1);

      thiswidth = LOWORD(result);
      thisheight = HIWORD(result);

      if(thiswidth > _DW_SCROLLED_MAX_WIDTH)
         thiswidth = _DW_SCROLLED_MAX_WIDTH;
      if(thiswidth < _DW_SCROLLED_MIN_WIDTH)
         thiswidth = _DW_SCROLLED_MIN_WIDTH;
      if(thisheight < _DW_SCROLLED_MIN_HEIGHT)
         thisheight = _DW_SCROLLED_MIN_HEIGHT;
      if(thisheight > _DW_SCROLLED_MAX_HEIGHT)
         thisheight = _DW_SCROLLED_MAX_HEIGHT;
   }
   /* Tree */
   else if(_tcsnicmp(tmpbuf, WC_TREEVIEW, _tcslen(WC_TREEVIEW)+1)== 0)
   {
      thiswidth = (int)((_DW_SCROLLED_MAX_WIDTH + _DW_SCROLLED_MIN_WIDTH)/2);
      thisheight = (int)((_DW_SCROLLED_MAX_HEIGHT + _DW_SCROLLED_MIN_HEIGHT)/2);
   }
   /* Buttons */
   else if(_tcsnicmp(tmpbuf, BUTTONCLASSNAME, _tcslen(BUTTONCLASSNAME)+1) == 0)
   {
      ULONG style = GetWindowLong(handle, GWL_STYLE);

      /* Bitmap buttons */
      if(hbm)
      {
         extrawidth = 5;
         extraheight = 5;
      }
      /* Checkbox or radio button */
      else if(style & BS_AUTOCHECKBOX || style & BS_AUTORADIOBUTTON)
      {
         extrawidth = 24;
         extraheight = 4;
      }
      /* Text buttons */
      else
      {
         extrawidth = 8;
         extraheight = 8;
      }
   }
   else if(_tcsnicmp(tmpbuf, StatusbarClassName, _tcslen(StatusbarClassName)+1) == 0)
   {
      extrawidth = 4;
      extraheight = 2;
   }

   /* Set the requested sizes */
   if(width)
      *width = thiswidth + extrawidth;
   if(height)
      *height = thisheight + extraheight;

   /* Free temporary bitmaps */
   if(ii.hbmColor)
      DeleteObject(ii.hbmColor);
   if(ii.hbmMask)
      DeleteObject(ii.hbmMask);
}

/*
 * Sets the font used by a specified window (widget) handle.
 * Parameters:
 *          handle: The window (widget) handle.
 *          fontname: Name and size of the font in the form "size.fontname"
 */
int API dw_window_set_font(HWND handle, const char *fontname)
{
    HFONT hfont = NULL, oldfont = NULL;
    ColorInfo *cinfo = _dw_window_get_cinfo(handle);
    TCHAR tmpbuf[100] = {0};

    GetClassName(handle, tmpbuf, 99);
    if ( _tcsnicmp( tmpbuf, FRAMECLASSNAME, _tcslen(FRAMECLASSNAME)+1) == 0 )
    {
        /* groupbox */
        Box *thisbox = (Box *)GetWindowLongPtr( handle, GWLP_USERDATA );
        if ( thisbox && thisbox->grouphwnd != (HWND)NULL )
        {
            handle = thisbox->grouphwnd;
        }
    }
#ifdef RICHEDIT
    if (_tcsnicmp(tmpbuf, RICHEDIT_CLASS, _tcslen(RICHEDIT_CLASS)+1) == 0 ||
        _tcsnicmp(tmpbuf, MSFTEDIT_CLASS, _tcslen(MSFTEDIT_CLASS)+1) == 0)
    {
       if(fontname)
       {
            CHARFORMAT2 cf = {0};
            char  *Italic, *Bold, *myFontName = strchr(fontname, '.');
            int size = atoi(fontname);

            /* If we found a '.' use the location after the . */
            if(myFontName)
               myFontName = _strdup(++myFontName);
            else /* Otherwise use the whole fontname and default size of 9 */
               myFontName = _strdup(fontname);

            Italic = strstr(myFontName, " Italic");
            Bold = strstr(myFontName, " Bold");
            if(Italic)
                *Italic = 0;
            if(Bold)
                *Bold = 0;

            cf.cbSize = sizeof(cf);
            SendMessage(handle, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf);
            cf.dwMask = CFM_FACE | CFM_SIZE | CFM_ITALIC | CFM_BOLD;
            cf.dwEffects = (Italic ? CFE_ITALIC : 0) | (Bold ? CFE_BOLD : 0);
            _tcsncpy(cf.szFaceName, UTF8toWide(myFontName), (sizeof(cf.szFaceName)/sizeof(TCHAR))-1);
            /* yHeight is apparently the point size * 20, defaulting to 9 (180) */
            cf.yHeight = (size > 0) ? (size * 20) : 180;
            SendMessage(handle, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf);
       }
       else
       {
           /* On testing what the default is, the szFaceName is empty,
            * yHeight is 180 (9 point). So use that to reset to default.
            */
           CHARFORMAT2 cf = {0};
           cf.cbSize = sizeof(cf);
           cf.yHeight = 180;
           cf.dwMask = CFM_FACE | CFM_SIZE | CFM_ITALIC | CFM_BOLD;
           SendMessage(handle, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf);
       }
    }
    else
#endif
    {
        /* This needs to be after we get the correct handle */
        oldfont = (HFONT)SendMessage(handle, WM_GETFONT, 0, 0);
        hfont = _dw_acquire_font(handle, fontname);
    }

    if(fontname)
    {
        if(cinfo || (cinfo = _dw_window_new_cinfo(handle, TRUE)))
        {
            strncpy(cinfo->fontname, fontname, 127);
            if(hfont)
            {
                if(!oldfont)
                    oldfont = cinfo->hfont;
                cinfo->hfont = hfont;
            }
        }
    }
    /* If we changed the font... */
    if(hfont)
    {
       Item *item = _dw_box_item(handle);

       SendMessage(handle, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
       if(oldfont)
          DeleteObject(oldfont);

       /* Check to see if any of the sizes need to be recalculated */
       if(item && (item->origwidth == DW_SIZE_AUTO || item->origheight == DW_SIZE_AUTO))
       {
          _dw_control_size(handle, item->origwidth == DW_SIZE_AUTO ? &item->width : NULL, item->origheight == DW_SIZE_AUTO ? &item->height : NULL);
          /* Queue a redraw on the top-level window */
         _dw_redraw(_dw_toplevel_window(handle), TRUE);
       }
       return DW_ERROR_NONE;
    }
   return DW_ERROR_UNKNOWN;
}

/* Allows the user to choose a font using the system's font chooser dialog.
 * Parameters:
 *       currfont: current font
 * Returns:
 *       A malloced buffer with the selected font or NULL on error.
 */
char * API dw_font_choose(const char *currfont)
{
   CHOOSEFONT cf = { 0 };
   LOGFONT lf = { 0 };
   char *str = NULL;
   char *bold = "";
   char *italic = "";

   if(currfont && *currfont)
      lf = _get_logfont(NULL, currfont);

   cf.lStructSize = sizeof(cf);
   cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT;
   cf.lpLogFont = &lf;

   if(ChooseFont(&cf))
   {
      str = (char *)calloc( 101, 1 );
      if ( str )
      {
         int height;
         HDC hdc = GetDC(NULL);

         if ( lf.lfWeight > FW_MEDIUM )
            bold = " Bold";
         if ( lf.lfItalic )
            italic = " Italic";
         height = MulDiv(abs(lf.lfHeight), 72,  GetDeviceCaps (hdc, LOGPIXELSY));
         ReleaseDC(NULL, hdc);
         _snprintf( str, 100, "%d.%s%s%s", height, WideToUTF8(lf.lfFaceName), bold, italic );
      }
   }
   return str;
}

/*
 * Gets the font used by a specified window (widget) handle.
 * Parameters:
 *          handle: The window (widget) handle.
 *          fontname: Name and size of the font in the form "size.fontname"
 */
char * API dw_window_get_font(HWND handle)
{
   HFONT oldfont = NULL;
   char *str = NULL;
   char *bold = "";
   char *italic = "";
   LOGFONT lf = { 0 };
   Box *thisbox;
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);
   if ( _tcsnicmp( tmpbuf, FRAMECLASSNAME, _tcslen(FRAMECLASSNAME)+1) == 0 )
   {
      /* groupbox */
      thisbox = (Box *)GetWindowLongPtr( handle, GWLP_USERDATA );
      if ( thisbox && thisbox->grouphwnd != (HWND)NULL )
      {
         handle = thisbox->grouphwnd;
      }
   }
   oldfont = (HFONT)SendMessage(handle, WM_GETFONT, 0, 0);
   if ( GetObject( oldfont, sizeof(lf), &lf ) )
   {
      str = (char *)calloc( 100, 1 );
      if ( str )
      {
         int height;
         HDC hdc = GetDC(handle);

         if ( lf.lfWeight > FW_MEDIUM )
            bold = " Bold";
         if ( lf.lfItalic )
            italic = " Italic";
         height = MulDiv(abs(lf.lfHeight), 72,  GetDeviceCaps (hdc, LOGPIXELSY));
         ReleaseDC(handle, hdc);
         _snprintf( str, 100, "%d.%s%s%s", height, WideToUTF8(lf.lfFaceName), bold, italic );
      }
   }
   if ( oldfont )
      DeleteObject( oldfont );
   return str;
}

/*
 * Sets the colors used by a specified window (widget) handle.
 * Parameters:
 *          handle: The window (widget) handle.
 *          fore: Foreground color in RGB format.
 *          back: Background color in RGB format.
 */
int API dw_window_set_color(HWND handle, ULONG fore, ULONG back)
{
   ColorInfo *cinfo = _dw_window_get_cinfo(handle);
   Box *thisbox;
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, WC_LISTVIEW, _tcslen(WC_LISTVIEW))==0)
   {
      cinfo->fore = fore = _dw_internal_color(fore);
      cinfo->back = back = _dw_internal_color(back);

      ListView_SetTextColor(handle, RGB(DW_RED_VALUE(fore),
                                DW_GREEN_VALUE(fore),
                                DW_BLUE_VALUE(fore)));
      ListView_SetTextBkColor(handle, RGB(DW_RED_VALUE(back),
                                 DW_GREEN_VALUE(back),
                                 DW_BLUE_VALUE(back)));
      ListView_SetBkColor(handle, RGB(DW_RED_VALUE(back),
                              DW_GREEN_VALUE(back),
                              DW_BLUE_VALUE(back)));
      InvalidateRgn(handle, NULL, TRUE);
      return TRUE;
   }
   else if ( _tcsnicmp( tmpbuf, FRAMECLASSNAME, _tcslen(FRAMECLASSNAME)) == 0 )
   {
      /* groupbox */
      thisbox = (Box *)GetWindowLongPtr( handle, GWLP_USERDATA );
      if ( thisbox && thisbox->grouphwnd != (HWND)NULL )
      {
         thisbox->cinfo.fore = fore;
         thisbox->cinfo.back = back;
      }
   }
#ifdef RICHEDIT
   else if (_tcsnicmp(tmpbuf, RICHEDIT_CLASS, _tcslen(RICHEDIT_CLASS)+1) == 0 ||
            _tcsnicmp(tmpbuf, MSFTEDIT_CLASS, _tcslen(MSFTEDIT_CLASS)+1) == 0)
   {
        ULONG _fore = _dw_internal_color(fore);
        ULONG _back = _dw_internal_color(back);
        CHARFORMAT2 cf;

        SendMessage(handle, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf);
        cf.cbSize = sizeof(cf);
        cf.dwMask = CFM_COLOR;
        cf.dwEffects = 0;
        cf.crTextColor = RGB(DW_RED_VALUE(_fore), DW_GREEN_VALUE(_fore), DW_BLUE_VALUE(_fore)); 
        SendMessage(handle, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf);

        SendMessage(handle, EM_SETBKGNDCOLOR, (back == DW_CLR_DEFAULT || back == DW_RGB_TRANSPARENT) ? 1 : 0, 
                    RGB(DW_RED_VALUE(_back), DW_GREEN_VALUE(_back), DW_BLUE_VALUE(_back)));
   }
#endif

   if(cinfo || (cinfo = _dw_window_new_cinfo(handle, TRUE)))
   {
      cinfo->fore = fore;
      cinfo->back = back;
   }
   InvalidateRgn(handle, NULL, TRUE);
   return TRUE;
}

/*
 * Sets the font used by a specified window (widget) handle.
 * Parameters:
 *          handle: The window (widget) handle.
 *          border: Size of the window border in pixels.
 */
int API dw_window_set_border(HWND handle, int border)
{
   return 0;
}

/*
 * Captures the mouse input to this window.
 * Parameters:
 *       handle: Handle to receive mouse input.
 */
void API dw_window_capture(HWND handle)
{
   SetCapture(handle);
}

/*
 * Releases previous mouse capture.
 */
void API dw_window_release(void)
{
   ReleaseCapture();
}

/*
 * Changes the appearance of the mouse pointer.
 * Parameters:
 *       handle: Handle to widget for which to change.
 *       cursortype: ID of the pointer you want.
 */
void API dw_window_set_pointer(HWND handle, int pointertype)
{
   HCURSOR cursor = pointertype < 65536 ? LoadCursor(NULL, MAKEINTRESOURCE(pointertype)) : (HCURSOR)(intptr_t)pointertype;

   if(!pointertype)
      dw_window_set_data(handle, "_dw_cursor", 0);
    else
   {
      dw_window_set_data(handle, "_dw_cursor", DW_POINTER(cursor));
      SetCursor(cursor);
   }
}

/*
 * Create a new Window Frame.
 * Parameters:
 *       owner: The Owner's window handle or HWND_DESKTOP.
 *       title: The Window title.
 *       flStyle: Style flags, see the DW reference.
 */
HWND API dw_window_new(HWND hwndOwner, const char *title, ULONG flStyle)
{
   HWND hwndframe;
   Box *newbox = calloc(sizeof(Box), 1);
   ULONG flStyleEx = 0;
#ifdef AEROGLASS
   flStyleEx = WS_EX_LAYERED;
#endif

   newbox->type = DW_VERT;
   newbox->vsize = newbox->hsize = _DW_SIZE_EXPAND;
   newbox->cinfo.fore = newbox->cinfo.back = -1;
   newbox->cinfo.style = flStyle;

   if(!(flStyle & WS_CAPTION))
      flStyle |= WS_POPUPWINDOW;

   if(flStyle & DW_FCF_TASKLIST ||
      flStyle & WS_VSCROLL /* This is deprecated and should go away in version 3? */)
   {
      hwndframe = CreateWindowEx(flStyleEx, ClassName, UTF8toWide(title), (flStyle | WS_CLIPCHILDREN) & 0xffdf0000, CW_USEDEFAULT, CW_USEDEFAULT,
                           0, 0, hwndOwner, NULL, _DWInstance, NULL);
   }
   else
   {
      flStyleEx |= WS_EX_TOOLWINDOW;

      hwndframe = CreateWindowEx(flStyleEx, ClassName, UTF8toWide(title), (flStyle | WS_CLIPCHILDREN) & 0xffff0000, CW_USEDEFAULT, CW_USEDEFAULT,
                           0, 0, hwndOwner, NULL, _DWInstance, NULL);
   }

   SetWindowLongPtr(hwndframe, GWLP_USERDATA, (LONG_PTR)newbox);

   if(hwndOwner)
      SetParent(hwndframe, hwndOwner);

#ifdef AEROGLASS
   /* Determine the borders of the default window frame */
   AdjustWindowRectEx(&(newbox->cinfo.rect), flStyle, FALSE, flStyleEx);
#ifdef DEBUG
   dw_debug("AdjustWindowRectExt(%d,%d,%d,%d)\n", newbox->cinfo.rect.left, newbox->cinfo.rect.right,
                                                  newbox->cinfo.rect.top, newbox->cinfo.rect.bottom);
#endif
   newbox->cinfo.rect.top *= -1;
   newbox->cinfo.rect.left = newbox->cinfo.rect.right = newbox->cinfo.rect.bottom = 1;

   if((flStyle & DW_FCF_COMPOSITED) && !IS_WIN8PLUS)
   {
      /* Attempt to enable Aero glass background on the entire window */
      if(_DwmExtendFrameIntoClientArea && _dw_composition)
      {
         MARGINS fullmar = {-1,-1,-1,-1};

         _DwmExtendFrameIntoClientArea(hwndframe, &fullmar);
         _dw_show_margins(hwndframe, fullmar, __LINE__);
      }
      SetLayeredWindowAttributes(hwndframe, _dw_transparencykey, 0, LWA_COLORKEY);
   }
   else
   {
      if(_DwmExtendFrameIntoClientArea && _dw_composition)
      {
         MARGINS mar = {0};

#ifdef DARK_MODE_TITLEBAR_MENU
         mar = _dw_rect_to_margins(newbox->cinfo.rect);
#endif

         _DwmExtendFrameIntoClientArea(hwndframe, &mar);
         _dw_show_margins(hwndframe, mar, __LINE__);
      }
      SetLayeredWindowAttributes(hwndframe, _dw_transparencykey, 255, LWA_ALPHA);
   }
#endif

   return hwndframe;
}

/*
 * Create a new Box to be packed.
 * Parameters:
 *       type: Either DW_VERT (vertical) or DW_HORZ (horizontal).
 *       pad: Number of pixels to pad around the box.
 */
HWND API dw_box_new(int type, int pad)
{
   Box *newbox = calloc(sizeof(Box), 1);
   HWND hwndframe;

   newbox->pad = pad;
   newbox->type = type;
   newbox->count = 0;
   newbox->grouphwnd = (HWND)NULL;
   newbox->cinfo.fore = newbox->cinfo.back = -1;

   hwndframe = CreateWindow(FRAMECLASSNAME,
                      NULL,
                      WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN,
                      0,0,0,0,
                      DW_HWND_OBJECT,
                      NULL,
                      _DWInstance,
                      NULL);

   newbox->cinfo.pOldProc = SubclassWindow(hwndframe, _dw_colorwndproc);
   newbox->cinfo.fore = newbox->cinfo.back = -1;

   SetWindowLongPtr(hwndframe, GWLP_USERDATA, (LONG_PTR)newbox);
   return hwndframe;
}

/*
 * Create a new scroll Box to be packed.
 * Parameters:
 *       type: Either DW_VERT (vertical) or DW_HORZ (horizontal).
 *       pad: Number of pixels to pad around the box.
 */
HWND API dw_scrollbox_new(int type, int pad)
{
   HWND hwndframe, box = dw_box_new(type, pad);
   HWND tmpbox = dw_box_new(DW_VERT, 0);
   ColorInfo *cinfo;

   dw_box_pack_start(tmpbox, box, 1, 1, TRUE, TRUE, 0);

   hwndframe = CreateWindow(ScrollClassName,
                     NULL,
                     WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     NULL,
                     _DWInstance,
                     NULL);

   if((cinfo = _dw_window_new_cinfo(hwndframe, FALSE)))
   {
      cinfo->buddy = box;
      cinfo->combo = tmpbox;
   }
   SetParent(tmpbox, hwndframe);
   return hwndframe;
}

/*
 * Returns the position of the scrollbar in the scrollbox
 * Parameters:
 *          handle: Handle to the scrollbox to be queried.
 *          orient: The vertical or horizontal scrollbar.
 */
int API dw_scrollbox_get_pos( HWND handle, int orient )
{
    SCROLLINFO si;
    int bar = SB_HORZ;

    if(orient == DW_VERT)
    {
        bar = SB_VERT;
    }

    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_POS;

    /* Get the current scroll positions */
    if(!GetScrollInfo(handle, bar, &si))
    {
        return -1;
    }
    return si.nPos;
}

/*
 * Gets the range for the scrollbar in the scrollbox.
 * Parameters:
 *          handle: Handle to the scrollbox to be queried.
 *          orient: The vertical or horizontal scrollbar.
 */
int API dw_scrollbox_get_range( HWND handle, int orient )
{
    SCROLLINFO si;
    int bar = SB_HORZ;

    if(orient == DW_VERT)
    {
        bar = SB_VERT;
    }

    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_RANGE;

    /* Get the current scroll positions */
    if(!GetScrollInfo(handle, bar, &si))
    {
        return -1;
    }
    return si.nMax;
}
/*
 * Create a new Group Box to be packed.
 * Parameters:
 *       type: Either DW_VERT (vertical) or DW_HORZ (horizontal).
 *       pad: Number of pixels to pad around the box.
 *       title: Text to be displayined in the group outline.
 */
HWND API dw_groupbox_new(int type, int pad, const char *title)
{
   Box *newbox = calloc(sizeof(Box), 1);
   HWND hwndframe;

   newbox->pad = pad;
   newbox->type = type;
   newbox->count = 0;
   newbox->cinfo.fore = newbox->cinfo.back = -1;

   hwndframe = CreateWindow(FRAMECLASSNAME,
                      NULL,
                      WS_VISIBLE | WS_CHILD,
                      0,0,0,0,
                      DW_HWND_OBJECT,
                      NULL,
                      _DWInstance,
                      NULL);

   newbox->grouphwnd = CreateWindow(BUTTONCLASSNAME,
                            UTF8toWide(title),
                            WS_CHILD | BS_GROUPBOX |
                            WS_VISIBLE | WS_CLIPCHILDREN,
                            0,0,0,0,
                            hwndframe,
                            NULL,
                            _DWInstance,
                            NULL);

   /* Disable visual styles by default */
   if(_SetWindowTheme)
      _SetWindowTheme(newbox->grouphwnd, L"", L"");

   SetWindowLongPtr(hwndframe, GWLP_USERDATA, (LONG_PTR)newbox);
   newbox->cinfo.pOldProc = SubclassWindow(hwndframe, _dw_colorwndproc);
   dw_window_set_font(hwndframe, DefaultFont);
   return hwndframe;
}

/*
 * Create a new MDI Frame to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id or 0L.
 */
HWND API dw_mdi_new(unsigned long id)
{
   CLIENTCREATESTRUCT ccs;
   HWND hwndframe;

   ccs.hWindowMenu = NULL;
   ccs.idFirstChild = 0;

   hwndframe = CreateWindow(TEXT("MDICLIENT"),
                      NULL,
                      WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
                      0,0,0,0,
                      DW_HWND_OBJECT,
                      (HMENU)(uintptr_t)id,
                      _DWInstance,
                      &ccs);
   return hwndframe;
}

/*
 * Create a new HTML browser frame to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id or 0L.
 */
HWND API dw_html_new(unsigned long id)
{
#if (defined(BUILD_DLL) || defined(BUILD_HTML))
   return CreateWindow(BrowserClassName,
                  NULL,
                  WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
                  0,0,0,0,
                  DW_HWND_OBJECT,
                  (HMENU)(uintptr_t)id,
                  _DWInstance,
                  NULL);
#else
   dw_debug("HTML widget not available; Support not enabled in this build.\n");
   return 0;
#endif
}

#if (defined(BUILD_DLL) || defined(BUILD_HTML))
void _dw_html_action(HWND hwnd, int action);
int _dw_html_raw(HWND hwnd, const char *string);
int _dw_html_url(HWND hwnd, const char *url);
int _dw_html_javascript_run(HWND hwnd, const char *script, void *scriptdata);
#ifdef BUILD_EDGE
void _dw_edge_action(HWND hwnd, int action);
int _dw_edge_raw(HWND hwnd, const char *string);
int _dw_edge_url(HWND hwnd, const char *url);
int _dw_edge_javascript_run(HWND hwnd, const char *script, void *scriptdata);
#endif
#endif

/*
 * Causes the embedded HTML widget to take action.
 * Parameters:
 *       handle: Handle to the window.
 *       action: One of the DW_HTML_* constants.
 */
void API dw_html_action(HWND handle, int action)
{
#if (defined(BUILD_DLL) || defined(BUILD_HTML))
#ifdef BUILD_EDGE
   if (_DW_EDGE_DETECTED)
      _dw_edge_action(handle, action);
   else
#endif
   _dw_html_action(handle, action);
#endif
}

/*
 * Render raw HTML code in the embedded HTML widget..
 * Parameters:
 *       handle: Handle to the window.
 *       string: String buffer containt HTML code to
 *               be rendered.
 * Returns:
 *       DW_ERROR_NONE (0) on success.
 */
int API dw_html_raw(HWND handle, const char *string)
{
#if (defined(BUILD_DLL) || defined(BUILD_HTML))
#ifdef BUILD_EDGE
   if (_DW_EDGE_DETECTED)
      return _dw_edge_raw(handle, string);
#endif
   return _dw_html_raw(handle, string);
#else
   return DW_ERROR_GENERAL;
#endif
}

/*
 * Render file or web page in the embedded HTML widget..
 * Parameters:
 *       handle: Handle to the window.
 *       url: Universal Resource Locator of the web or
 *               file object to be rendered.
 * Returns:
 *       DW_ERROR_NONE (0) on success.
 */
int API dw_html_url(HWND handle, const char *url)
{
#if (defined(BUILD_DLL) || defined(BUILD_HTML))
#if BUILD_EDGE
   if (_DW_EDGE_DETECTED)
      return _dw_edge_url(handle, url);
#endif
   return _dw_html_url(handle, url);
#else
   return DW_ERROR_GENERAL;
#endif
}

/*
 * Executes the javascript contained in "script" in the HTML window.
 * Parameters:
 *       handle: Handle to the HTML window.
 *       script: Javascript code to execute.
 *       scriptdata: Data passed to the signal handler.
 * Notes: A DW_SIGNAL_HTML_RESULT event will be raised with scriptdata.
 * Returns:
 *       DW_ERROR_NONE (0) on success.
 */
int dw_html_javascript_run(HWND handle, const char *script, void *scriptdata)
{
#if (defined(BUILD_DLL) || defined(BUILD_HTML))
#if BUILD_EDGE
   if (_DW_EDGE_DETECTED)
      return _dw_edge_javascript_run(handle, script, scriptdata);
#endif
   return _dw_html_javascript_run(handle, script, scriptdata);
#else
   return DW_ERROR_UNKNOWN;
#endif
}

/*
 * Create a bitmap object to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id or 0L.
 */
HWND API dw_bitmap_new(ULONG id)
{
   return CreateWindow(STATICCLASSNAME,
                  NULL,
                  SS_BITMAP | SS_CENTERIMAGE | WS_VISIBLE |
                  WS_CHILD | WS_CLIPCHILDREN,
                  0,0,0,0,
                  DW_HWND_OBJECT,
                  (HMENU)(uintptr_t)id,
                  _DWInstance,
                  NULL);
}

/*
 * Create a notebook object to be packed.
 * Parameters:
 *       id: An ID to be used for getting the resource from the
 *           resource file.
 */
HWND API dw_notebook_new(ULONG id, int top)
{
   ULONG flags = 0;
   HWND tmp;
   NotebookPage **array = calloc(256, sizeof(NotebookPage *));
   ColorInfo *cinfo;

   if(!top)
      flags = TCS_BOTTOM;

   tmp = CreateWindow(WC_TABCONTROL,
                  NULL,
                  WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | flags,
                  0,0,0,0,
                  DW_HWND_OBJECT,
                  (HMENU)(uintptr_t)id,
                  _DWInstance,
                  NULL);
   if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
      cinfo->pOldProc = SubclassWindow(tmp, _dw_simplewndproc);
   dw_window_set_data(tmp, "_dw_array", (void *)array);
   dw_window_set_font(tmp, DefaultFont);
   return tmp;
}

/*
 * Create a menu object to be popped up.
 * Parameters:
 *       id: An ID to be used for getting the resource from the
 *           resource file.
 */
HMENUI API dw_menu_new(ULONG id)
{
   return (HMENUI)CreatePopupMenu();
}

/*
 * Create a menubar on a window.
 * Parameters:
 *       location: Handle of a window frame to be attached to.
 */
HMENUI API dw_menubar_new(HWND location)
{
   HMENUI tmp;
   ColorInfo *cinfo = _dw_window_get_cinfo(location);

   if(!cinfo)
      return NULL;

#ifdef DARK_MODE_TITLEBAR_MENU
   if(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_SUPPORTED)
      tmp = (HMENUI)CreatePopupMenu();
   else
#endif
   {
      MENUINFO mi;

      tmp = (HMENUI)CreateMenu();

      mi.cbSize = sizeof(MENUINFO);
      mi.fMask = MIM_MENUDATA;
      mi.dwMenuData = (ULONG_PTR)1;

      SetMenuInfo((HMENU)tmp, &mi);
   }

   cinfo->hmenu = (HMENU)tmp;

#ifdef DARK_MODE_TITLEBAR_MENU
   if(!(_DW_DARK_MODE_ALLOWED > DW_DARK_MODE_BASIC && _DW_DARK_MODE_SUPPORTED))
#endif      
   SetMenu(location, (HMENU)tmp);
   return location;
}

/*
 * Destroys a menu created with dw_menubar_new or dw_menu_new.
 * Parameters:
 *       menu: Handle of a menu.
 */
void API dw_menu_destroy(HMENUI *menu)
{
   if(menu)
   {
      HMENU mymenu = (HMENU)*menu;

      if(IsWindow((HWND)mymenu) && !IsMenu(mymenu))
      {
         ColorInfo *cinfo = _dw_window_get_cinfo((HWND)mymenu);

         if(cinfo)
            mymenu = cinfo->hmenu;
      }
      if(IsMenu(mymenu))
         DestroyMenu(mymenu);
      *menu = NULL;
   }
}

/* Internal function to make sure menu ID isn't in use */
int _dw_menuid_allocated(int id)
{
   DWSignalHandler *tmp = Root;

   while(tmp)
   {
     if(tmp->id == id)
        return TRUE;
     tmp = tmp->next;
   }
   return FALSE;
}

/*
 * Adds a menuitem or submenu to an existing menu.
 * Parameters:
 *       menu: The handle to the existing menu.
 *       title: The title text on the menu item to be added.
 *       id: An ID to be used for message passing.
 *       end: If TRUE memu is positioned at the end of the menu.
 *       check: If TRUE menu is "check"able.
 *       flags: Extended attributes to set on the menu.
 *       submenu: Handle to an existing menu to be a submenu or NULL.
 */
HWND API dw_menu_append_item(HMENUI menux, const char *title, ULONG id, ULONG flags, int end, int check, HMENUI submenu)
{
   MENUITEMINFO mii;
   HMENU mymenu = (HMENU)menux;
   char buffer[31] = {0};
   int is_checked, is_disabled;
   char *menutitle = (char *)title;

   /*
    * Check if this is a menubar; if so get the menu object
    * for the menubar
    */
   if(IsWindow(menux) && !IsMenu(mymenu))
   {
      ColorInfo *cinfo = _dw_window_get_cinfo((HWND)menux);

      if(cinfo)
         mymenu = cinfo->hmenu;
      else
         return NULL;
   }

   memset( &mii, 0, sizeof(mii) );
   mii.cbSize = sizeof(MENUITEMINFO);
   mii.fMask = MIIM_ID | MIIM_SUBMENU | MIIM_TYPE | MIIM_STATE;

   /* Convert from OS/2 style accellerators to Win32 style */
   if (title)
   {
      char *tmp = menutitle = _alloca(strlen(title)+1);

      strcpy(tmp, title);

      while(*tmp)
      {
         if(*tmp == '~')
            *tmp = '&';
         tmp++;
      }
   }

   if (menutitle && *menutitle)
   {
      /* Code to autogenerate a menu ID if not specified or invalid
       * First pool is smaller for transient popup menus
       */
      if(id == (ULONG)-1)
      {
         static ULONG tempid = 61000;

         tempid++;
         id = tempid;

         if(tempid > 65500)
            tempid = 61000;
      }
      /* Special internal case - 60001 to 60999 reserved for DW internal use */
      else if(id > 60000 && id < 61000 && check == -1)
      {
          check = 0;
      }
      /* Second pool is larger for more static windows */
      else if(!id || id >= 30000)
      {
         static ULONG menuid = 30000;

         do
         {
            menuid++;
            if(menuid > 60000)
               menuid = 30000;
         }
         while(_dw_menuid_allocated(menuid));
         id = menuid;
      }
      mii.fType = MFT_STRING;
   }
   else
      mii.fType = MFT_SEPARATOR;

   /*
    * Handle flags
    */
   is_checked = (flags & DW_MIS_CHECKED) ? 1 : 0;
   if ( is_checked )
      mii.fState |= MFS_CHECKED;
   else
      mii.fState |= MFS_UNCHECKED;
   is_disabled = (flags & DW_MIS_DISABLED) ? 1 : 0;
   if ( is_disabled )
      mii.fState |= MFS_DISABLED;
   else
      mii.fState |= MFS_ENABLED;

   mii.wID = id;
   if (IsMenu((HMENU)submenu))
      mii.hSubMenu = (HMENU)submenu;
   else
      mii.hSubMenu = 0;
   mii.dwTypeData = UTF8toWide(menutitle);
   mii.cch = (UINT)_tcslen(mii.dwTypeData);

   InsertMenuItem(mymenu, 65535, TRUE, &mii);

   _snprintf(buffer, 30, "_dw_id%ld", id);
   dw_window_set_data( DW_HWND_OBJECT, buffer, DW_POINTER(mymenu) );
   _snprintf(buffer, 30, "_dw_checkable%ld", id);
   dw_window_set_data( DW_HWND_OBJECT, buffer, DW_INT_TO_POINTER(check) );
   _snprintf(buffer, 30, "_dw_ischecked%ld", id);
   dw_window_set_data( DW_HWND_OBJECT, buffer, DW_INT_TO_POINTER(is_checked) );
   _snprintf(buffer, 30, "_dw_isdisabled%ld", id);
   dw_window_set_data( DW_HWND_OBJECT, buffer, DW_INT_TO_POINTER(is_disabled) );

   if (submenu)
   {
      MENUINFO mi;

      mi.cbSize = sizeof(MENUINFO);
      mi.fMask = MIM_MENUDATA;
      mi.dwMenuData = (ULONG_PTR)mymenu;

      SetMenuInfo( (HMENU)submenu, &mi );
   }

   if (IsWindow(menux) && !IsMenu((HMENU)menux))
      DrawMenuBar(menux);
   return (HWND)(uintptr_t)id;
}

/*
 * Sets the state of a menu item check.
 * Deprecated: use dw_menu_item_set_state()
 * Parameters:
 *       menu: The handle to the existing menu.
 *       id: Menuitem id.
 *       check: TRUE for checked FALSE for not checked.
 */
void API dw_menu_item_set_check(HMENUI menux, unsigned long id, int check)
{
   MENUITEMINFO mii;
   HMENU mymenu = (HMENU)menux;
   char buffer[30];

   if(IsWindow(menux) && !IsMenu(mymenu))
   {
      ColorInfo *cinfo = _dw_window_get_cinfo((HWND)menux);

      if(cinfo)
         mymenu = cinfo->hmenu;
      else
         return;
   }

   /*
    * Get the current state of the menu item in case it already has some other state set on it
    */
   memset( &mii, 0, sizeof(mii) );
   GetMenuItemInfo( mymenu, id, FALSE, &mii);

   mii.cbSize = sizeof(MENUITEMINFO);
   mii.fMask = MIIM_STATE | MIIM_CHECKMARKS;
   if (check)
      mii.fState |= MFS_CHECKED;
   else
      mii.fState |= MFS_UNCHECKED;
   SetMenuItemInfo( mymenu, id, FALSE, &mii );
   /*
    * Keep our internal state consistent
    */
   _snprintf( buffer, 30, "_dw_ischecked%ld", id );
   dw_window_set_data( DW_HWND_OBJECT, buffer, DW_INT_TO_POINTER(check) );
}

/*
 * Sets the state of a menu item.
 * Parameters:
 *       menu: The handle to the existing menu.
 *       id: Menuitem id.
 *       flags: DW_MIS_ENABLED/DW_MIS_DISABLED
 *              DW_MIS_CHECKED/DW_MIS_UNCHECKED
 */
void API dw_menu_item_set_state(HMENUI menux, unsigned long id, unsigned long state)
{
   MENUITEMINFO mii;
   HMENU mymenu = (HMENU)menux;
   char buffer1[31] = {0}, buffer2[31] = {0};
   int check;
   int disabled;

   if(IsWindow(menux) && !IsMenu(mymenu))
   {
      ColorInfo *cinfo = _dw_window_get_cinfo((HWND)menux);

      if(cinfo)
         mymenu = cinfo->hmenu;
      else
         return;
   }

   _snprintf( buffer1, 30, "_dw_ischecked%ld", id );
   check = DW_POINTER_TO_INT(dw_window_get_data( DW_HWND_OBJECT, buffer1 ));
   _snprintf( buffer2, 30, "_dw_isdisabled%ld", id );
   disabled = DW_POINTER_TO_INT(dw_window_get_data( DW_HWND_OBJECT, buffer2 ));

   memset(&mii, 0, sizeof(mii));

   mii.cbSize = sizeof(MENUITEMINFO);
   mii.fMask = MIIM_STATE | MIIM_CHECKMARKS;
   if((state & DW_MIS_CHECKED) || (state & DW_MIS_UNCHECKED))
   {
      /*
       * If we are changing state of "checked" base our setting on the passed flag...
       */
      if ( state & DW_MIS_CHECKED )
      {
         mii.fState |= MFS_CHECKED;
         check = 1;
      }
      else
      {
         mii.fState |= MFS_UNCHECKED;
         check = 0;
      }
   }
   else
   {
      /*
       * ...otherwise base our setting on the current "checked" state.
       */
      if ( check )
      {
         mii.fState |= MFS_CHECKED;
      }
      else
      {
         mii.fState |= MFS_UNCHECKED;
      }
   }
   if((state & DW_MIS_ENABLED) || (state & DW_MIS_DISABLED))
   {
      if ( state & DW_MIS_DISABLED )
      {
         mii.fState |= MFS_DISABLED;
         disabled = 1;
      }
      else
      {
         mii.fState |= MFS_ENABLED;
         disabled = 0;
      }
   }
   else
   {
      /*
       * ...otherwise base our setting on the current "checked" state.
       */
      if ( disabled )
      {
         mii.fState |= MFS_DISABLED;
      }
      else
      {
         mii.fState |= MFS_ENABLED;
      }
   }
   SetMenuItemInfo(mymenu, id, FALSE, &mii);
   /*
    * Keep our internal checked state consistent
    */
   dw_window_set_data(DW_HWND_OBJECT, buffer1, DW_INT_TO_POINTER(check));
   dw_window_set_data(DW_HWND_OBJECT, buffer2, DW_INT_TO_POINTER(disabled));
}

/*
 * Deletes the menu item specified
 * Parameters:
 *       menu: The handle to the  menu in which the item was appended.
 *       id: Menuitem id.
 * Returns:
 *       DW_ERROR_NONE (0) on success or DW_ERROR_UNKNOWN on failure.
 */
int API dw_menu_delete_item(HMENUI menux, unsigned long id)
{
   HMENU mymenu = (HMENU)menux;

   if(IsWindow(menux) && !IsMenu(mymenu))
   {
      ColorInfo *cinfo = _dw_window_get_cinfo((HWND)menux);

      if(cinfo)
         mymenu = cinfo->hmenu;
      else
         return DW_ERROR_UNKNOWN;
   }

   if(mymenu == 0 || DeleteMenu(mymenu, id, MF_BYCOMMAND) == 0)
      return DW_ERROR_UNKNOWN;

   /* If the ID was autogenerated it is safe to remove it */
   if(id >= 30000)
      dw_signal_disconnect_by_window((HWND)(uintptr_t)id);

   /* Make sure the menu is redrawn if needed */
   if((HMENU)menux != mymenu)
      DrawMenuBar(menux);
   return DW_ERROR_NONE;
}

/*
 * Pops up a context menu at given x and y coordinates.
 * Parameters:
 *       menu: The handle the the existing menu.
 *       parent: Handle to the window initiating the popup.
 *       x: X coordinate.
 *       y: Y coordinate.
 */
void API dw_menu_popup(HMENUI *menu, HWND parent, int x, int y)
{
   if(menu)
   {
      HMENU mymenu = (HMENU)*menu;

      if(IsWindow(*menu) && !IsMenu(mymenu))
      {
         ColorInfo *cinfo = _dw_window_get_cinfo((HWND)*menu);

         if(cinfo)
            mymenu = cinfo->hmenu;
         else
            return;
      }

      _dw_popup = parent;
      TrackPopupMenu(mymenu, 0, x, y, 0, parent, NULL);
      PostMessage(DW_HWND_OBJECT, WM_USER+5, (LPARAM)mymenu, 0);
      *menu = NULL;
   }
}


/*
 * Create a container object to be packed.
 * Parameters:
 *       id: An ID to be used for getting the resource from the
 *           resource file.
 */
HWND API dw_container_new(ULONG id, int multi)
{
   HWND tmp = CreateWindow(WC_LISTVIEW,
                     NULL,
                     WS_VISIBLE | WS_CHILD |
                     (multi ? 0 : LVS_SINGLESEL) |
                     LVS_REPORT | LVS_SHOWSELALWAYS |
                     LVS_SHAREIMAGELISTS | WS_BORDER |
                     WS_CLIPCHILDREN,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);
   ContainerInfo *cinfo = (ContainerInfo *)calloc(1, sizeof(ContainerInfo));

   if(!cinfo)
   {
      DestroyWindow(tmp);
      return NULL;
   }

   cinfo->cinfo.pOldProc = SubclassWindow(tmp, _dw_containerwndproc);
   cinfo->cinfo.fore = cinfo->cinfo.back = -1;
   cinfo->odd = cinfo->even = DW_RGB_TRANSPARENT;

   SetWindowLongPtr(tmp, GWLP_USERDATA, (LONG_PTR)cinfo);
   dw_window_set_font(tmp, DefaultFont);
   /* If we are running XP or higher... */
   if(IS_XPPLUS)
   {
      /* Enable double buffering to prevent flicker */
      ListView_SetExtendedListViewStyleEx(tmp, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER);
   }
   return tmp;
}

/*
 * Create a tree object to be packed.
 * Parameters:
 *       id: An ID to be used for getting the resource from the
 *           resource file.
 */
HWND API dw_tree_new(ULONG id)
{
   HWND tmp = CreateWindow(WC_TREEVIEW,
                     NULL,
                     WS_VISIBLE | WS_CHILD |
                     TVS_HASLINES | TVS_SHOWSELALWAYS |
                     TVS_HASBUTTONS | TVS_LINESATROOT |
                     WS_BORDER | WS_CLIPCHILDREN,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);
   ContainerInfo *cinfo = (ContainerInfo *)calloc(1, sizeof(ContainerInfo));
   TreeView_SetItemHeight(tmp, 16);

   if(!cinfo)
   {
      DestroyWindow(tmp);
      return NULL;
   }

   cinfo->cinfo.pOldProc = SubclassWindow(tmp, _dw_treewndproc);
   cinfo->cinfo.fore = cinfo->cinfo.back = -1;
   cinfo->odd = cinfo->even = DW_RGB_TRANSPARENT;

   SetWindowLongPtr(tmp, GWLP_USERDATA, (LONG_PTR)cinfo);
   dw_window_set_font(tmp, DefaultFont);
   return tmp;
}

/*
 * Returns the current X and Y coordinates of the mouse pointer.
 * Parameters:
 *       x: Pointer to variable to store X coordinate.
 *       y: Pointer to variable to store Y coordinate.
 */
void API dw_pointer_query_pos(long *x, long *y)
{
   POINT ptl;

   GetCursorPos(&ptl);
   if(x && y)
   {
      *x = ptl.x;
      *y = ptl.y;
   }
}

/*
 * Sets the X and Y coordinates of the mouse pointer.
 * Parameters:
 *       x: X coordinate.
 *       y: Y coordinate.
 */
void API dw_pointer_set_pos(long x, long y)
{
   SetCursorPos(x, y);
}

/*
 * Create a new static text window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_text_new(const char *text, ULONG id)
{
   HWND tmp = CreateWindow(STATICCLASSNAME,
                     UTF8toWide(text),
                     SS_NOPREFIX | SS_NOTIFY | SS_LEFTNOWORDWRAP |
                     WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);
#ifdef AEROGLASS
   ColorInfo *cinfo = _dw_window_new_cinfo(tmp, FALSE);

   if(cinfo)
      cinfo->pOldProc = SubclassWindow(tmp, _dw_staticwndproc);
#endif
   dw_window_set_font(tmp, DefaultFont);
   dw_window_set_color(tmp, DW_CLR_DEFAULT, DW_RGB_TRANSPARENT);
   return tmp;
}

/*
 * Create a new status text window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_status_text_new(const char *text, ULONG id)
{
   HWND tmp = CreateWindow(StatusbarClassName,
                     UTF8toWide(text),
                     WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);
   dw_window_set_font(tmp, DefaultFont);
   return tmp;
}

/*
 * Create a new Multiline Editbox window (widget) to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_mle_new(ULONG id)
{
   HWND tmp = CreateWindowEx(WS_EX_CLIENTEDGE,
#ifdef RICHEDIT
                       _DW_MLE_RICH_EDIT == DW_FEATURE_ENABLED ? (hmsftedit ? MSFTEDIT_CLASS : (hrichedit ? RICHEDIT_CLASS : EDITCLASSNAME)) : EDITCLASSNAME,
#else
                       EDITCLASSNAME,
#endif
                       NULL,
                       WS_VISIBLE | WS_BORDER |
                       WS_VSCROLL | ES_MULTILINE |
                       ES_WANTRETURN | WS_CHILD |
                       WS_CLIPCHILDREN,
                       0,0,0,0,
                       DW_HWND_OBJECT,
                       (HMENU)(uintptr_t)id,
                       _DWInstance,
                       NULL);
   ContainerInfo *cinfo = (ContainerInfo *)calloc(1, sizeof(ContainerInfo));

   if(!cinfo)
   {
      DestroyWindow(tmp);
      return NULL;
   }

#ifdef RICHEDIT
   if(_DW_MLE_RICH_EDIT == DW_FEATURE_ENABLED && (hrichedit || hmsftedit))
      cinfo->cinfo.pOldProc = SubclassWindow(tmp, _dw_richeditwndproc);
   else
#endif
   cinfo->cinfo.pOldProc = SubclassWindow(tmp, _dw_simplewndproc);
   cinfo->cinfo.fore = cinfo->cinfo.back = -1;
   cinfo->odd = cinfo->even = DW_RGB_TRANSPARENT;

   SetWindowLongPtr(tmp, GWLP_USERDATA, (LONG_PTR)cinfo);
   dw_window_set_font(tmp, DefaultFont);
   return tmp;
}

/*
 * Create a new Entryfield window (widget) to be packed.
 * Parameters:
 *       text: The default text to be in the entryfield widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_entryfield_new(const char *text, ULONG id)
{
   HWND tmp = CreateWindowEx(WS_EX_CLIENTEDGE,
                       EDITCLASSNAME,
                       UTF8toWide(text),
                       ES_WANTRETURN | WS_CHILD |
                       WS_BORDER | ES_AUTOHSCROLL |
                       WS_VISIBLE | WS_CLIPCHILDREN,
                       0,0,0,0,
                       DW_HWND_OBJECT,
                       (HMENU)(uintptr_t)id,
                       _DWInstance,
                       NULL);

   _dw_window_new_cinfo(tmp, TRUE);
   dw_window_set_font(tmp, DefaultFont);
   return tmp;
}

/*
 * Create a new Entryfield passwird window (widget) to be packed.
 * Parameters:
 *       text: The default text to be in the entryfield widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_entryfield_password_new(const char *text, ULONG id)
{
   HWND tmp = CreateWindowEx(WS_EX_CLIENTEDGE,
                       EDITCLASSNAME,
                       UTF8toWide(text),
                       ES_WANTRETURN | WS_CHILD |
                       ES_PASSWORD | WS_BORDER | WS_VISIBLE |
                       ES_AUTOHSCROLL | WS_CLIPCHILDREN,
                       0,0,0,0,
                       DW_HWND_OBJECT,
                       (HMENU)(uintptr_t)id,
                       _DWInstance,
                       NULL);

   _dw_window_new_cinfo(tmp, TRUE);
   dw_window_set_font(tmp, DefaultFont);
   return tmp;
}

BOOL CALLBACK _dw_subclass_child(HWND handle, LPARAM lp)
{
   ColorInfo *cinfo = (ColorInfo *)lp;

   if(cinfo)
   {
      cinfo->buddy = handle;
      cinfo->pOldProc = SubclassWindow(handle, _dw_colorwndproc);
      SetWindowLongPtr(handle, GWLP_USERDATA, (LONG_PTR)cinfo);
   }
   return FALSE;
}

/*
 * Create a new Combobox window (widget) to be packed.
 * Parameters:
 *       text: The default text to be in the combpbox widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_combobox_new(const char *text, ULONG id)
{
   HWND tmp = CreateWindow(COMBOBOXCLASSNAME,
                     UTF8toWide(text),
                     WS_CHILD | CBS_DROPDOWN | WS_VSCROLL |
                     WS_CLIPCHILDREN | CBS_AUTOHSCROLL | WS_VISIBLE,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);
   ColorInfo *cinfo = (ColorInfo *)calloc(1, sizeof(ColorInfo));
   ColorInfo *cinfo2 = (ColorInfo *)calloc(1, sizeof(ColorInfo));

   if(!cinfo || !cinfo2)
   {
      if(cinfo)
         free(cinfo);
      if(cinfo2)
         free(cinfo2);
      DestroyWindow(tmp);
      return NULL;
   }

   cinfo2->fore = cinfo->fore = -1;
   cinfo2->back = cinfo->back = -1;
   cinfo2->combo = cinfo->combo = tmp;
   EnumChildWindows(tmp, _dw_subclass_child, (LPARAM)cinfo2);
   cinfo->buddy = cinfo2->buddy;

   SetWindowLongPtr(tmp, GWLP_USERDATA, (LONG_PTR)cinfo);
   dw_window_set_font(tmp, DefaultFont);
   SetWindowText(tmp, UTF8toWide(text));
   return tmp;
}

/*
 * Create a new button window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_button_new(const char *text, ULONG id)
{
   HWND tmp = CreateWindow(BUTTONCLASSNAME,
                     UTF8toWide(text),
                     WS_CHILD | BS_PUSHBUTTON |
                     WS_VISIBLE | WS_CLIPCHILDREN,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);
   ColorInfo *cinfo;

   if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
      cinfo->pOldProc = SubclassWindow(tmp, _dw_buttonwndproc);

   dw_window_set_font(tmp, DefaultFont);
   return tmp;
}

#ifdef TOOLBAR
/* Internal function to create a grayscale bitmap from a color one */
void _dw_to_grayscale(HBITMAP hbm, int width, int height)
{
   HDC hdc = CreateCompatibleDC(NULL);
   if (hdc)
   {
      HBITMAP hbmPrev = SelectBitmap(hdc, hbm);
      int x, y;

      for(y=0;y<height;y++)
      {
         for(x=0;x<width;x++)
         {
            COLORREF c = GetPixel(hdc, x, y);
            /* Use half-values then add 127 to make it look washed out */
            int luma = (int)(GetRValue(c)*0.15 + GetGValue(c)*0.3+ GetBValue(c)*0.06) + 127;

            SetPixel(hdc, x, y, RGB(luma,luma,luma));
         }
      }
      SelectBitmap(hdc, hbmPrev);
      DeleteDC(hdc);
   }
}

/* Internal function to create a toolbar based button */
HWND _dw_create_toolbar(const char *text, ULONG id, HICON icon, HBITMAP hbitmap)
{
   HWND tmp;
   HIMAGELIST imlist, dimlist;
   BITMAP bmi = { 0 };
   TBBUTTON tbButtons[] = {
   { MAKELONG(0, 0), id, TBSTATE_ENABLED, TBSTYLE_BUTTON}
   };

   /* Get the bitmap from either the icon or bitmap itself */
   if(hbitmap)
   {
      GetObject(hbitmap, sizeof(BITMAP), &bmi);
      imlist = ImageList_Create(bmi.bmWidth, bmi.bmHeight, ILC_COLOR32, 1, 0);
      ImageList_Add(imlist, hbitmap, NULL);
      dimlist = ImageList_Create(bmi.bmWidth, bmi.bmHeight, ILC_COLOR32, 1, 0);
      _dw_to_grayscale(hbitmap, bmi.bmWidth, bmi.bmHeight);
      ImageList_Add(dimlist, hbitmap, NULL);
      DeleteObject(hbitmap);
   }
   else if(icon)
   {
      ICONINFO iconinfo;

      GetIconInfo(icon, &iconinfo);
      GetObject(iconinfo.hbmColor, sizeof(BITMAP), &bmi);
      imlist = ImageList_Create(bmi.bmWidth, bmi.bmHeight, ILC_COLOR32 | ILC_MASK, 1, 0);
      ImageList_AddIcon(imlist, icon);
      dimlist = ImageList_Create(bmi.bmWidth, bmi.bmHeight, ILC_COLOR32 | ILC_MASK, 1, 0);
      _dw_to_grayscale(iconinfo.hbmColor, bmi.bmWidth, bmi.bmHeight);
      ImageList_Add(dimlist, iconinfo.hbmColor, iconinfo.hbmMask);
      DeleteObject(iconinfo.hbmColor);
      DeleteObject(iconinfo.hbmMask);
      DestroyIcon(icon);
   }
   else
      return 0;

   /* Create the toolbar */
   tmp = CreateWindowEx(0L, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | TBSTYLE_AUTOSIZE | CCS_NORESIZE |
                        CCS_NOPARENTALIGN | CCS_NODIVIDER, 0, 0, 100, 30, DW_HWND_OBJECT, (HMENU)(uintptr_t)id, _DWInstance, NULL);

   /* Disable visual styles by default */
   if(_SetWindowTheme)
      _SetWindowTheme(tmp, L"", L"");

   /* Insert the single bitmap and button into the toolbar */
   SendMessage(tmp, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
   SendMessage(tmp, TB_SETBUTTONSIZE, 0, MAKELPARAM(bmi.bmWidth, bmi.bmHeight));
   SendMessage(tmp, TB_SETPADDING, 0, 0);
   SendMessage(tmp, TB_SETIMAGELIST, 0, (LPARAM)imlist);
   SendMessage(tmp, TB_SETDISABLEDIMAGELIST, 0, (LPARAM)dimlist);
   SendMessage(tmp, TB_ADDBUTTONS, 1, (LPARAM) &tbButtons);

   _dw_create_tooltip(tmp, text);
   return tmp;
}
#endif

/*
 * Create a new bitmap button window (widget) to be packed.
 * Parameters:
 *       text: Bubble help text to be displayed.
 *       id: An ID of a bitmap in the resource file.
 */
HWND API dw_bitmapbutton_new(const char *text, ULONG id)
{
   HICON icon = LoadImage(_DWInstance, MAKEINTRESOURCE(id), IMAGE_ICON, 0, 0, 0);
   HBITMAP hbitmap = icon ? 0 : LoadBitmap(_DWInstance, MAKEINTRESOURCE(id));
   ColorInfo *cinfo;
   HWND tmp;

#ifdef TOOLBAR
   if((tmp = _dw_create_toolbar(text, id, icon, hbitmap)))
   {
      if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
         cinfo->pOldProc = SubclassWindow(tmp, _dw_simplewndproc);
      return tmp;
   }
#endif

   tmp = CreateWindow(BUTTONCLASSNAME,
                  NULL,
                  WS_CHILD | BS_PUSHBUTTON |
                  WS_VISIBLE | WS_CLIPCHILDREN |
                  (icon ? BS_ICON : BS_BITMAP),
                  0,0,0,0,
                  DW_HWND_OBJECT,
                  (HMENU)(uintptr_t)id,
                  _DWInstance,
                  NULL);

   if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
      cinfo->pOldProc = SubclassWindow(tmp, _dw_buttonwndproc);

   _dw_create_tooltip(tmp, text);

   if(icon)
      SendMessage(tmp, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)icon);
   else if(hbitmap)
      SendMessage(tmp, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbitmap);
   return tmp;
}

/*
 * Create a new bitmap button window (widget) to be packed from a file.
 * Parameters:
 *       text: Bubble help text to be displayed.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 *       filename: Name of the file, omit extention to have
 *                 DW pick the appropriate file extension.
 *                 (BMP or ICO on OS/2 or Windows, XPM on Unix)
 */
HWND API dw_bitmapbutton_new_from_file(const char *text, unsigned long id, const char *filename)
{
   HWND tmp;
   ColorInfo *cinfo;
   HBITMAP hbitmap = 0;
   HANDLE hicon = 0;
   int windowtype = 0;

#ifdef GDIPLUS
   if((hicon = _dw_load_icon(filename)))
      windowtype = BS_ICON;
   else
   {
      hbitmap = _dw_load_bitmap(filename, NULL);
      windowtype = BS_BITMAP;
   }
#else
   windowtype = _dw_get_image_handle(filename, &hicon, &hbitmap);
#endif

#ifdef TOOLBAR
   if((tmp = _dw_create_toolbar(text, id, hicon, hbitmap)))
   {
      if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
         cinfo->pOldProc = SubclassWindow(tmp, _dw_simplewndproc);
      return tmp;
   }
#endif
   tmp = CreateWindow( BUTTONCLASSNAME,
                       NULL,
                       windowtype | WS_CHILD | BS_PUSHBUTTON | WS_CLIPCHILDREN | WS_VISIBLE,
                       0,0,0,0,
                       DW_HWND_OBJECT,
                       (HMENU)(uintptr_t)id,
                       _DWInstance,
                       NULL);

   if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
      cinfo->pOldProc = SubclassWindow(tmp, _dw_buttonwndproc);

   _dw_create_tooltip(tmp, text);

   if (hicon)
      SendMessage(tmp, BM_SETIMAGE,(WPARAM)IMAGE_ICON,(LPARAM)hicon);
   else if (hbitmap)
      SendMessage(tmp, BM_SETIMAGE,(WPARAM)IMAGE_BITMAP, (LPARAM)hbitmap);
   return tmp;
}

/*
 * Create a new bitmap button window (widget) to be packed from data.
 * Parameters:
 *       text: Bubble help text to be displayed.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 *       data: The contents of the image
 *            (BMP or ICO on OS/2 or Windows, XPM on Unix)
 *       len: length of str
 */
HWND API dw_bitmapbutton_new_from_data(const char *text, unsigned long id, const char *data, int len)
{
   HWND tmp;
   ColorInfo *cinfo;
   HBITMAP hbitmap = 0;
   HANDLE hicon = 0;
   char *file;
   FILE *fp;
   int windowtype = BS_BITMAP;

   file = _tempnam( _dw_alternate_temp_dir, "dw" );
   if ( file != NULL )
   {
      fp = fopen( file, "wb" );
      if ( fp != NULL )
      {
         fwrite( data, 1, len, fp );
         fclose( fp );
#ifdef GDIPLUS
         if((hicon = _dw_load_icon(file)))
            windowtype = BS_ICON;
         else
            hbitmap = _dw_load_bitmap(file, NULL);
#else
         if ( len > 1 && data[0] == 'B' && data[1] == 'M' ) /* first 2 chars of data is BM, then its a BMP */
            hbitmap = (HBITMAP)LoadImage( NULL, UTF8toWide(file), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
         else /* otherwise its assumed to be an ico */
         {
            hicon = LoadImage( NULL, UTF8toWide(file), IMAGE_ICON, 0, 0, LR_LOADFROMFILE );
            windowtype = BS_ICON;
         }
#endif
      }
      else
      {
         _unlink( file );
         free( file );
         return 0;
      }
      _unlink( file );
      free( file );
   }

#ifdef TOOLBAR
   if((tmp = _dw_create_toolbar(text, id, hicon, hbitmap)))
   {
      if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
         cinfo->pOldProc = SubclassWindow(tmp, _dw_simplewndproc);
      return tmp;
   }
#endif
   tmp = CreateWindow( BUTTONCLASSNAME,
                       NULL,
                       WS_CHILD | BS_PUSHBUTTON |
                       windowtype | WS_CLIPCHILDREN |
                       WS_VISIBLE,
                       0,0,0,0,
                       DW_HWND_OBJECT,
                       (HMENU)(uintptr_t)id,
                       _DWInstance,
                       NULL );

   if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
      cinfo->pOldProc = SubclassWindow( tmp, _dw_buttonwndproc );

   _dw_create_tooltip(tmp, text);

   if(hicon)
      SendMessage(tmp, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon);
   else if(hbitmap)
      SendMessage(tmp, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbitmap);
   return tmp;
}

/*
 * Create a new spinbutton window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_spinbutton_new(const char *text, ULONG id)
{
   HWND buddy = CreateWindowEx(WS_EX_CLIENTEDGE,
                        EDITCLASSNAME,
                        UTF8toWide(text),
                        WS_CHILD | WS_BORDER | WS_VISIBLE |
                        ES_NUMBER | WS_CLIPCHILDREN,
                        0,0,0,0,
                        DW_HWND_OBJECT,
                        NULL,
                        _DWInstance,
                        NULL);
   HWND tmp = CreateWindowEx(WS_EX_CLIENTEDGE,
                       UPDOWN_CLASS,
                       NULL,
                       WS_CHILD | UDS_ALIGNRIGHT | WS_BORDER |
                       UDS_ARROWKEYS | UDS_SETBUDDYINT |
                       UDS_WRAP | UDS_NOTHOUSANDS | WS_VISIBLE,
                       0,0,0,0,
                       DW_HWND_OBJECT,
                       (HMENU)(uintptr_t)id,
                       _DWInstance,
                       NULL);
   ColorInfo *cinfo;

   SendMessage(tmp, UDM_SETRANGE32, (WPARAM)-65536,(LPARAM)65536);
   SendMessage(tmp, UDM_SETBUDDY, (WPARAM)buddy, 0);
   
   if((cinfo = _dw_window_new_cinfo(buddy, TRUE)))
      cinfo->buddy = tmp;

   if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
   {
      cinfo->buddy = buddy;

      /* The horrible spinbutton workaround isn't necessary
       * any more on Vista or 7... but still seems necessary
       * for XP, so only enable it if on XP or lower.
       */
      if(!IS_VISTAPLUS)
         cinfo->pOldProc = SubclassWindow(tmp, _spinnerwndproc);
   }

   dw_window_set_font(buddy, DefaultFont);
   return tmp;
}

/*
 * Create a new radiobutton window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_radiobutton_new(const char *text, ULONG id)
{
   ColorInfo *cinfo;
   HWND tmp = CreateWindow(BUTTONCLASSNAME,
                     UTF8toWide(text),
                     WS_CHILD | BS_AUTORADIOBUTTON |
                     WS_CLIPCHILDREN | WS_VISIBLE,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);

   /* Disable visual styles by default */
   if(_SetWindowTheme)
      _SetWindowTheme(tmp, L"", L"");

   if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
      cinfo->pOldProc = SubclassWindow(tmp, _dw_buttonwndproc);
   dw_window_set_font(tmp, DefaultFont);
   dw_window_set_color(tmp, DW_CLR_DEFAULT, DW_RGB_TRANSPARENT);
   return tmp;
}


/*
 * Create a new slider window (widget) to be packed.
 * Parameters:
 *       vertical: TRUE or FALSE if slider is vertical.
 *       increments: Number of increments available.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_slider_new(int vertical, int increments, ULONG id)
{
   HWND tmp = CreateWindow(TRACKBAR_CLASS,
                     NULL,
                     WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE |
                     (vertical ? TBS_VERT : TBS_HORZ),
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);

   _dw_window_new_cinfo(tmp, TRUE);
   SendMessage(tmp, TBM_SETRANGE, (WPARAM)FALSE, MAKELPARAM(0, increments-1));
   return tmp;
}

/*
 * Create a new scrollbar window (widget) to be packed.
 * Parameters:
 *       vertical: TRUE or FALSE if scrollbar is vertical.
 *       increments: Number of increments available.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_scrollbar_new(int vertical, ULONG id)
{
   HWND tmp = CreateWindow(SCROLLBARCLASSNAME,
                     NULL,
                     WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE |
                     (vertical ? SBS_VERT : SBS_HORZ),
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);

   _dw_window_new_cinfo(tmp, TRUE);
   dw_window_set_data(tmp, "_dw_scrollbar", (void *)1);
   return tmp;
}

/*
 * Create a new percent bar window (widget) to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_percent_new(ULONG id)
{
   return CreateWindow(PROGRESS_CLASS,
                  NULL,
                  WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN,
                  0,0,0,0,
                  DW_HWND_OBJECT,
                  (HMENU)(uintptr_t)id,
                  _DWInstance,
                  NULL);
}

/*
 * Create a new checkbox window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_checkbox_new(const char *text, ULONG id)
{
   ColorInfo *cinfo;
   HWND tmp = CreateWindow(BUTTONCLASSNAME,
                     UTF8toWide(text),
                     WS_CHILD | BS_AUTOCHECKBOX |
                     BS_TEXT | WS_CLIPCHILDREN | WS_VISIBLE,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);

   /* Disable visual styles by default */
   if(_SetWindowTheme)
      _SetWindowTheme(tmp, L"", L"");

   if((cinfo = _dw_window_new_cinfo(tmp, FALSE)))
      cinfo->pOldProc = SubclassWindow(tmp, _dw_buttonwndproc);
   dw_window_set_data(tmp, "_dw_checkbox", DW_INT_TO_POINTER(1));
   dw_window_set_font(tmp, DefaultFont);
   dw_window_set_color(tmp, DW_CLR_DEFAULT, DW_RGB_TRANSPARENT);
   return tmp;
}

/*
 * Create a new listbox window (widget) to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id() or 0L.
 *       multi: Multiple select TRUE or FALSE.
 */
HWND API dw_listbox_new(ULONG id, int multi)
{
   HWND tmp = CreateWindowEx(WS_EX_CLIENTEDGE,
                       LISTBOXCLASSNAME,
                       NULL,
                       WS_VISIBLE | LBS_NOINTEGRALHEIGHT |
                       WS_CHILD | LBS_HASSTRINGS |
                       LBS_NOTIFY | WS_BORDER  | WS_CLIPCHILDREN |
                       WS_VSCROLL | (multi ? LBS_MULTIPLESEL : 0) ,
                       0,0,0,0,
                       DW_HWND_OBJECT,
                       (HMENU)(uintptr_t)id,
                       _DWInstance,
                       NULL);
   ContainerInfo *cinfo = (ContainerInfo *)calloc(1, sizeof(ContainerInfo));

   if(!cinfo)
   {
      DestroyWindow(tmp);
      return NULL;
   }

   cinfo->cinfo.fore = cinfo->cinfo.back = -1;
   cinfo->odd = cinfo->even = DW_RGB_TRANSPARENT;
   cinfo->cinfo.pOldProc = SubclassWindow(tmp, _dw_containerwndproc);

   SetWindowLongPtr(tmp, GWLP_USERDATA, (LONG_PTR)cinfo);
   dw_window_set_font(tmp, DefaultFont);
   return tmp;
}

/*
 * Sets the icon used for a given window.
 * Parameters:
 *       handle: Handle to the window.
 *       id: An ID to be used to specify the icon.
 */
void API dw_window_set_icon(HWND handle, HICN icon)
{
   int iicon = DW_POINTER_TO_INT(icon);
   HICON hicon = iicon < 65536 ? LoadIcon(_DWInstance, MAKEINTRESOURCE(iicon)) : (HICON)icon;

   SendMessage(handle, WM_SETICON,
            (WPARAM) IMAGE_ICON,
            (LPARAM) hicon);
}

/* Internal function to set bitmap for the next two functions */
int _dw_window_set_bitmap(HWND handle, HICON icon, HBITMAP hbitmap)
{
   HBITMAP oldbitmap = 0;
   HANDLE oldicon = 0;
   TCHAR tmpbuf[100] = {0};

   if (!icon && !hbitmap)
      return DW_ERROR_GENERAL;

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, BUTTONCLASSNAME, _tcslen(BUTTONCLASSNAME)+1)==0)
   {
      oldbitmap = (HBITMAP)SendMessage(handle, BM_GETIMAGE, IMAGE_BITMAP, 0);
      oldicon = (HICON)SendMessage(handle, BM_GETIMAGE, IMAGE_ICON, 0);
      SendMessage(handle, BM_SETIMAGE,
               (icon ? (WPARAM)IMAGE_ICON : (WPARAM)IMAGE_BITMAP),
               (icon ? (LPARAM)icon : (LPARAM)hbitmap));
   }
#ifdef TOOLBAR
   /* Bitmap Buttons */
   else if(_tcsnicmp(tmpbuf, TOOLBARCLASSNAME, _tcslen(TOOLBARCLASSNAME)+1) == 0)
   {
      HIMAGELIST imlist = (HIMAGELIST)SendMessage(handle, TB_GETIMAGELIST, 0, 0);
      HIMAGELIST dimlist = (HIMAGELIST)SendMessage(handle, TB_GETDISABLEDIMAGELIST, 0, 0);
      BITMAP bmi = { 0 };

      if(hbitmap)
      {
         GetObject(hbitmap, sizeof(BITMAP), &bmi);
         ImageList_Replace(imlist, 0, hbitmap, NULL);
         _dw_to_grayscale(hbitmap, bmi.bmWidth, bmi.bmHeight);
         ImageList_Replace(dimlist, 0, hbitmap, NULL);
         DeleteObject(hbitmap);
      }
      else if(icon)
      {
         ICONINFO iconinfo;

         GetIconInfo(icon, &iconinfo);
         GetObject(iconinfo.hbmColor, sizeof(BITMAP), &bmi);
         ImageList_ReplaceIcon(imlist, 0, icon);
         _dw_to_grayscale(iconinfo.hbmColor, bmi.bmWidth, bmi.bmHeight);
         ImageList_Replace(dimlist, 0, iconinfo.hbmColor, iconinfo.hbmMask);
         DeleteObject(iconinfo.hbmColor);
         DeleteObject(iconinfo.hbmMask);
         DestroyIcon(icon);
      }
      InvalidateRect(handle, NULL, FALSE);
   }
#endif
   else
   {
      oldbitmap = (HBITMAP)SendMessage(handle, STM_GETIMAGE, IMAGE_BITMAP, 0);
      oldicon = (HICON)SendMessage(handle, STM_GETIMAGE, IMAGE_ICON, 0);
      SendMessage(handle, STM_SETIMAGE,
               (icon ? (WPARAM)IMAGE_ICON : (WPARAM)IMAGE_BITMAP),
               (icon ? (LPARAM)icon : (LPARAM)hbitmap));
   }

   if(oldbitmap)
      DeleteObject(oldbitmap);
   if(oldicon)
      DeleteObject(oldicon);

   /* If we changed the bitmap... */
   {
      Item *item = _dw_box_item(handle);

      /* Check to see if any of the sizes need to be recalculated */
      if(item && (item->origwidth == DW_SIZE_AUTO || item->origheight == DW_SIZE_AUTO))
      {
         _dw_control_size(handle, item->origwidth == DW_SIZE_AUTO ? &item->width : NULL, item->origheight == DW_SIZE_AUTO ? &item->height : NULL);
         /* Queue a redraw on the top-level window */
         _dw_redraw(_dw_toplevel_window(handle), TRUE);
      }
   }
   return DW_ERROR_NONE;
}

/*
 * Sets the bitmap used for a given static window.
 * Parameters:
 *       handle: Handle to the window.
 *       id: An ID to be used to specify the icon,
 *           (pass 0 if you use the filename param)
 *       filename: a path to a file (Bitmap on OS/2 or
 *                 Windows and a pixmap on Unix, pass
 *                 NULL if you use the id param)
 * Returns:
 *        DW_ERROR_NONE on success.
 *        DW_ERROR_UNKNOWN if the parameters were invalid.
 *        DW_ERROR_GENERAL if the bitmap was unable to be loaded.
 */
int API dw_window_set_bitmap(HWND handle, unsigned long id, const char *filename)
{
   HBITMAP hbitmap = 0;
   HANDLE icon = 0;

   if(id)
   {
      hbitmap = LoadImage(_DWInstance, MAKEINTRESOURCE(id), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS | LR_SHARED);
      icon = LoadImage(_DWInstance, MAKEINTRESOURCE(id), IMAGE_ICON, 0, 0, LR_SHARED);
   }
   else if(filename)
   {
#ifdef GDIPLUS
      hbitmap = _dw_load_bitmap(filename, NULL);
#else
      _dw_get_image_handle(filename, &icon, &hbitmap);
#endif
   }
   else
      return DW_ERROR_UNKNOWN;

   return _dw_window_set_bitmap(handle, icon, hbitmap);
}

/*
 * Sets the bitmap used for a given static window from data.
 * Parameters:
 *       handle: Handle to the window.
 *       id: An ID to be used to specify the icon,
 *           (pass 0 if you use the data param)
 *       data: the image from meory
 *                 Bitmap on Windows and a pixmap on Unix, pass
 *                 NULL if you use the id param)
 *       len: length of data
 * Returns:
 *        DW_ERROR_NONE on success.
 *        DW_ERROR_UNKNOWN if the parameters were invalid.
 *        DW_ERROR_GENERAL if the bitmap was unable to be loaded.
 */
int API dw_window_set_bitmap_from_data(HWND handle, unsigned long id, const char *data, int len)
{
   HBITMAP hbitmap=0;
   HICON icon=0;
   char *file;
   FILE *fp;

   if (data)
   {
      file = _tempnam( _dw_alternate_temp_dir, "dw" );
      if ( file != NULL )
      {
         fp = fopen( file, "wb" );
         if ( fp != NULL )
         {
            fwrite( data, 1, len, fp );
            fclose( fp );
#ifdef GDIPLUS
            hbitmap = _dw_load_bitmap(file, NULL);
#else
            if ( len > 1 && data[0] == 'B' && data[1] == 'M' ) /* first 2 chars of data is BM, then its a BMP */
               hbitmap = (HBITMAP)LoadImage( NULL, UTF8toWide(file), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
            else /* otherwise its assumed to be an ico */
               icon = LoadImage( NULL, UTF8toWide(file), IMAGE_ICON, 0, 0, LR_LOADFROMFILE );
#endif
         }
         else
         {
            _unlink( file );
            free( file );
            return DW_ERROR_GENERAL;
         }
         _unlink( file );
         free( file );
      }
      if (icon == 0 && hbitmap == 0)
         return DW_ERROR_GENERAL;
   }
   else if ( id )
   {
      hbitmap = LoadBitmap(_DWInstance, MAKEINTRESOURCE(id));
      icon = LoadImage(_DWInstance, MAKEINTRESOURCE(id), IMAGE_ICON, 0, 0, LR_SHARED);
   }
   else
      return DW_ERROR_UNKNOWN;

   return _dw_window_set_bitmap(handle, icon, hbitmap);
}


/*
 * Sets the text used for a given window.
 * Parameters:
 *       handle: Handle to the window.
 *       text: The text associsated with a given window.
 */
void API dw_window_set_text(HWND handle, const char *text)
{
   Box *thisbox;
   TCHAR tmpbuf[100] = {0}, *wtext = UTF8toWide(text);

   GetClassName(handle, tmpbuf, 99);

   SetWindowText(handle, wtext);

   /* Combobox */
   if(_tcsnicmp( tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1) == 0)
      SendMessage(handle, CB_SETEDITSEL, 0, MAKELPARAM(-1, 0));
   else if(_tcsnicmp( tmpbuf, UPDOWN_CLASS, _tcslen(UPDOWN_CLASS)+1) == 0)
   {
      ColorInfo *cinfo = _dw_window_get_cinfo(handle);
      if(cinfo && cinfo->buddy)
         SetWindowText(cinfo->buddy, wtext);
   }
   else if(_tcsnicmp( tmpbuf, FRAMECLASSNAME, _tcslen(FRAMECLASSNAME)+1) == 0)
   {
      /* groupbox */
      thisbox = (Box *)GetWindowLongPtr(handle, GWLP_USERDATA);
      if(thisbox && thisbox->grouphwnd != (HWND)NULL)
         SetWindowText(thisbox->grouphwnd, wtext);
   }
   /* If we changed the text... */
   {
      Item *item = _dw_box_item(handle);

      /* Check to see if any of the sizes need to be recalculated */
      if(item && (item->origwidth == DW_SIZE_AUTO || item->origheight == DW_SIZE_AUTO))
      {
         int newwidth, newheight;

         _dw_control_size(handle, &newwidth, &newheight);

         /* Only update the item and redraw the window if it changed */
         if((item->origwidth == DW_SIZE_AUTO && item->width != newwidth) ||
            (item->origheight == DW_SIZE_AUTO && item->height != newheight))
         {
            if(item->origwidth == DW_SIZE_AUTO)
               item->width = newwidth;
            if(item->origheight == DW_SIZE_AUTO)
               item->height = newheight;
            /* Queue a redraw on the top-level window */
            _dw_redraw(_dw_toplevel_window(handle), TRUE);
         }
      }
   }
}

/*
 * Sets the text used for a given window's floating bubble help.
 * Parameters:
 *       handle: Handle to the window (widget).
 *       bubbletext: The text in the floating bubble tooltip.
 */
void API dw_window_set_tooltip(HWND handle, const char *bubbletext)
{
    ColorInfo *cinfo = _dw_window_get_cinfo(handle);

    if(cinfo && cinfo->buddy)
        _dw_create_tooltip(cinfo->buddy, bubbletext);
    _dw_create_tooltip(handle, bubbletext);
}

/*
 * Gets the text used for a given window.
 * Parameters:
 *       handle: Handle to the window.
 * Returns:
 *       text: The text associsated with a given window.
 */
char * API dw_window_get_text(HWND handle)
{
   char *retbuf = NULL;
   TCHAR *tempbuf, tmpbuf[100] = { 0 };
   int len;

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp( tmpbuf, UPDOWN_CLASS, _tcslen(UPDOWN_CLASS)+1) == 0)
   {
      ColorInfo *cinfo = (ColorInfo *)GetWindowLongPtr(handle, GWLP_USERDATA);

      if(cinfo && cinfo->buddy)
        handle = cinfo->buddy;
      else
        return NULL;
   }

   /* Figure out the wide length, allocate a temp buffer
    * and fill it with the current text.
    */
   len = GetWindowTextLength(handle) + 1;
   if((tempbuf = _alloca(len * sizeof(TCHAR))))
      GetWindowText(handle, tempbuf, len);

   /* Figure out the UTF8 length, allocate a return buffer
    * and fill it with the UTF8 text and return it.
    */
   if(tempbuf && (retbuf = WideToUTF8(tempbuf)))
      retbuf = _strdup(retbuf);
   return retbuf;
}

/*
 * Disables given window (widget).
 * Parameters:
 *       handle: Handle to the window.
 */
void API dw_window_disable(HWND handle)
{
   if(handle < (HWND)65536)
   {
      char buffer[31] = {0};
      HMENU mymenu;
      ULONG id = (ULONG)(uintptr_t)handle;

      _snprintf(buffer, 30, "_dw_id%ld", id);
      mymenu = (HMENU)dw_window_get_data(DW_HWND_OBJECT, buffer);

      if(mymenu && IsMenu(mymenu))
         dw_menu_item_set_state((HMENUI)mymenu, id, DW_MIS_DISABLED);
   }
   else
      EnableWindow(handle, FALSE);
}

/*
 * Enables given window (widget).
 * Parameters:
 *       handle: Handle to the window.
 */
void API dw_window_enable(HWND handle)
{
   if(handle < (HWND)65536)
   {
      char buffer[31] = {0};
      HMENU mymenu;
      ULONG id = (ULONG)(uintptr_t)handle;

      _snprintf(buffer, 30, "_dw_id%ld", id);
      mymenu = (HMENU)dw_window_get_data(DW_HWND_OBJECT, buffer);

      if(mymenu && IsMenu(mymenu))
         dw_menu_item_set_state((HMENUI)mymenu, id, DW_MIS_ENABLED);
   }
   else
   EnableWindow(handle, TRUE);
}

static HWND _dw_wfid_hwnd = NULL;

BOOL CALLBACK _dw_wfid(HWND handle, LPARAM lParam)
{
   if(GetWindowLong(handle, GWL_ID) == lParam)
   {
      _dw_wfid_hwnd = handle;
      return FALSE;
   }
   return TRUE;
}

/*
 * Gets the child window handle with specified ID.
 * Parameters:
 *       handle: Handle to the parent window.
 *       id: Integer ID of the child.
 */
HWND API dw_window_from_id(HWND handle, int id)
{
   _dw_wfid_hwnd = NULL;
   EnumChildWindows(handle, _dw_wfid, (LPARAM)id);
    return _dw_wfid_hwnd;
}

/* Internal box packing function called by the other 3 functions */
void _dw_box_pack(HWND box, HWND item, int index, int width, int height, int hsize, int vsize, int pad, char *funcname)
{
   Box *thisbox = NULL;
   TCHAR tmpbuf[100] = {0};

      /*
       * If you try and pack an item into itself VERY bad things can happen; like at least an
       * infinite loop on GTK! Lets be safe!
       */
   if(box == item)
   {
      dw_messagebox(funcname, DW_MB_OK|DW_MB_ERROR, "Danger! Danger! Will Robinson; box and item are the same!");
      return;
   }

   GetClassName(box, tmpbuf, 99);

   /* If we are in a scrolled box... extract the interal box */
   if(_tcsnicmp(tmpbuf, ScrollClassName, _tcslen(ScrollClassName)+1)==0)
   {
        ColorInfo *cinfo = _dw_window_get_cinfo(box);
        if(cinfo)
        {
            box = cinfo->buddy;
            thisbox = (Box *)GetWindowLongPtr(box, GWLP_USERDATA);
        }
   }
   else //if(_tcsnicmp(tmpbuf, FRAMECLASSNAME, _tcslen(FRAMECLASSNAME)+1)==0)
       thisbox = (Box *)GetWindowLongPtr(box, GWLP_USERDATA);
   if(thisbox)
   {
      int z, x = 0;
      Item *tmpitem, *thisitem = thisbox->items;

      /* Do some sanity bounds checking */
      if(!thisitem)
          thisbox->count = 0;
      if(index < 0)
        index = 0;
      if(index > thisbox->count)
        index = thisbox->count;

      tmpitem = calloc(sizeof(Item), (thisbox->count+1));

      for(z=0;z<thisbox->count;z++)
      {
         if(z == index)
            x++;
         tmpitem[x] = thisitem[z];
         x++;
      }

      GetClassName(item, tmpbuf, 99);

      if(vsize && !height)
         height = 1;
      if(hsize && !width)
         width = 1;

      if(_tcsnicmp(tmpbuf, FRAMECLASSNAME, _tcslen(FRAMECLASSNAME)+1)==0)
         tmpitem[index].type = _DW_TYPE_BOX;
      else
      {
         if(_tcsnicmp(tmpbuf, TEXT("SysMonthCal32"), 13)==0)
         {
            RECT rc;
            MonthCal_GetMinReqRect(item, &rc);
            width = 1 + rc.right - rc.left;
            height = 1 + rc.bottom - rc.top;
            tmpitem[index].type = _DW_TYPE_ITEM;
         }
         else
         {
            if ( width == 0 && hsize == FALSE )
               dw_messagebox(funcname, DW_MB_OK|DW_MB_ERROR, "Width and expand Horizonal both unset for box: %x item: %x",box,item);
            if ( height == 0 && vsize == FALSE )
               dw_messagebox(funcname, DW_MB_OK|DW_MB_ERROR, "Height and expand Vertical both unset for box: %x item: %x",box,item);

            tmpitem[index].type = _DW_TYPE_ITEM;
         }
      }

      tmpitem[index].hwnd = item;
      tmpitem[index].origwidth = tmpitem[index].width = width;
      tmpitem[index].origheight = tmpitem[index].height = height;
      tmpitem[index].pad = pad;
      tmpitem[index].hsize = hsize ? _DW_SIZE_EXPAND : _DW_SIZE_STATIC;
      tmpitem[index].vsize = vsize ? _DW_SIZE_EXPAND : _DW_SIZE_STATIC;

      /* If either of the parameters are -1 (DW_SIZE_AUTO) ... calculate the size */
      if(width == DW_SIZE_AUTO || height == DW_SIZE_AUTO)
         _dw_control_size(item, width == DW_SIZE_AUTO ? &tmpitem[index].width : NULL, height == DW_SIZE_AUTO ? &tmpitem[index].height : NULL);

      thisbox->items = tmpitem;

      if(thisitem)
         free(thisitem);

      thisbox->count++;

#ifdef AEROGLASS
      _dw_allow_dark_mode_for_window(item, _DW_DARK_MODE_ENABLED);
#endif
      SetParent(item, box);
      if(_tcsnicmp(tmpbuf, UPDOWN_CLASS, _tcslen(UPDOWN_CLASS)+1)==0)
      {
         ColorInfo *cinfo = _dw_window_get_cinfo(item);

         if(cinfo)
         {
            SetParent(cinfo->buddy, box);
            ShowWindow(cinfo->buddy, SW_SHOW);
            SendMessage(item, UDM_SETBUDDY, (WPARAM)cinfo->buddy, 0);
         }
      }
#ifdef TOOLBAR
      else if(_tcsnicmp(tmpbuf, TOOLBARCLASSNAME, _tcslen(TOOLBARCLASSNAME)+1) == 0)
      {
#ifdef AEROGLASS	
         if(!(_dw_composition && (GetWindowLongPtr(_dw_toplevel_window(box), GWL_EXSTYLE) & WS_EX_LAYERED)))
#endif
         {
            /* Enable double buffering if our window isn't composited */
#ifdef TBSTYLE_EX_DOUBLEBUFFER
            SendMessage(item, TB_SETEXTENDEDSTYLE, 0, (LPARAM)TBSTYLE_EX_DOUBLEBUFFER);
#endif
         }
      }
#endif
      /* Queue a redraw on the top-level window */
      _dw_redraw(_dw_toplevel_window(box), TRUE);
   }
}

/*
 * Remove windows (widgets) from the box they are packed into.
 * Parameters:
 *       handle: Window handle of the packed item to be removed.
 * Returns:
 *       DW_ERROR_NONE on success and DW_ERROR_GENERAL on failure.
 */
int API dw_box_unpack(HWND handle)
{
   HWND parent = GetParent(handle);

   if(handle && parent != HWND_DESKTOP)
   {
      Box *thisbox = (Box *)GetWindowLongPtr(parent, GWLP_USERDATA);

      /* If the parent box has items...
       * try to remove it from the layout
       */
      if(thisbox && thisbox->count)
      {
         int z, index = -1;
         Item *tmpitem = NULL, *thisitem = thisbox->items;

         if(!thisitem)
             thisbox->count = 0;

         for(z=0;z<thisbox->count;z++)
         {
            if(thisitem[z].hwnd == handle)
               index = z;
         }

         if(index == -1)
            return DW_ERROR_GENERAL;

         if(thisbox->count > 1)
         {
            tmpitem = calloc(sizeof(Item), (thisbox->count-1));

            /* Copy all but the current entry to the new list */
            for(z=0;z<index;z++)
            {
               tmpitem[z] = thisitem[z];
            }
            for(z=index+1;z<thisbox->count;z++)
            {
               tmpitem[z-1] = thisitem[z];
            }
         }

         thisbox->items = tmpitem;
         if(thisitem)
            free(thisitem);
         if(tmpitem)
            thisbox->count--;
         else
            thisbox->count = 0;

         SetParent(handle, DW_HWND_OBJECT);
         /* Queue a redraw on the top-level window */
         _dw_redraw(_dw_toplevel_window(parent), TRUE);
         return DW_ERROR_NONE;
      }
   }
   return DW_ERROR_GENERAL;
}

/*
 * Remove windows (widgets) from a box at an arbitrary location.
 * Parameters:
 *       box: Window handle of the box to be removed from.
 *       index: 0 based index of packed items.
 * Returns:
 *       Handle to the removed item on success, 0 on failure or padding.
 */
HWND API dw_box_unpack_at_index(HWND box, int index)
{
   Box *thisbox = (Box *)GetWindowLongPtr(box, GWLP_USERDATA);

   /* Try to remove it from the layout */
   if(thisbox && index > -1 && index < thisbox->count)
   {
      int z;
      Item *tmpitem = NULL, *thisitem = thisbox->items;
      HWND handle = thisitem[index].hwnd;

      if(thisbox->count > 1)
      {
         tmpitem = calloc(sizeof(Item), (thisbox->count-1));

         /* Copy all but the current entry to the new list */
         for(z=0;z<index;z++)
         {
            tmpitem[z] = thisitem[z];
         }
         for(z=index+1;z<thisbox->count;z++)
         {
            tmpitem[z-1] = thisitem[z];
         }
      }

      thisbox->items = tmpitem;
      if(thisitem)
         free(thisitem);
      if(tmpitem)
         thisbox->count--;
      else
         thisbox->count = 0;

      /* If it isn't padding, reset the parent */
      if(handle)
         SetParent(handle, DW_HWND_OBJECT);
      /* Queue a redraw on the top-level window */
      _dw_redraw(_dw_toplevel_window(box), TRUE);
      return handle;
   }
   return 0;
}

/*
 * Pack windows (widgets) into a box at an arbitrary location.
 * Parameters:
 *       box: Window handle of the box to be packed into.
 *       item: Window handle of the item to pack.
 *       index: 0 based index of packed items.
 *       width: Width in pixels of the item or -1 to be self determined.
 *       height: Height in pixels of the item or -1 to be self determined.
 *       hsize: TRUE if the window (widget) should expand horizontally to fill space given.
 *       vsize: TRUE if the window (widget) should expand vertically to fill space given.
 *       pad: Number of pixels of padding around the item.
 */
void API dw_box_pack_at_index(HWND box, HWND item, int index, int width, int height, int hsize, int vsize, int pad)
{
    _dw_box_pack(box, item, index, width, height, hsize, vsize, pad, "dw_box_pack_at_index()");
}

/*
 * Pack windows (widgets) into a box from the start (or top).
 * Parameters:
 *       box: Window handle of the box to be packed into.
 *       item: Window handle of the item to pack.
 *       width: Width in pixels of the item or -1 to be self determined.
 *       height: Height in pixels of the item or -1 to be self determined.
 *       hsize: TRUE if the window (widget) should expand horizontally to fill space given.
 *       vsize: TRUE if the window (widget) should expand vertically to fill space given.
 *       pad: Number of pixels of padding around the item.
 */
void API dw_box_pack_start(HWND box, HWND item, int width, int height, int hsize, int vsize, int pad)
{
    /* 65536 is the table limit on GTK...
     * seems like a high enough value we will never hit it here either.
     */
    _dw_box_pack(box, item, 65536, width, height, hsize, vsize, pad, "dw_box_pack_start()");
}

/*
 * Pack windows (widgets) into a box from the end (or bottom).
 * Parameters:
 *       box: Window handle of the box to be packed into.
 *       item: Window handle of the item to pack.
 *       width: Width in pixels of the item or -1 to be self determined.
 *       height: Height in pixels of the item or -1 to be self determined.
 *       hsize: TRUE if the window (widget) should expand horizontally to fill space given.
 *       vsize: TRUE if the window (widget) should expand vertically to fill space given.
 *       pad: Number of pixels of padding around the item.
 */
void API dw_box_pack_end(HWND box, HWND item, int width, int height, int hsize, int vsize, int pad)
{
    _dw_box_pack(box, item, 0, width, height, hsize, vsize, pad, "dw_box_pack_end()");
}

/*
 * The following is an attempt to dynamically size a window based on the size of its
 * children before realization. Only applicable when width or height is less than one.
 */
void _dw_get_window_for_size(HWND handle, unsigned long *width, unsigned long *height)
{
   Box *thisbox = (Box *)GetWindowLongPtr(handle, GWLP_USERDATA);

   if(thisbox)
   {
      int depth = 0;
      DWORD dwStyle = GetWindowLong(handle, GWL_STYLE);
      DWORD dwExStyle = GetWindowLong(handle, GWL_EXSTYLE);
      HMENU menu = GetMenu(handle) ;
      RECT rc = { 0 } ;

      /* Calculate space requirements */
      _dw_resize_box(thisbox, &depth, *width, *height, 0, 0, 1);

      rc.right = thisbox->minwidth;
      rc.bottom = thisbox->minheight;

      /* Take into account the window border and menu here */
      AdjustWindowRectEx(&rc, dwStyle, menu ? TRUE : FALSE, dwExStyle);

      if ( *width < 1 ) *width = rc.right - rc.left;
      if ( *height < 1 ) *height = rc.bottom - rc.top;
   }
}

/*
 * Sets the size of a given window (widget).
 * Parameters:
 *          handle: Window (widget) handle.
 *          width: New width in pixels.
 *          height: New height in pixels.
 */
void API dw_window_set_size(HWND handle, ULONG width, ULONG height)
{
   /* Attempt to auto-size */
   if ( width < 1 || height < 1 )
      _dw_get_window_for_size(handle, &width, &height);

   /* Finally set the size */
   SetWindowPos(handle, (HWND)NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
}

/*
 * Gets the size the system thinks the widget should be.
 * Parameters:
 *       handle: Window handle of the item to be back.
 *       width: Width in pixels of the item or NULL if not needed.
 *       height: Height in pixels of the item or NULL if not needed.
 */
void API dw_window_get_preferred_size(HWND handle, int *width, int *height)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, ClassName, _tcslen(ClassName)+1) == 0)
   {
      unsigned long thiswidth = 0, thisheight = 0;

      /* Get the size with the border */
      _dw_get_window_for_size(handle, &thiswidth, &thisheight);

      /* Return what was requested */
      if(width) *width = (int)thiswidth;
      if(height) *height = (int)thisheight;
   }
   else if(_tcsnicmp(tmpbuf, FRAMECLASSNAME, _tcslen(FRAMECLASSNAME)+1) == 0)
   {
      Box *thisbox = (Box *)GetWindowLongPtr(handle, GWLP_USERDATA);

      if(thisbox)
      {
         int depth = 0;

         /* Calculate space requirements */
         _dw_resize_box(thisbox, &depth, 0, 0, 0, 0, 1);

         /* Return what was requested */
         if(width) *width = thisbox->minwidth;
         if(height) *height = thisbox->minheight;
      }
   }
   else
      _dw_control_size(handle, width, height);
}

/*
 * Returns the width of the screen.
 */
int API dw_screen_width(void)
{
   return GetSystemMetrics(SM_CXSCREEN);
}

/*
 * Returns the height of the screen.
 */
int API dw_screen_height(void)
{
   return GetSystemMetrics(SM_CYSCREEN);
}

/* This should return the current color depth */
unsigned long API dw_color_depth_get(void)
{
   int bpp;
   HDC hdc = GetDC(HWND_DESKTOP);

   bpp = GetDeviceCaps(hdc, BITSPIXEL);

   ReleaseDC(HWND_DESKTOP, hdc);

   return bpp;
}

/*
 * Sets the gravity of a given window (widget).
 * Gravity controls which corner of the screen and window the position is relative to.
 * Parameters:
 *          handle: Window (widget) handle.
 *          horz: DW_GRAV_LEFT (default), DW_GRAV_RIGHT or DW_GRAV_CENTER.
 *          vert: DW_GRAV_TOP (default), DW_GRAV_BOTTOM or DW_GRAV_CENTER.
 */
void API dw_window_set_gravity(HWND handle, int horz, int vert)
{
   dw_window_set_data(handle, "_dw_grav_horz", DW_INT_TO_POINTER(horz));
   dw_window_set_data(handle, "_dw_grav_vert", DW_INT_TO_POINTER(vert));
}

/* Convert the coordinates based on gravity */
void _dw_handle_gravity(HWND handle, long *x, long *y, unsigned long width, unsigned long height)
{
   int horz = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_grav_horz"));
   int vert = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_grav_vert"));

   /* Do any gravity calculations */
   if(horz || vert)
   {
      long newx = *x, newy = *y;

      /* Handle horizontal center gravity */
      if((horz & 0xf) == DW_GRAV_CENTER)
         newx += ((dw_screen_width() / 2) - (width / 2));
      /* Handle right gravity */
      else if((horz & 0xf) == DW_GRAV_RIGHT)
         newx = dw_screen_width() - width - *x;
      /* Handle vertical center gravity */
      if((vert & 0xf) == DW_GRAV_CENTER)
         newy += ((dw_screen_height() / 2) - (height / 2));
      else if((vert & 0xf) == DW_GRAV_BOTTOM)
         newy = dw_screen_height() - height - *y;

      /* Save the new values */
      *x = newx;
      *y = newy;

       /* Adjust the values to avoid Taskbar if requested */
       if((horz | vert) & DW_GRAV_OBSTACLES)
       {
         POINT pt = { 0, 0 };
         HMONITOR mon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
         MONITORINFO mi;

         mi.cbSize = sizeof(MONITORINFO);

         GetMonitorInfo(mon, &mi);

         if(horz & DW_GRAV_OBSTACLES)
         {
            if((horz & 0xf) == DW_GRAV_LEFT)
               *x += (mi.rcWork.left - mi.rcMonitor.left);
            else if((horz & 0xf) == DW_GRAV_RIGHT)
               *x -= (mi.rcMonitor.right - mi.rcWork.right);
         }
         if(vert & DW_GRAV_OBSTACLES)
         {
            if((vert & 0xf) == DW_GRAV_TOP)
               *y += (mi.rcWork.top - mi.rcMonitor.top);
            else if((vert & 0xf) == DW_GRAV_BOTTOM)
               *y -= (mi.rcMonitor.bottom - mi.rcWork.bottom);
         }
      }
   }
}

/*
 * Sets the position of a given window (widget).
 * Parameters:
 *          handle: Window (widget) handle.
 *          x: X location from the bottom left.
 *          y: Y location from the bottom left.
 */
void API dw_window_set_pos(HWND handle, long x, long y)
{
   unsigned long width, height;
   RECT rect;

   GetClientRect(handle, &rect);

   /* Can't position an unsized window, so attempt to auto-size */
   if((rect.bottom - rect.top) == 0 || (rect.right - rect.left) == 0)
      dw_window_set_size(handle, 0, 0);

   dw_window_get_pos_size(handle, NULL, NULL, &width, &height);
   _dw_handle_gravity(handle, &x, &y, width, height);
   SetWindowPos(handle, (HWND)NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}

/*
 * Sets the position and size of a given window (widget).
 * Parameters:
 *          handle: Window (widget) handle.
 *          x: X location from the bottom left.
 *          y: Y location from the bottom left.
 *          width: Width of the widget.
 *          height: Height of the widget.
 */
void API dw_window_set_pos_size(HWND handle, long x, long y, ULONG width, ULONG height)
{
   /* Attempt to auto-size */
   if ( width < 1 || height < 1 )
      _dw_get_window_for_size(handle, &width, &height);

   _dw_handle_gravity(handle, &x, &y, width, height);
   /* Finally set the size */
   SetWindowPos(handle, (HWND)NULL, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
}

/*
 * Gets the position and size of a given window (widget).
 * Parameters:
 *          handle: Window (widget) handle.
 *          x: X location from the bottom left.
 *          y: Y location from the bottom left.
 *          width: Width of the widget.
 *          height: Height of the widget.
 */
void API dw_window_get_pos_size(HWND handle, long *x, long *y, ULONG *width, ULONG *height)
{
   WINDOWPLACEMENT wp;

   wp.length = sizeof(WINDOWPLACEMENT);

   GetWindowPlacement(handle, &wp);
   if( wp.showCmd == SW_SHOWMAXIMIZED)
   {
      if(x)
         *x=0;
      if(y)
         *y=0;
      if(width)
         *width=dw_screen_width();
      if(height)
         *height=dw_screen_height();
   }
   else
   {
      if(x)
         *x = wp.rcNormalPosition.left;
      if(y)
         *y = wp.rcNormalPosition.top;
      if(width)
         *width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
      if(height)
         *height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
   }
}

/*
 * Sets the style of a given window (widget).
 * Parameters:
 *          handle: Window (widget) handle.
 *          width: New width in pixels.
 *          height: New height in pixels.
 */
void API dw_window_set_style(HWND handle, ULONG style, ULONG mask)
{
   ULONG tmp, currentstyle;
   ColorInfo *cinfo;
   TCHAR tmpbuf[100] = {0};

   if(!handle || !mask)
      return;

   if(handle < (HWND)65536)
   {
      char buffer[31] = {0};
      HMENU mymenu;
      ULONG id = (ULONG)(uintptr_t)handle;

      _snprintf(buffer, 30, "_dw_id%ld", id);
      mymenu = (HMENU)dw_window_get_data(DW_HWND_OBJECT, buffer);

      if(mymenu && IsMenu(mymenu))
         dw_menu_item_set_state((HMENUI)mymenu, id, style & mask);
      return;
   }

   GetClassName(handle, tmpbuf, 99);

   currentstyle = GetWindowLong(handle, GWL_STYLE);
   cinfo = _dw_window_get_cinfo(handle);

#ifdef TOOLBAR
   /* Bitmap Buttons */
   if(_tcsnicmp(tmpbuf, TOOLBARCLASSNAME, _tcslen(TOOLBARCLASSNAME)+1) == 0)
   {
      ULONG thisstyle = (TBSTYLE_FLAT | TBSTYLE_TRANSPARENT);

      if(mask & DW_BS_NOBORDER)
      {
         SetWindowLong(handle, GWL_STYLE, (style & DW_BS_NOBORDER) ? (currentstyle | thisstyle) : (currentstyle & ~thisstyle));

         /* Enable or disable visual themese */
         if(_SetWindowTheme)
            _SetWindowTheme(handle, (style & DW_BS_NOBORDER) ? NULL : L"", (style & DW_BS_NOBORDER) ? NULL : L"");

         return;
      }
   }
#endif

   tmp = currentstyle | mask;
   tmp ^= mask;
   tmp |= style & mask;

   /* Drop out for status bar, it currently doesn't accept styles on Windows */
   if(_tcsnicmp(tmpbuf, StatusbarClassName, _tcslen(StatusbarClassName)+1)==0)
      return;
   else if(_tcsnicmp(tmpbuf, ClassName, _tcslen(ClassName)+1)==0)
   {
      tmp = tmp & 0xffff0000;
#ifdef AEROGLASS
      if(mask & DW_FCF_COMPOSITED && _DwmExtendFrameIntoClientArea && _dw_composition && !IS_WIN8PLUS)
      {
         if(style & DW_FCF_COMPOSITED)
         {
            MARGINS mar = {-1,-1,-1,-1};

            /* Attempt to enable Aero glass background on the entire window */
            SetLayeredWindowAttributes(handle, _dw_transparencykey, 0, LWA_COLORKEY);
            _DwmExtendFrameIntoClientArea(handle, &mar);
            _dw_show_margins(handle, mar, __LINE__);
         }
         else
         {
            MARGINS mar = {0};

#ifdef DARK_MODE_TITLEBAR_MENU
            if(cinfo)
               mar = _dw_rect_to_margins(cinfo->rect);
#endif

            /* Remove Aero Glass */
            SetLayeredWindowAttributes(handle, _dw_transparencykey, 255, LWA_ALPHA);
            _DwmExtendFrameIntoClientArea(handle, &mar);
            _dw_show_margins(handle, mar, __LINE__);
         }
      }
#endif
   }
   else if(_tcsnicmp(tmpbuf, STATICCLASSNAME, _tcslen(STATICCLASSNAME)+1)==0)
   {
      ULONG thismask = mask & ~(DW_DT_VCENTER | DW_DT_WORDBREAK);
      ULONG thisstyle = style & ~(DW_DT_VCENTER | DW_DT_WORDBREAK);
      ULONG type = style & mask & 0xFL;

      /* Need to filter out bits that shouldn't be set */
      tmp = currentstyle | thismask;
      tmp ^= thismask;
      tmp |= thisstyle & thismask;

      if(mask & DW_DT_VCENTER && style & DW_DT_VCENTER && !cinfo)
         cinfo = _dw_window_new_cinfo(handle, TRUE);

      /* Alignment style is 0 for word wrap */
      if((style & DW_DT_WORDBREAK) && (mask & DW_DT_WORDBREAK))
         tmp &= ~(0xFL);
      else if(type == SS_LEFTNOWORDWRAP)
         tmp = (tmp & ~(0xFL)) | SS_LEFTNOWORDWRAP;
      else if(type == SS_CENTER)
         tmp = (tmp & ~(0xFL)) | SS_CENTER;
      else if(type == SS_RIGHT)
         tmp = (tmp & ~(0xFL)) | SS_RIGHT;
   }

   /* If we have cinfo store the style there for later use */
   if(cinfo)
   {
      cinfo->style = cinfo->style | mask;
      cinfo->style ^= mask;
      cinfo->style |= style & mask;
   }

   SetWindowLong(handle, GWL_STYLE, tmp);
}

/* Finds the physical ID from the reference ID */
int _dw_findnotebookid(NotebookPage **array, int pageid)
{
   int z;

   for(z=0;z<256;z++)
   {
      if(array[z] && array[z]->realid == pageid)
         return z;
   }
   return -1;
}

/*
 * Adds a new page to specified notebook.
 * Parameters:
 *          handle: Window (widget) handle.
 *          flags: Any additional page creation flags.
 *          front: If TRUE page is added at the beginning.
 */
unsigned long API dw_notebook_page_new(HWND handle, ULONG flags, int front)
{
   NotebookPage **array = (NotebookPage **)dw_window_get_data(handle, "_dw_array");

   if(array)
   {
      int z, refid = -1;

      for(z=0;z<256;z++)
      {
         if(_dw_findnotebookid(array, z) == -1)
         {
            refid = z;
            break;
         }
      }

      if(refid == -1)
         return -1;

      for(z=0;z<256;z++)
      {
         if(!array[z])
         {
            array[z] = calloc(1, sizeof(NotebookPage));
            array[z]->realid = refid;
            array[z]->item.mask = TCIF_TEXT;
            array[z]->item.iImage = -1;
            array[z]->item.pszText = TEXT("");
            TabCtrl_InsertItem(handle, z, &(array[z]->item));
            return refid;
         }
      }
   }
   return -1;
}

/*
 * Sets the text on the specified notebook tab.
 * Parameters:
 *          handle: Notebook handle.
 *          pageid: Page ID of the tab to set.
 *          text: Pointer to the text to set.
 */
void API dw_notebook_page_set_text(HWND handle, ULONG pageidx, const char *text)
{

   NotebookPage **array = (NotebookPage **)dw_window_get_data(handle, "_dw_array");
   int pageid;

   if(!array)
      return;

   pageid = _dw_findnotebookid(array, pageidx);

   if(pageid > -1 && array[pageid])
   {
      array[pageid]->item.mask = TCIF_TEXT;
      array[pageid]->item.pszText = UTF8toWide(text);
      TabCtrl_SetItem(handle, pageid, &(array[pageid]->item));
      _dw_resize_notebook_page(handle, pageid);
   }
}

/*
 * Sets the text on the specified notebook tab status area.
 * Parameters:
 *          handle: Notebook handle.
 *          pageid: Page ID of the tab to set.
 *          text: Pointer to the text to set.
 */
void API dw_notebook_page_set_status_text(HWND handle, ULONG pageid, const char *text)
{
}

/*
 * Packs the specified box into the notebook page.
 * Parameters:
 *          handle: Handle to the notebook to be packed.
 *          pageid: Page ID in the notebook which is being packed.
 *          page: Box handle to be packed.
 */
void API dw_notebook_pack(HWND handle, ULONG pageidx, HWND page)
{
   NotebookPage **array = (NotebookPage **)dw_window_get_data(handle, "_dw_array");
   int pageid;

   if(!array)
      return;

   pageid = _dw_findnotebookid(array, pageidx);

   if(pageid > -1 && array[pageid])
   {
      HWND tmpbox = dw_box_new(DW_VERT, 0);

      dw_box_pack_start(tmpbox, page, 0, 0, TRUE, TRUE, 0);
      if(array[pageid]->hwnd)
         dw_window_destroy(array[pageid]->hwnd);
      array[pageid]->hwnd = tmpbox;
      if(pageidx == dw_notebook_page_get(handle))
      {
         ShowWindow(tmpbox, SW_HIDE);
         SetParent(tmpbox, handle);
         _dw_resize_notebook_page(handle, pageid);
      }
   }
}

/*
 * Remove a page from a notebook.
 * Parameters:
 *          handle: Handle to the notebook widget.
 *          pageid: ID of the page to be destroyed.
 */
void API dw_notebook_page_destroy(HWND handle, unsigned long pageidx)
{
   NotebookPage **array = (NotebookPage **)dw_window_get_data(handle, "_dw_array");
   int newid = -1, z, pageid;
   HWND pagehwnd = 0;

   if(!array)
      return;

   pageid = _dw_findnotebookid(array, pageidx);

   if(pageid < 0)
      return;

   if(array[pageid])
   {
      SetParent(array[pageid]->hwnd, DW_HWND_OBJECT);
      pagehwnd = array[pageid]->hwnd;
      free(array[pageid]);
      array[pageid] = NULL;
   }

   TabCtrl_DeleteItem(handle, pageid);

   /* Shift the pages over 1 */
   for(z=pageid;z<255;z++)
      array[z] = array[z+1];
   array[255] = NULL;

   for(z=0;z<256;z++)
   {
      if(array[z])
      {
         newid = z;
         break;
      }
   }
   if(newid > -1)
   {
      SetParent(array[newid]->hwnd, handle);
      _dw_resize_notebook_page(handle, newid);
      dw_notebook_page_set(handle, array[newid]->realid);
   }
   if(pagehwnd)
      dw_window_destroy(pagehwnd);
}

/*
 * Queries the currently visible page ID.
 * Parameters:
 *          handle: Handle to the notebook widget.
 */
unsigned long API dw_notebook_page_get(HWND handle)
{
   NotebookPage **array = (NotebookPage **)dw_window_get_data(handle, "_dw_array");
   int physid = TabCtrl_GetCurSel(handle);

   if(physid > -1 && physid < 256 && array && array[physid])
      return array[physid]->realid;
   return -1;
}

/*
 * Sets the currently visible page ID.
 * Parameters:
 *          handle: Handle to the notebook widget.
 *          pageid: ID of the page to be made visible.
 */
void API dw_notebook_page_set(HWND handle, unsigned long pageidx)
{
   NotebookPage **array = (NotebookPage **)dw_window_get_data(handle, "_dw_array");
   int pageid;

   if(!array)
      return;

   pageid = _dw_findnotebookid(array, pageidx);

   if(pageid > -1 && pageid < 256)
   {
      int oldpage = TabCtrl_GetCurSel(handle);

      if(oldpage > -1 && array && array[oldpage])
         SetParent(array[oldpage]->hwnd, DW_HWND_OBJECT);

      TabCtrl_SetCurSel(handle, pageid);

      SetParent(array[pageid]->hwnd, handle);
      _dw_resize_notebook_page(handle, pageid);
   }
}

/*
 * Appends the specified text to the listbox's (or combobox) entry list.
 * Parameters:
 *          handle: Handle to the listbox to be appended to.
 *          text: Text to append into listbox.
 */
void API dw_listbox_append(HWND handle, const char *text)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
      SendMessage(handle,
               CB_ADDSTRING,
               0, (LPARAM)UTF8toWide(text));
   else
      SendMessage(handle,
               LB_ADDSTRING,
               0, (LPARAM)UTF8toWide(text));
}

/*
 * Appends the specified text items to the listbox's (or combobox) entry list.
 * Parameters:
 *          handle: Handle to the listbox to be appended to.
 *          text: Text strings to append into listbox.
 *          count: Number of text strings to append
 */
void API dw_listbox_list_append(HWND handle, char **text, int count)
{
   TCHAR tmpbuf[100] = {0};
   int listbox_type;
   int i;

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
      listbox_type = CB_ADDSTRING;
   else
      listbox_type = LB_ADDSTRING;

   for(i=0;i<count;i++)
      SendMessage(handle,(WPARAM)listbox_type,0,(LPARAM)UTF8toWide(text[i]));
}

/*
 * Inserts the specified text to the listbox's (or combobox) entry list.
 * Parameters:
 *          handle: Handle to the listbox to be appended to.
 *          text: Text to append into listbox.
 *          pos: 0 based position to insert text
 */
void API dw_listbox_insert(HWND handle, const char *text, int pos)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
      SendMessage(handle,
               CB_INSERTSTRING,
               pos, (LPARAM)UTF8toWide(text));
   else
      SendMessage(handle,
               LB_INSERTSTRING,
               pos, (LPARAM)UTF8toWide(text));
}

/*
 * Clears the listbox's (or combobox) list of all entries.
 * Parameters:
 *          handle: Handle to the listbox to be cleared.
 */
void API dw_listbox_clear(HWND handle)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
   {
      char *buf = dw_window_get_text(handle);

      SendMessage(handle,
               CB_RESETCONTENT, 0L, 0L);

      if(buf)
      {
         dw_window_set_text(handle, buf);
         free(buf);
      }
   }
   else
      SendMessage(handle,
               LB_RESETCONTENT, 0L, 0L);
}

/*
 * Sets the text of a given listbox entry.
 * Parameters:
 *          handle: Handle to the listbox to be queried.
 *          index: Index into the list to be queried.
 *          buffer: Buffer where text will be copied.
 */
void API dw_listbox_set_text(HWND handle, unsigned int index, const char *buffer)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
   {
      SendMessage(handle,  CB_DELETESTRING, (WPARAM)index, 0);
      SendMessage(handle, CB_INSERTSTRING, (WPARAM)index, (LPARAM)buffer);
   }
   else
   {
      unsigned int sel = (unsigned int)SendMessage(handle, LB_GETCURSEL, 0, 0);
      SendMessage(handle,  LB_DELETESTRING, (WPARAM)index, 0);
      SendMessage(handle, LB_INSERTSTRING, (WPARAM)index, (LPARAM)buffer);
      SendMessage(handle, LB_SETCURSEL, (WPARAM)sel, 0);
      SendMessage(handle, LB_SETSEL, (WPARAM)TRUE, (LPARAM)sel);
   }
}

/*
 * Copies the given index item's text into buffer.
 * Parameters:
 *          handle: Handle to the listbox to be queried.
 *          index: Index into the list to be queried.
 *          buffer: Buffer where text will be copied.
 *          length: Length of the buffer (including NULL).
 */
void API dw_listbox_get_text(HWND handle, unsigned int index, char *buffer, unsigned int length)
{
   TCHAR tmpbuf[100] = {0}, *wbuffer;
   unsigned int len;

   buffer[0] = 0;

   if(!buffer || !length)
      return;

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
   {
      len = (int)SendMessage(handle, CB_GETLBTEXTLEN, (WPARAM)index, 0);

      if(len != CB_ERR && (wbuffer = _alloca((len+1)*sizeof(TCHAR))))
      {
         SendMessage(handle, CB_GETLBTEXT, (WPARAM)index, (LPARAM)wbuffer);
         strncpy(buffer, WideToUTF8(wbuffer), length);
      }
   }
   else
   {
      len = (int)SendMessage(handle, LB_GETTEXTLEN, (WPARAM)index, 0);

      if(len != LB_ERR && (wbuffer = _alloca((len+1)*sizeof(TCHAR))))
      {
         SendMessage(handle, LB_GETTEXT, (WPARAM)index, (LPARAM)wbuffer);
         strncpy(buffer, WideToUTF8(wbuffer), length);
      }
   }
}

/*
 * Returns the index to the item in the list currently selected.
 * Parameters:
 *          handle: Handle to the listbox to be queried.
 */
int API dw_listbox_selected(HWND handle)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
      return (unsigned int)SendMessage(handle,
                               CB_GETCURSEL,
                               0, 0);

   return (unsigned int)SendMessage(handle,
                            LB_GETCURSEL,
                            0, 0);
}

/*
 * Returns the index to the current selected item or -1 when done.
 * Parameters:
 *          handle: Handle to the listbox to be queried.
 *          where: Either the previous return or -1 to restart.
 */
int API dw_listbox_selected_multi(HWND handle, int where)
{
   int *array, count, z;
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   /* This doesn't work on comboboxes */
   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
      return -1;

   count = (int)SendMessage(handle, LB_GETSELCOUNT, 0, 0);
   if(count > 0)
   {
      array = malloc(sizeof(int)*count);
      SendMessage(handle, LB_GETSELITEMS, (WPARAM)count, (LPARAM)array);

      if(where == -1)
      {
         int ret = array[0];
         free(array);
         return ret;
      }
      for(z=0;z<count;z++)
      {
         if(array[z] == where && (z+1) < count)
         {
            int ret = array[z+1];
            free(array);
            return ret;
         }
      }
      free(array);
   }
   return -1;
}

/*
 * Sets the selection state of a given index.
 * Parameters:
 *          handle: Handle to the listbox to be set.
 *          index: Item index.
 *          state: TRUE if selected FALSE if unselected.
 */
void API dw_listbox_select(HWND handle, int index, int state)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
      SendMessage(handle, CB_SETCURSEL, (WPARAM)index, 0);
   else
   {
      SendMessage(handle, LB_SETCURSEL, (WPARAM)index, 0);
      SendMessage(handle, LB_SETSEL, (WPARAM)state, (LPARAM)index);
   }
   _dw_wndproc(handle, WM_COMMAND, (WPARAM)(LBN_SELCHANGE << 16), (LPARAM)handle);
}

/*
 * Deletes the item with given index from the list.
 * Parameters:
 *          handle: Handle to the listbox to be set.
 *          index: Item index.
 */
void API dw_listbox_delete(HWND handle, int index)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
      SendMessage(handle, CB_DELETESTRING, (WPARAM)index, 0);
   else
      SendMessage(handle, LB_DELETESTRING, (WPARAM)index, 0);
}

/*
 * Returns the listbox's item count.
 * Parameters:
 *          handle: Handle to the listbox to be cleared.
 */
int API dw_listbox_count(HWND handle)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
      return (int)SendMessage(handle,
                        CB_GETCOUNT,0L, 0L);

   return (int)SendMessage(handle,
                     LB_GETCOUNT,0L, 0L);
}

/*
 * Sets the topmost item in the viewport.
 * Parameters:
 *          handle: Handle to the listbox to be cleared.
 *          top: Index to the top item.
 */
void API dw_listbox_set_top(HWND handle, int top)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   /* This doesn't work on comboboxes */
   if(_tcsnicmp(tmpbuf, COMBOBOXCLASSNAME, _tcslen(COMBOBOXCLASSNAME)+1)==0)
      return;

   SendMessage(handle, LB_SETTOPINDEX, (WPARAM)top, 0);
}

/*
 * Adds text to an MLE box and returns the current point.
 * Parameters:
 *          handle: Handle to the MLE to be queried.
 *          buffer: Text buffer to be imported.
 *          startpoint: Point to start entering text.
 */
unsigned int API dw_mle_import(HWND handle, const char *buffer, int startpoint)
{
   int textlen, len = GetWindowTextLength(handle);
   TCHAR *tmpbuf, *srcbuf = UTF8toWide(buffer);

   if(startpoint < 0)
      startpoint = 0;

   if(!buffer || (textlen = (int)_tcslen(srcbuf)) < 1)
      return startpoint;

   tmpbuf = calloc(sizeof(TCHAR), len + textlen + startpoint + 2);

   if(len)
   {
      TCHAR *dest, *start;
      int copylen = len - startpoint;

      GetWindowText(handle, tmpbuf, len+1);

      dest = &tmpbuf[startpoint+textlen];
      start = &tmpbuf[startpoint];

      if(copylen > 0)
         memcpy(dest, start, copylen*sizeof(TCHAR));
   }
   memcpy(&tmpbuf[startpoint], srcbuf, textlen*sizeof(TCHAR));

   SetWindowText(handle, tmpbuf);

   free(tmpbuf);
   return (startpoint + textlen);
}

/*
 * Grabs text from an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be queried.
 *          buffer: Text buffer to be exported. MUST allow for trailing nul character.
 *          startpoint: Point to start grabbing text.
 *          length: Amount of text to be grabbed.
 */
void API dw_mle_export(HWND handle, char *buffer, int startpoint, int length)
{
   int max, len = GetWindowTextLength(handle);
   TCHAR *tmpbuf = calloc(sizeof(TCHAR), len+2);

   if(len)
      GetWindowText(handle, tmpbuf, len+1);

   buffer[0] = 0;

   if(startpoint < len)
   {
      max = MIN(length, len - startpoint);

      memcpy(buffer, WideToUTF8(&tmpbuf[startpoint]), max);
      buffer[max] = '\0';
   }

   free(tmpbuf);
}

/*
 * Obtains information about an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be queried.
 *          bytes: A pointer to a variable to return the total bytes.
 *          lines: A pointer to a variable to return the number of lines.
 */
void API dw_mle_get_size(HWND handle, unsigned long *bytes, unsigned long *lines)
{
   if(bytes)
      *bytes = GetWindowTextLength(handle);
   if(lines)
      *lines = (unsigned long)SendMessage(handle, EM_GETLINECOUNT, 0, 0);
}

/*
 * Deletes text from an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be deleted from.
 *          startpoint: Point to start deleting text.
 *          length: Amount of text to be deleted.
 */
void API dw_mle_delete(HWND handle, int startpoint, int length)
{
   int len = GetWindowTextLength(handle);
   TCHAR *tmpbuf = calloc(sizeof(TCHAR), len+2);

   GetWindowText(handle, tmpbuf, len+1);

   if(startpoint + length < len)
   {
      _tcscpy(&tmpbuf[startpoint], &tmpbuf[startpoint+length]);

      SetWindowText(handle, tmpbuf);
   }

   free(tmpbuf);
}

/*
 * Clears all text from an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be cleared.
 */
void API dw_mle_clear(HWND handle)
{
   SetWindowText(handle, TEXT(""));
}

/*
 * Sets the visible line of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE.
 *          line: Line to be visible.
 */
void API dw_mle_set_visible(HWND handle, int line)
{
   int point = (int)SendMessage(handle, EM_LINEINDEX, (WPARAM)line, 0);
   dw_mle_set_cursor(handle, point);
}

/*
 * Sets the editablity of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE.
 *          state: TRUE if it can be edited, FALSE for readonly.
 */
void API dw_mle_set_editable(HWND handle, int state)
{
   SendMessage(handle, EM_SETREADONLY, (WPARAM)(state ? FALSE : TRUE), 0);
}

/*
 * Sets the word wrap state of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE.
 *          state: TRUE if it wraps, FALSE if it doesn't.
 */
void API dw_mle_set_word_wrap(HWND handle, int state)
{
#ifdef RICHEDIT
   /* If it is a rich edit control use the rich edit message */
   if(_DW_MLE_RICH_EDIT == DW_FEATURE_ENABLED && (hrichedit || hmsftedit))
   {
      SendMessage(handle, EM_SHOWSCROLLBAR, (WPARAM)SB_HORZ, (LPARAM)(state ? FALSE : TRUE));
      SendMessage(handle, EM_SETTARGETDEVICE, 0, state ? 0 : 1);
   }
#endif   
}

/*
 * Sets the word auto complete state of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE.
 *          state: Bitwise combination of DW_MLE_COMPLETE_TEXT/DASH/QUOTE
 */
void API dw_mle_set_auto_complete(HWND handle, int state)
{
}

/*
 * Sets the current cursor position of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be positioned.
 *          point: Point to position cursor.
 */
void API dw_mle_set_cursor(HWND handle, int point)
{
   SendMessage(handle, EM_SETSEL, (WPARAM)point, (LPARAM)point);
#ifdef RICHEDIT
   if(_DW_MLE_RICH_EDIT == DW_FEATURE_ENABLED && (hrichedit || hmsftedit))
      SendMessage(handle, EM_HIDESELECTION, 0, 0);
#endif
   SendMessage(handle, EM_SCROLLCARET, 0, 0);
}

/*
 * Finds text in an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be cleared.
 *          text: Text to search for.
 *          point: Start point of search.
 *          flags: Search specific flags.
 */
int API dw_mle_search(HWND handle, const char *text, int point, unsigned long flags)
{
   int len = GetWindowTextLength(handle);
   TCHAR *tmpbuf = calloc(sizeof(TCHAR), len+2);
   TCHAR *searchtext = UTF8toWide(text);
   int z, textlen, retval = 0;

   GetWindowText(handle, tmpbuf, len+1);

   textlen = (int)strlen(text);

   if(flags & DW_MLE_CASESENSITIVE)
   {
      for(z=point;z<(len-textlen) && !retval;z++)
      {
         if(_tcsncmp(&tmpbuf[z], searchtext, textlen) == 0)
            retval = z + textlen;
      }
   }
   else
   {
      for(z=point;z<(len-textlen) && !retval;z++)
      {
         if(_tcsnicmp(&tmpbuf[z], searchtext, textlen) == 0)
            retval = z + textlen;
      }
   }

   if(retval)
   {
      SendMessage(handle, EM_SETSEL, (WPARAM)retval - textlen, (LPARAM)retval);
#ifdef RICHEDIT
      if(_DW_MLE_RICH_EDIT == DW_FEATURE_ENABLED && (hrichedit || hmsftedit))
         SendMessage(handle, EM_HIDESELECTION, 0, 0);
#endif
      SendMessage(handle, EM_SCROLLCARET, 0, 0);
   }

   free(tmpbuf);

   return retval;
}

/*
 * Stops redrawing of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to freeze.
 */
void API dw_mle_freeze(HWND handle)
{
}

/*
 * Resumes redrawing of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to thaw.
 */
void API dw_mle_thaw(HWND handle)
{
}

/*
 * Sets the percent bar position.
 * Parameters:
 *          handle: Handle to the percent bar to be set.
 *          position: Position of the percent bar withing the range.
 */
void API dw_percent_set_pos(HWND handle, unsigned int position)
{
   if(position == DW_PERCENT_INDETERMINATE)
   {
      /* If our common controls supports it... */
      if((_dwComctlVer >= PACKVERSION(6,0)))
      {
          /* Enable the style on the control */
          SetWindowLong(handle, GWL_STYLE, GetWindowLong(handle, GWL_STYLE) | PBS_MARQUEE);
          /* Start the bar going */
          SendMessage(handle, PBM_SETMARQUEE, 1, 100);
      }
      else
         SendMessage(handle, PBM_SETPOS, 0, 0);
   }
   else
   {
      if((_dwComctlVer >= PACKVERSION(6,0)))
      {
          unsigned long style = GetWindowLong(handle, GWL_STYLE);

          if(style & PBS_MARQUEE)
          {
             /* Stop the bar */
             SendMessage(handle, PBM_SETMARQUEE, 0, 0);
             /* Disable the style on the control */
             SetWindowLong(handle, GWL_STYLE, style & ~PBS_MARQUEE);
          }
      }
      /* Otherwise just set the position */
      SendMessage(handle, PBM_SETPOS, (WPARAM)position, 0);
   }
}

/*
 * Returns the position of the slider.
 * Parameters:
 *          handle: Handle to the slider to be queried.
 */
unsigned int API dw_slider_get_pos(HWND handle)
{
   int max = (int)SendMessage(handle, TBM_GETRANGEMAX, 0, 0);
   ULONG currentstyle = GetWindowLong(handle, GWL_STYLE);

   if(currentstyle & TBS_VERT)
      return max - (unsigned int)SendMessage(handle, TBM_GETPOS, 0, 0);
   return (unsigned int)SendMessage(handle, TBM_GETPOS, 0, 0);
}

/*
 * Sets the slider position.
 * Parameters:
 *          handle: Handle to the slider to be set.
 *          position: Position of the slider withing the range.
 */
void API dw_slider_set_pos(HWND handle, unsigned int position)
{
   int max = (int)SendMessage(handle, TBM_GETRANGEMAX, 0, 0);
   ULONG currentstyle = GetWindowLong(handle, GWL_STYLE);

   if(currentstyle & TBS_VERT)
      SendMessage(handle, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)max - position);
   else
      SendMessage(handle, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)position);
}

/*
 * Returns the position of the scrollbar.
 * Parameters:
 *          handle: Handle to the scrollbar to be queried.
 */
unsigned int API dw_scrollbar_get_pos(HWND handle)
{
   return (unsigned int)SendMessage(handle, SBM_GETPOS, 0, 0);
}

/*
 * Sets the scrollbar position.
 * Parameters:
 *          handle: Handle to the scrollbar to be set.
 *          position: Position of the scrollbar withing the range.
 */
void API dw_scrollbar_set_pos(HWND handle, unsigned int position)
{
   dw_window_set_data(handle, "_dw_scrollbar_value", DW_UINT_TO_POINTER(position));
   SendMessage(handle, SBM_SETPOS, (WPARAM)position, (LPARAM)TRUE);
}

/*
 * Sets the scrollbar range.
 * Parameters:
 *          handle: Handle to the scrollbar to be set.
 *          range: Maximum range value.
 *          visible: Visible area relative to the range.
 */
void API dw_scrollbar_set_range(HWND handle, unsigned int range, unsigned int visible)
{
   SCROLLINFO si;

   si.cbSize = sizeof(SCROLLINFO);
   si.fMask = SIF_RANGE | SIF_PAGE;
   si.nMin = 0;
   si.nMax = range - 1;
   si.nPage = visible;
   SendMessage(handle, SBM_SETSCROLLINFO, (WPARAM)TRUE, (LPARAM)&si);
}

/*
 * Sets the spinbutton value.
 * Parameters:
 *          handle: Handle to the spinbutton to be set.
 *          position: Current value of the spinbutton.
 */
void API dw_spinbutton_set_pos(HWND handle, long position)
{
   TCHAR tmpbuf[101] = {0};
   ColorInfo *cinfo = _dw_window_get_cinfo(handle);

   _sntprintf(tmpbuf, 100, TEXT("%ld"), position);

   if(cinfo && cinfo->buddy)
      SetWindowText(cinfo->buddy, tmpbuf);

   SendMessage(handle, UDM_SETPOS32, 0, (LPARAM)position);
}

/*
 * Sets the spinbutton limits.
 * Parameters:
 *          handle: Handle to the spinbutton to be set.
 *          position: Current value of the spinbutton.
 *          position: Current value of the spinbutton.
 */
void API dw_spinbutton_set_limits(HWND handle, long upper, long lower)
{
   SendMessage(handle, UDM_SETRANGE32, (WPARAM)lower,(LPARAM)upper);
}

/*
 * Sets the entryfield character limit.
 * Parameters:
 *          handle: Handle to the spinbutton to be set.
 *          limit: Number of characters the entryfield will take.
 */
void API dw_entryfield_set_limit(HWND handle, ULONG limit)
{
   SendMessage(handle, EM_SETLIMITTEXT, (WPARAM)limit, 0);
}

/*
 * Returns the current value of the spinbutton.
 * Parameters:
 *          handle: Handle to the spinbutton to be queried.
 */
long API dw_spinbutton_get_pos(HWND handle)
{
   return (long)SendMessage(handle, UDM_GETPOS32, 0, 0);
}

/*
 * Returns the state of the checkbox.
 * Parameters:
 *          handle: Handle to the checkbox to be queried.
 */
int API dw_checkbox_get(HWND handle)
{
   if(SendMessage(handle, BM_GETCHECK, 0, 0) == BST_CHECKED)
      return (in_checkbox_handler ? FALSE : TRUE);
   return (in_checkbox_handler ? TRUE : FALSE);
}

/* This function unchecks all radiobuttons on a box */
BOOL CALLBACK _dw_uncheck_radios(HWND handle, LPARAM lParam)
{
   TCHAR tmpbuf[100] = {0};

   GetClassName(handle, tmpbuf, 99);

   if(_tcsnicmp(tmpbuf, BUTTONCLASSNAME, _tcslen(BUTTONCLASSNAME)+1)==0)
   {
      if(!dw_window_get_data(handle, "_dw_checkbox"))
         SendMessage(handle, BM_SETCHECK, 0, 0);
   }
   return TRUE;
}
/*
 * Sets the state of the checkbox.
 * Parameters:
 *          handle: Handle to the checkbox to be queried.
 *          value: TRUE for checked, FALSE for unchecked.
 */
void API dw_checkbox_set(HWND handle, int value)
{
   if(!dw_window_get_data(handle, "_dw_checkbox"))
   {
      HWND parent = GetParent(handle);

      if(parent)
         EnumChildWindows(parent, _dw_uncheck_radios, 0);
   }
   SendMessage(handle, BM_SETCHECK, (WPARAM)value, 0);
}

/*
 * Inserts an item into a tree window (widget) after another item.
 * Parameters:
 *          handle: Handle to the tree to be inserted.
 *          item: Handle to the item to be positioned after.
 *          title: The text title of the entry.
 *          icon: Handle to coresponding icon.
 *          parent: Parent handle or 0 if root.
 *          itemdata: Item specific data.
 */
HTREEITEM API dw_tree_insert_after(HWND handle, HTREEITEM item, const char *title, HICN icon, HTREEITEM parent, void *itemdata)
{
   TVITEM tvi;
   TVINSERTSTRUCT tvins;
   HTREEITEM hti;

   tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
   tvi.pszText = UTF8toWide(title);
   tvi.lParam = (LPARAM)itemdata;
   tvi.cchTextMax = (int)_tcslen(tvi.pszText);
   tvi.iSelectedImage = tvi.iImage = _dw_lookup_icon(handle, (HICON)icon, 1);

   tvins.item = tvi;
   tvins.hParent = parent;
   tvins.hInsertAfter = item ? item : TVI_FIRST;

   hti = TreeView_InsertItem(handle, &tvins);

   return hti;
}

/*
 * Inserts an item into a tree window (widget).
 * Parameters:
 *          handle: Handle to the tree to be inserted.
 *          title: The text title of the entry.
 *          icon: Handle to coresponding icon.
 *          parent: Parent handle or 0 if root.
 *          itemdata: Item specific data.
 */
HTREEITEM API dw_tree_insert(HWND handle, const char *title, HICN icon, HTREEITEM parent, void *itemdata)
{
   TVITEM tvi;
   TVINSERTSTRUCT tvins;
   HTREEITEM hti;

   tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
   tvi.pszText = UTF8toWide(title);
   tvi.lParam = (LPARAM)itemdata;
   tvi.cchTextMax = (int)_tcslen(tvi.pszText);
   tvi.iSelectedImage = tvi.iImage = _dw_lookup_icon(handle, (HICON)icon, 1);

   tvins.item = tvi;
   tvins.hParent = parent;
   tvins.hInsertAfter = TVI_LAST;

   hti = TreeView_InsertItem(handle, &tvins);

   return hti;
}

/*
 * Sets the text and icon of an item in a tree window (widget).
 * Parameters:
 *          handle: Handle to the tree containing the item.
 *          item: Handle of the item to be modified.
 *          title: The text title of the entry.
 *          icon: Handle to coresponding icon.
 */
void API dw_tree_item_change(HWND handle, HTREEITEM item, const char *title, HICN icon)
{
   TVITEM tvi;

   tvi.mask = TVIF_HANDLE;
   tvi.hItem = item;

   if(TreeView_GetItem(handle, &tvi))
   {
      tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
      tvi.pszText = UTF8toWide(title);
      tvi.cchTextMax = (int)_tcslen(tvi.pszText);
      tvi.iSelectedImage = tvi.iImage = _dw_lookup_icon(handle, (HICON)icon, 1);
      tvi.hItem = (HTREEITEM)item;

      TreeView_SetItem(handle, &tvi);
   }
}

/*
 * Sets the item data of a tree item.
 * Parameters:
 *          handle: Handle to the tree containing the item.
 *          item: Handle of the item to be modified.
 *          itemdata: User defined data to be associated with item.
 */
void API dw_tree_item_set_data(HWND handle, HTREEITEM item, void *itemdata)
{
   TVITEM tvi;

   tvi.mask = TVIF_HANDLE | TVIF_PARAM;
   tvi.hItem = item;
   tvi.lParam = (LPARAM)itemdata;

   TreeView_SetItem(handle, &tvi);
}

/*
 * Gets the item data of a tree item.
 * Parameters:
 *          handle: Handle to the tree containing the item.
 *          item: Handle of the item to be modified.
 */
void * API dw_tree_item_get_data(HWND handle, HTREEITEM item)
{
   TVITEM tvi;

   tvi.mask = TVIF_HANDLE | TVIF_PARAM;
   tvi.hItem = item;

   if(TreeView_GetItem(handle, &tvi))
   {
      return (void *)tvi.lParam;
   }
   return NULL;
}

/*
 * Gets the text an item in a tree window (widget).
 * Parameters:
 *          handle: Handle to the tree containing the item.
 *          item: Handle of the item to be modified.
 */
char * API dw_tree_get_title(HWND handle, HTREEITEM item)
{
   TVITEM tvi;
   TCHAR textbuf[1025] = {0}, *textptr = textbuf;

   tvi.mask = TVIF_HANDLE | TVIF_TEXT;
   tvi.hItem = item;
   tvi.pszText = textptr;
   tvi.cchTextMax = 1024;

   if(TreeView_GetItem(handle, &tvi))
      return _strdup(WideToUTF8(textptr));
    return NULL;
}

/*
 * Gets the text an item in a tree window (widget).
 * Parameters:
 *          handle: Handle to the tree containing the item.
 *          item: Handle of the item to be modified.
 */
HTREEITEM API dw_tree_get_parent(HWND handle, HTREEITEM item)
{
   return TreeView_GetParent(handle, item);
}

/*
 * Sets this item as the active selection.
 * Parameters:
 *       handle: Handle to the tree window (widget) to be selected.
 *       item: Handle to the item to be selected.
 */
void API dw_tree_item_select(HWND handle, HTREEITEM item)
{
   TreeView_SelectItem(handle, item);
}

/*
 * Removes all nodes from a tree.
 * Parameters:
 *       handle: Handle to the window (widget) to be cleared.
 */
void API dw_tree_clear(HWND handle)
{
   dw_window_set_data(handle, "_dw_select_item", DW_INT_TO_POINTER(1));
   TreeView_DeleteAllItems(handle);
   dw_window_set_data(handle, "_dw_select_item", NULL);
}

/*
 * Expands a node on a tree.
 * Parameters:
 *       handle: Handle to the tree window (widget).
 *       item: Handle to node to be expanded.
 */
void API dw_tree_item_expand(HWND handle, HTREEITEM item)
{
   TreeView_Expand(handle, item, TVE_EXPAND);
}

/*
 * Collapses a node on a tree.
 * Parameters:
 *       handle: Handle to the tree window (widget).
 *       item: Handle to node to be collapsed.
 */
void API dw_tree_item_collapse(HWND handle, HTREEITEM item)
{
   TreeView_Expand(handle, item, TVE_COLLAPSE);
}

/*
 * Removes a node from a tree.
 * Parameters:
 *       handle: Handle to the window (widget) to be cleared.
 *       item: Handle to node to be deleted.
 */
void API dw_tree_item_delete(HWND handle, HTREEITEM item)
{
   TreeView_DeleteItem(handle, item);
}

/*
 * Sets up the container columns.
 * Parameters:
 *          handle: Handle to the container to be configured.
 *          flags: An array of unsigned longs with column flags.
 *          titles: An array of strings with column text titles.
 *          count: The number of columns (this should match the arrays).
 *          separator: The column number that contains the main separator.
 *                     (only used on OS/2 but must be >= 0 on all)
 */
int API dw_container_setup(HWND handle, unsigned long *flags, char **titles, int count, int separator)
{
   ContainerInfo *cinfo = (ContainerInfo *)GetWindowLongPtr(handle, GWLP_USERDATA);
   int z, l = 0;
   unsigned long *tempflags = calloc(sizeof(unsigned long), count + 2);
   LVCOLUMN lvc;

   if(separator == -1)
   {
      tempflags[0] = DW_CFA_RESERVED;
      l = 1;
   }

   memcpy(&tempflags[l], flags, sizeof(unsigned long) * count);
   tempflags[count + l] = 0;
   cinfo->flags = tempflags;
   cinfo->columns = count + l;


   for(z=0;z<count;z++)
   {
      if(titles[z])
      {
         lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_FMT;
         lvc.pszText = UTF8toWide(titles[z]);
         lvc.cchTextMax = (int)_tcslen(lvc.pszText);
         if(flags[z] & DW_CFA_RIGHT)
            lvc.fmt = LVCFMT_RIGHT;
         else if(flags[z] & DW_CFA_CENTER)
            lvc.fmt = LVCFMT_CENTER;
         else
            lvc.fmt = LVCFMT_LEFT;
         lvc.cx = 75;
         lvc.iSubItem = count;
         SendMessage(handle, LVM_INSERTCOLUMN, (WPARAM)z + l, (LPARAM)&lvc);
      }
   }
   ListView_SetExtendedListViewStyle(handle, LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);
   return DW_ERROR_NONE;
}

/*
 * Configures the main filesystem column title for localization.
 * Parameters:
 *          handle: Handle to the container to be configured.
 *          title: The title to be displayed in the main column.
 */
void API dw_filesystem_set_column_title(HWND handle, const char *title)
{
    char *newtitle = _strdup(title ? title : "");

    dw_window_set_data(handle, "_dw_coltitle", newtitle);
}

/*
 * Sets up the filesystem columns, note: filesystem always has an icon/filename field.
 * Parameters:
 *          handle: Handle to the container to be configured.
 *          flags: An array of unsigned longs with column flags.
 *          titles: An array of strings with column text titles.
 *          count: The number of columns (this should match the arrays).
 */
int API dw_filesystem_setup(HWND handle, unsigned long *flags, char **titles, int count)
{
   char *coltitle = (char *)dw_window_get_data(handle, "_dw_coltitle");
   LV_COLUMN lvc;

   lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
   lvc.pszText = coltitle ? UTF8toWide(coltitle) : TEXT("Filename");
   lvc.cchTextMax = 8;
   lvc.fmt = 0;
   if(!count)
      lvc.cx = 300;
   else
      lvc.cx = 150;
   lvc.iSubItem = count;
   SendMessage(handle, LVM_INSERTCOLUMN, (WPARAM)0, (LPARAM)&lvc);
   dw_container_setup(handle, flags, titles, count, -1);
   if(coltitle)
   {
      dw_window_set_data(handle, "_dw_coltitle", NULL);
      free(coltitle);
   }
   return DW_ERROR_NONE;
}

/*
 * Obtains an icon from a module (or header in GTK).
 * Parameters:
 *          module: Handle to module (DLL) in OS/2 and Windows.
 *          id: A unsigned long id int the resources on OS/2 and
 *              Windows, on GTK this is converted to a pointer
 *              to an embedded XPM.
 */
HICN API dw_icon_load(unsigned long module, unsigned long id)
{
   return (HICN)LoadIcon(_DWInstance, MAKEINTRESOURCE(id));
}

/*
 * Obtains an icon from a file.
 * Parameters:
 *       filename: Name of the file, omit extention to have
 *                 DW pick the appropriate file extension.
 *                 (ICO on OS/2 or Windows, XPM on Unix)
 */
HICN API dw_icon_load_from_file(const char *filename)
{
#ifdef GDIPLUS
    return _dw_load_icon(filename);
#else
   char *file = malloc(strlen(filename) + 5);
   HANDLE icon;

   if(!file)
      return 0;

   strcpy(file, filename);

   /* check if we can read from this file (it exists and read permission) */
   if(access(file, 04) != 0)
   {
      /* Try with .ico extention */
      strcat(file, ".ico");
      if(access(file, 04) != 0)
      {
         free(file);
         return 0;
      }
   }
   icon = LoadImage(NULL, UTF8toWide(file), IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
   free(file);
   return (HICN)icon;
#endif
}

/*
 * Obtains an icon from data
 * Parameters:
 *       data: Source of icon data
 *                 DW pick the appropriate file extension.
 *                 (ICO on OS/2 or Windows, XPM on Unix)
 */
HICN API dw_icon_load_from_data(const char *data, int len)
{
   HANDLE icon = 0;
   char *file;
   FILE *fp;

   if ( !data )
      return 0;
   file = _tempnam( _dw_alternate_temp_dir, "dw" );
   if ( file != NULL )
   {
      fp = fopen( file, "wb" );
      if ( fp != NULL )
      {
         fwrite( data, 1, len, fp );
         fclose( fp );
#ifdef GDIPLUS
         icon = _dw_load_icon(file);
#else
         icon = LoadImage( NULL, UTF8toWide(file), IMAGE_ICON, 0, 0, LR_LOADFROMFILE );
#endif
      }
      else
      {
         _unlink( file );
         free( file );
         return 0;
      }
      _unlink( file );
      free( file );
   }
   return (HICN)icon;
}

/*
 * Frees a loaded resource in OS/2 and Windows.
 * Parameters:
 *          handle: Handle to icon returned by dw_icon_load().
 */
void API dw_icon_free(HICN handle)
{
   DestroyIcon((HICON)handle);
}

/*
 * Allocates memory used to populate a container.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          rowcount: The number of items to be populated.
 */
void * API dw_container_alloc(HWND handle, int rowcount)
{
   LV_ITEM lvi;
   int z, item;

   lvi.mask = LVIF_DI_SETITEM | LVIF_TEXT | LVIF_IMAGE;
   lvi.iSubItem = 0;
   /* Insert at the end */
   lvi.iItem = 1000000;
   lvi.pszText = TEXT("");
   lvi.cchTextMax = 1;
   lvi.iImage = -1;

   ShowWindow(handle, SW_HIDE);
   item = ListView_InsertItem(handle, &lvi);
   for(z=1;z<rowcount;z++)
      ListView_InsertItem(handle, &lvi);
   dw_window_set_data(handle, "_dw_insertitem", DW_INT_TO_POINTER(item));
   return (void *)handle;
}

/* Finds a icon in the table, otherwise it adds it to the table
 * and returns the index in the table.
 */
int _dw_lookup_icon(HWND handle, HICON hicon, int type)
{
   int z;
   static HWND lasthwnd = NULL;

   /* We can't add an invalid handle */
   if(!hicon)
      return -1;

   if(!_dw_image_list_small || !_dw_image_list_large)
   {
      _dw_image_list_small = ImageList_Create(16, 16, ILC_COLOR16 | ILC_MASK, ICON_INDEX_LIMIT, 0);
      _dw_image_list_large = ImageList_Create(32, 32, ILC_COLOR16 | ILC_MASK, ICON_INDEX_LIMIT, 0);
   }
   for(z=0;z<ICON_INDEX_LIMIT;z++)
   {
      if(!_dw_icon_lookup[z])
      {
         _dw_icon_lookup[z] = hicon;
         ImageList_AddIcon(_dw_image_list_small, hicon);
         ImageList_AddIcon(_dw_image_list_large, hicon);
         if(type)
         {
            TreeView_SetImageList(handle, _dw_image_list_small, TVSIL_NORMAL);
         }
         else
         {
            ListView_SetImageList(handle, _dw_image_list_small, LVSIL_SMALL);
            ListView_SetImageList(handle, _dw_image_list_large, LVSIL_NORMAL);
         }
         lasthwnd = handle;
         return z;
      }

      if(hicon == _dw_icon_lookup[z])
      {
         if(lasthwnd != handle)
         {
            if(type)
            {
               TreeView_SetImageList(handle, _dw_image_list_small, TVSIL_NORMAL);
            }
            else
            {
               ListView_SetImageList(handle, _dw_image_list_small, LVSIL_SMALL);
               ListView_SetImageList(handle, _dw_image_list_large, LVSIL_NORMAL);
            }
            lasthwnd = handle;
         }
         return z;
      }
   }
   return -1;
}

/*
 * Sets an item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
void API dw_filesystem_set_file(HWND handle, void *pointer, int row, const char *filename, HICN icon)
{
   LV_ITEM lvi;
   int item = 0;

   if(pointer)
   {
      item = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_insertitem"));
   }

   lvi.iItem = row + item;
   lvi.iSubItem = 0;
   lvi.mask = LVIF_DI_SETITEM | LVIF_IMAGE | LVIF_TEXT;
   lvi.pszText = UTF8toWide(filename);
   lvi.cchTextMax = (int)_tcslen(lvi.pszText);
   lvi.iImage = _dw_lookup_icon(handle, (HICON)icon, 0);

   ListView_SetItem(handle, &lvi);
}

/*
 * Sets an item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
void API dw_filesystem_set_item(HWND handle, void *pointer, int column, int row, void *data)
{
   dw_container_set_item(handle, pointer, column + 1, row, data);
}

/*
 * Sets an item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
void API dw_container_set_item(HWND handle, void *pointer, int column, int row, void *data)
{
   ContainerInfo *cinfo = (ContainerInfo *)GetWindowLongPtr(handle, GWLP_USERDATA);
   ULONG *flags;
   LV_ITEM lvi;
   TCHAR textbuffer[101] = {0};
   int item = 0;

   if(pointer)
   {
      item = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_insertitem"));
   }

   if(!cinfo || !cinfo->flags)
      return;

   flags = cinfo->flags;

   lvi.mask = LVIF_DI_SETITEM | LVIF_TEXT;
   lvi.iItem = row + item;
   lvi.iSubItem = column;
   lvi.pszText = textbuffer;
   lvi.cchTextMax = 0;

   if(flags[column] & DW_CFA_BITMAPORICON)
   {
      lvi.mask = LVIF_DI_SETITEM | LVIF_IMAGE;
      lvi.pszText = NULL;

      if(data)
      {
         HICON hicon = *((HICON *)data);

         lvi.iImage = _dw_lookup_icon(handle, hicon, 0);
      }
      else
         lvi.iImage = -1;
   }
   else if(flags[column] & DW_CFA_STRING && data)
   {
      char *tmp = *((char **)data);

      if(!tmp)
         tmp = "";

      lvi.pszText = UTF8toWide(tmp);
      lvi.cchTextMax = (int)_tcslen(lvi.pszText);
   }
   else if(flags[column] & DW_CFA_ULONG && data)
   {
      ULONG tmp = *((ULONG *)data);

      _sntprintf(textbuffer, 100, TEXT("%lu"), tmp);

      lvi.cchTextMax = (int)_tcslen(textbuffer);
   }
   else if(flags[column] & DW_CFA_DATE && data)
   {
      struct tm curtm;
      CDATE cdate = *((CDATE *)data);

      memset(&curtm, 0, sizeof(struct tm));

      /* Safety check... zero dates are crashing
       * Visual Studio 2008. -Brian
       */
      if(cdate.year > 1900 && cdate.year < 2100)
      {
          curtm.tm_mday = (cdate.day >= 0 && cdate.day < 32) ? cdate.day : 1;
          curtm.tm_mon = (cdate.month > 0 && cdate.month < 13) ? cdate.month - 1 : 0;
          curtm.tm_year = cdate.year - 1900;
          _tcsftime(textbuffer, 100, TEXT("%x"), &curtm);
      }

      lvi.cchTextMax = (int)_tcslen(textbuffer);
   }
   else if(flags[column] & DW_CFA_TIME && data)
   {
      struct tm curtm;
      CTIME ctime = *((CTIME *)data);

      curtm.tm_hour = (ctime.hours >= 0 && ctime.hours < 24) ? ctime.hours : 0;
      curtm.tm_min = (ctime.minutes >= 0 && ctime.minutes < 60) ? ctime.minutes : 0;
      curtm.tm_sec = (ctime.seconds >= 0 && ctime.seconds < 60) ? ctime.seconds : 0;

      _tcsftime(textbuffer, 100, TEXT("%X"), &curtm);

      lvi.cchTextMax = (int)_tcslen(textbuffer);
   }

   ListView_SetItem(handle, &lvi);
}

/*
 * Changes an existing item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
void API dw_container_change_item(HWND handle, int column, int row, void *data)
{
   dw_container_set_item(handle, NULL, column, row, data);
}

/*
 * Changes an existing item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
void API dw_filesystem_change_item(HWND handle, int column, int row, void *data)
{
   dw_filesystem_set_item(handle, NULL, column, row, data);
}

/*
 * Changes an item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
void API dw_filesystem_change_file(HWND handle, int row, const char *filename, HICN icon)
{
   dw_filesystem_set_file(handle, NULL, row, filename, icon);
}

/*
 * Gets column type for a container column
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          column: Zero based column.
 */
int API dw_container_get_column_type(HWND handle, int column)
{
   ContainerInfo *cinfo = (ContainerInfo *)GetWindowLongPtr(handle, GWLP_USERDATA);
   ULONG *flags;
   int rc;

   if(!cinfo || !cinfo->flags)
      return 0;

   flags = cinfo->flags;

   if(flags[column] & DW_CFA_BITMAPORICON)
      rc = DW_CFA_BITMAPORICON;
   else if(flags[column] & DW_CFA_STRING)
      rc = DW_CFA_STRING;
   else if(flags[column] & DW_CFA_ULONG)
      rc = DW_CFA_ULONG;
   else if(flags[column] & DW_CFA_DATE)
      rc = DW_CFA_DATE;
   else if(flags[column] & DW_CFA_TIME)
      rc = DW_CFA_TIME;
   else
      rc = 0;
   return rc;
}

/*
 * Gets column type for a filesystem container column
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          column: Zero based column.
 */
int API dw_filesystem_get_column_type(HWND handle, int column)
{
   return dw_container_get_column_type( handle, column + 1 );
}

/*
 * Sets the alternating row colors for container window (widget) handle.
 * Parameters:
 *          handle: The window (widget) handle.
 *          oddcolor: Odd row background color in DW_RGB format or a default color index.
 *          evencolor: Even row background color in DW_RGB format or a default color index.
 *                    DW_RGB_TRANSPARENT will disable coloring rows.
 *                    DW_CLR_DEFAULT will use the system default alternating row colors.
 */
void API dw_container_set_stripe(HWND handle, unsigned long oddcolor, unsigned long evencolor)
{
    ContainerInfo *cinfo = (ContainerInfo *)GetWindowLongPtr(handle, GWLP_USERDATA);

    /* Drop out on error */
    if(!cinfo)
        return;

    /* Create new brushes or remove if transparent */
    cinfo->odd = oddcolor;
    cinfo->even = evencolor;
}

/*
 * Sets the width of a column in the container.
 * Parameters:
 *          handle: Handle to window (widget) of container.
 *          column: Zero based column of width being set.
 *          width: Width of column in pixels.
 */
void API dw_container_set_column_width(HWND handle, int column, int width)
{
   ListView_SetColumnWidth(handle, column, width);
}

/* Internal version that handles both types */
void _dw_container_set_row_data(HWND handle, void *pointer, int row, int type, void *data)
{
   LV_ITEM lvi;
   int item = 0;

   if(pointer)
   {
      item = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_insertitem"));
   }

   memset(&lvi, 0, sizeof(LV_ITEM));

   lvi.iItem = row + item;
   lvi.mask = LVIF_PARAM;

   if(ListView_GetItem(handle, &lvi))
   {
      void **params = (void **)lvi.lParam;
      void *newparam = data;

      /* Make sure we have our pointer array... */
      if(!params)
      {
         /* If not allocate it */
         params = (void **)calloc(2, sizeof(void *));
         lvi.lParam = (LPARAM)params;
         ListView_SetItem(handle, &lvi);
      }
      /* If type string, we need to duplicate the string...
       * freeing any existing string.
       */
      if(type == _DW_DATA_TYPE_STRING)
      {
         void *oldparam = params[type];

         params[type] = NULL;

         if(oldparam)
            free(oldparam);
         if(newparam)
            newparam = _strdup((char *)newparam);
      }
      /* Set the new data in the pointer array */
      params[type] = newparam;
   }
}

/*
 * Sets the title of a row in the container.
 * Parameters:
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          row: Zero based row of data being set.
 *          title: String title of the item.
 */
void API dw_container_set_row_title(void *pointer, int row, const char *title)
{
   _dw_container_set_row_data(pointer, pointer, row, _DW_DATA_TYPE_STRING, (void *)title);
}

/*
 * Changes the title of a row already inserted in the container.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          row: Zero based row of data being set.
 *          title: String title of the item.
 */
void API dw_container_change_row_title(HWND handle, int row, const char *title)
{
   _dw_container_set_row_data(handle, NULL, row, _DW_DATA_TYPE_STRING, (void *)title);
}

/*
 * Sets the data of a row in the container.
 * Parameters:
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          row: Zero based row of data being set.
 *          data: Data pointer.
 */
void API dw_container_set_row_data(void *pointer, int row, void *data)
{
   _dw_container_set_row_data(pointer, pointer, row, _DW_DATA_TYPE_POINTER, data);
}

/*
 * Changes the data of a row already inserted in the container.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          row: Zero based row of data being set.
 *          data: Data pointer.
 */
void API dw_container_change_row_data(HWND handle, int row, void *data)
{
   _dw_container_set_row_data(handle, NULL, row, _DW_DATA_TYPE_POINTER, data);
}

/*
 * Sets the title of a row in the container.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          rowcount: The number of rows to be inserted.
 */
void API dw_container_insert(HWND handle, void *pointer, int rowcount)
{
   ShowWindow(handle, SW_SHOW);
}

/*
 * Removes all rows from a container.
 * Parameters:
 *       handle: Handle to the window (widget) to be cleared.
 *       redraw: TRUE to cause the container to redraw immediately.
 */
void API dw_container_clear(HWND handle, int redraw)
{
   ListView_DeleteAllItems(handle);
}

/*
 * Removes the first x rows from a container.
 * Parameters:
 *       handle: Handle to the window (widget) to be deleted from.
 *       rowcount: The number of rows to be deleted.
 */
void API dw_container_delete(HWND handle, int rowcount)
{
   int z, _index = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_index"));

   for(z=0;z<rowcount;z++)
   {
      ListView_DeleteItem(handle, 0);
   }
   if(rowcount > _index)
      dw_window_set_data(handle, "_dw_index", 0);
   else
      dw_window_set_data(handle, "_dw_index", DW_INT_TO_POINTER((_index - rowcount)));
}

/*
 * Scrolls container up or down.
 * Parameters:
 *       handle: Handle to the window (widget) to be scrolled.
 *       direction: DW_SCROLL_UP, DW_SCROLL_DOWN, DW_SCROLL_TOP or
 *                  DW_SCROLL_BOTTOM. (rows is ignored for last two)
 *       rows: The number of rows to be scrolled.
 */
void API dw_container_scroll(HWND handle, int direction, long rows)
{
   switch(direction)
   {
   case DW_SCROLL_TOP:
      ListView_Scroll(handle, 0, -10000000);
        break;
   case DW_SCROLL_BOTTOM:
      ListView_Scroll(handle, 0, 10000000);
      break;
   }
}

/*
 * Starts a new query of a container.
 * Parameters:
 *       handle: Handle to the window (widget) to be queried.
 *       flags: If this parameter is DW_CRA_SELECTED it will only
 *              return items that are currently selected.  Otherwise
 *              it will return all records in the container.
 */
char * API dw_container_query_start(HWND handle, unsigned long flags)
{
   LV_ITEM lvi;
   int _index = ListView_GetNextItem(handle, -1, flags);
   void **params;
   int type = _DW_DATA_TYPE_STRING;
   char *retval = NULL;

   if(_index == -1)
      return retval;

   if(flags & DW_CR_RETDATA)
      type = _DW_DATA_TYPE_POINTER;

   memset(&lvi, 0, sizeof(LV_ITEM));

   lvi.iItem = _index;
   lvi.mask = LVIF_PARAM;

   ListView_GetItem(handle, &lvi);
   params = (void **)lvi.lParam;

   if(params)
   {
      if(type == _DW_DATA_TYPE_STRING && params[type])
         retval = _strdup((char *)params[type]);
      else
         retval = (char *)params[type];
   }

   dw_window_set_data(handle, "_dw_index", DW_INT_TO_POINTER(_index));
   return retval;
}

/*
 * Continues an existing query of a container.
 * Parameters:
 *       handle: Handle to the window (widget) to be queried.
 *       flags: If this parameter is DW_CRA_SELECTED it will only
 *              return items that are currently selected.  Otherwise
 *              it will return all records in the container.
 */
char * API dw_container_query_next(HWND handle, unsigned long flags)
{
   LV_ITEM lvi;
   int _index = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_index"));
   void **params;
   int type = _DW_DATA_TYPE_STRING;
   char *retval = NULL;

   _index = ListView_GetNextItem(handle, _index, flags);

   if(_index == -1)
      return retval;

   if(flags & DW_CR_RETDATA)
      type = _DW_DATA_TYPE_POINTER;

   memset(&lvi, 0, sizeof(LV_ITEM));

   lvi.iItem = _index;
   lvi.mask = LVIF_PARAM;

   ListView_GetItem(handle, &lvi);
   params = (void **)lvi.lParam;

   if(params)
   {
      if(type == _DW_DATA_TYPE_STRING && params[type])
         retval = _strdup((char *)params[type]);
      else
         retval = (char *)params[type];
   }

   dw_window_set_data(handle, "_dw_index", DW_INT_TO_POINTER(_index));
   return retval;
}

/*
 * Cursors the item with the text speficied, and scrolls to that item.
 * Parameters:
 *       handle: Handle to the window (widget) to be queried.
 *       text:  Text usually returned by dw_container_query().
 */
void API dw_container_cursor(HWND handle, const char *text)
{
   int index = ListView_GetNextItem(handle, -1, LVNI_ALL);

   while ( index != -1 )
   {
      LV_ITEM lvi;
      void **params;

      memset(&lvi, 0, sizeof(LV_ITEM));

      lvi.iItem = index;
      lvi.mask = LVIF_PARAM;

      ListView_GetItem( handle, &lvi );
      params = (void **)lvi.lParam;

      if ( params && params[_DW_DATA_TYPE_STRING] && strcmp( (char *)params[_DW_DATA_TYPE_STRING], text ) == 0 )
      {
         unsigned long width, height;

         ListView_SetItemState( handle, index, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED );
         dw_window_get_pos_size( handle, NULL, NULL, &width, &height);
         if(width < 10 || height < 10)
            dw_window_set_data( handle, "_dw_cursor", DW_INT_TO_POINTER(index) );
         ListView_EnsureVisible( handle, index, TRUE );
         return;
      }

      index = ListView_GetNextItem( handle, index, LVNI_ALL );
   }
}

/*
 * Cursors the item with the text speficied, and scrolls to that item.
 * Parameters:
 *       handle: Handle to the window (widget) to be queried.
 *       text:  Text usually returned by dw_container_query().
 */
void API dw_container_cursor_by_data(HWND handle, void *data)
{
   int index = ListView_GetNextItem(handle, -1, LVNI_ALL);

   while ( index != -1 )
   {
      LV_ITEM lvi;
      void **params;

      memset(&lvi, 0, sizeof(LV_ITEM));

      lvi.iItem = index;
      lvi.mask = LVIF_PARAM;

      ListView_GetItem( handle, &lvi );
      params = (void **)lvi.lParam;

      if ( params && params[_DW_DATA_TYPE_POINTER] == data )
      {
         unsigned long width, height;

         ListView_SetItemState( handle, index, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED );
         dw_window_get_pos_size( handle, NULL, NULL, &width, &height);
         if(width < 10 || height < 10)
            dw_window_set_data( handle, "_dw_cursor", DW_INT_TO_POINTER(index) );
         ListView_EnsureVisible( handle, index, TRUE );
         return;
      }

      index = ListView_GetNextItem( handle, index, LVNI_ALL );
   }
}

/*
 * Deletes the item with the text speficied.
 * Parameters:
 *       handle: Handle to the window (widget).
 *       text:  Text usually returned by dw_container_query().
 */
void API dw_container_delete_row(HWND handle, const char *text)
{
   int index = ListView_GetNextItem(handle, -1, LVNI_ALL);

   while(index != -1)
   {
      LV_ITEM lvi;
      void **params;

      memset(&lvi, 0, sizeof(LV_ITEM));

      lvi.iItem = index;
      lvi.mask = LVIF_PARAM;

      ListView_GetItem(handle, &lvi);
      params = (void **)lvi.lParam;

      if ( params && params[_DW_DATA_TYPE_STRING] && strcmp( (char *)params[_DW_DATA_TYPE_STRING], text ) == 0 )
      {
         int _index = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_index"));

         if(index < _index)
            dw_window_set_data(handle, "_dw_index", DW_INT_TO_POINTER((_index - 1)));

         ListView_DeleteItem(handle, index);
         return;
      }

      index = ListView_GetNextItem(handle, index, LVNI_ALL);
   }
}

/*
 * Deletes the item with the text speficied.
 * Parameters:
 *       handle: Handle to the window (widget).
 *       text:  Text usually returned by dw_container_query().
 */
void API dw_container_delete_row_by_data(HWND handle, void *data)
{
   int index = ListView_GetNextItem(handle, -1, LVNI_ALL);

   while(index != -1)
   {
      LV_ITEM lvi;
      void **params;

      memset(&lvi, 0, sizeof(LV_ITEM));

      lvi.iItem = index;
      lvi.mask = LVIF_PARAM;

      ListView_GetItem(handle, &lvi);
      params = (void **)lvi.lParam;

      if ( params && params[_DW_DATA_TYPE_POINTER] == data )
      {
         int _index = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_index"));

         if(index < _index)
            dw_window_set_data(handle, "_dw_index", DW_INT_TO_POINTER((_index - 1)));

         ListView_DeleteItem(handle, index);
         return;
      }

      index = ListView_GetNextItem(handle, index, LVNI_ALL);
   }
}

/*
 * Optimizes the column widths so that all data is visible.
 * Parameters:
 *       handle: Handle to the window (widget) to be optimized.
 */
void API dw_container_optimize(HWND handle)
{
   ContainerInfo *cinfo = (ContainerInfo *)GetWindowLongPtr(handle, GWLP_USERDATA);
   if(cinfo && cinfo->columns == 1)
   {
      ListView_SetColumnWidth(handle, 0, LVSCW_AUTOSIZE);
   }
   else if(cinfo && cinfo->columns > 1)
   {
      ULONG *flags = cinfo->flags, *columns = calloc(sizeof(ULONG), cinfo->columns);
      TCHAR *text = calloc(sizeof(TCHAR), 1024);
      unsigned int z;
      int index;

      /* Initialize with sizes of column labels */
      for(z=0;z<cinfo->columns;z++)
      {
         if(flags[z] & DW_CFA_BITMAPORICON)
            columns[z] = 5;
         else
         {
            LVCOLUMN lvc;

            lvc.mask = LVCF_TEXT;
            lvc.cchTextMax = 1023;
            lvc.pszText = text;

            if(ListView_GetColumn(handle, z, &lvc))
               columns[z] = ListView_GetStringWidth(handle, lvc.pszText);

            if(flags[z] & DW_CFA_RESERVED)
               columns[z] += 20;
         }
      }

      index = ListView_GetNextItem(handle, -1, LVNI_ALL);

      /* Query all the item texts */
      while(index != -1)
      {
         for(z=0;z<cinfo->columns;z++)
         {
            unsigned int width;

            ListView_GetItemText(handle, index, z, text, 1023);
            width = ListView_GetStringWidth(handle, text);

            /* Figure extra space for the icon for the first column */
            if(z == 0)
               width += 20;

            if(width > columns[z])
               columns[z] = width;
         }

         index = ListView_GetNextItem(handle, index, LVNI_ALL);
      }

      /* Set the new sizes... Microsoft says we need to add
       * padding to the calculated sized but does not give
       * a value.  Trial and error shows that 16 works for us.
       */
      for(z=0;z<cinfo->columns;z++)
         ListView_SetColumnWidth(handle, z, columns[z] + 16);

      free(columns);
      free(text);
   }
}

/*
 * Inserts an icon into the taskbar.
 * Parameters:
 *       handle: Window handle that will handle taskbar icon messages.
 *       icon: Icon handle to display in the taskbar.
 *       bubbletext: Text to show when the mouse is above the icon.
 */
void API dw_taskbar_insert(HWND handle, HICN icon, const char *bubbletext)
{
   NOTIFYICONDATA tnid;

   tnid.cbSize = sizeof(NOTIFYICONDATA);
   tnid.hWnd = handle;
   tnid.uID = (UINT)(uintptr_t)icon;
   tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
   tnid.uCallbackMessage = WM_USER+2;
   tnid.hIcon = (HICON)icon;
   if(bubbletext)
      _tcsncpy(tnid.szTip, UTF8toWide(bubbletext), sizeof(tnid.szTip) / sizeof(TCHAR));
   else
      tnid.szTip[0] = 0;

   Shell_NotifyIcon(NIM_ADD, &tnid);
}

/*
 * Deletes an icon from the taskbar.
 * Parameters:
 *       handle: Window handle that was used with dw_taskbar_insert().
 *       icon: Icon handle that was used with dw_taskbar_insert().
 */
void API dw_taskbar_delete(HWND handle, HICN icon)
{
   NOTIFYICONDATA tnid;

   tnid.cbSize = sizeof(NOTIFYICONDATA);
   tnid.hWnd = handle;
   tnid.uID = (UINT)(uintptr_t)icon;

   Shell_NotifyIcon(NIM_DELETE, &tnid);
}

/*
 * Creates a rendering context widget (window) to be packed.
 * Parameters:
 *       id: An id to be used with dw_window_from_id.
 * Returns:
 *       A handle to the widget or NULL on failure.
 */
HWND API dw_render_new(unsigned long id)
{
   Box *newbox = calloc(sizeof(Box), 1);
   HWND tmp = CreateWindow(ObjectClassName,
                     NULL,
                     WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);
   newbox->pad = 0;
   newbox->type = 0;
   newbox->count = 0;
   newbox->grouphwnd = (HWND)NULL;
   newbox->cinfo.pOldProc = SubclassWindow(tmp, _dw_rendwndproc);
   newbox->cinfo.fore = newbox->cinfo.back = -1;
   SetWindowLongPtr( tmp, GWLP_USERDATA, (LONG_PTR)newbox );
   return tmp;
}

/*
 * Invalidate the render widget triggering an expose event.
 * Parameters:
 *       handle: A handle to a render widget to be redrawn.
 */
void API dw_render_redraw(HWND handle)
{
   InvalidateRect(handle, NULL, FALSE);
}

/* Sets the current foreground drawing color.
 * Parameters:
 *       red: red value.
 *       green: green value.
 *       blue: blue value.
 */
void API dw_color_foreground_set(unsigned long value)
{
   HPEN hPen = TlsGetValue(_dw_hpen);
   HBRUSH hBrush = TlsGetValue(_dw_hbrush);
   COLORREF foreground;
#ifdef GDIPLUS
   GpBrush *brush = TlsGetValue(_dw_gpbrush);
   GpPen *pen = TlsGetValue(_dw_gppen);
   ARGB gpfore;
#endif

   value = _dw_internal_color(value);
   foreground = RGB(DW_RED_VALUE(value), DW_GREEN_VALUE(value), DW_BLUE_VALUE(value));
#ifdef GDIPLUS
   gpfore = MAKEARGB(255, DW_RED_VALUE(value), DW_GREEN_VALUE(value), DW_BLUE_VALUE(value));

   GdipDeletePen(pen);
   GdipCreatePen1(gpfore, 1.0, UnitPixel, &pen);
   TlsSetValue(_dw_gppen, (LPVOID)pen);
   GdipSetSolidFillColor(brush, gpfore);
#endif

   DeleteObject(hPen);
   DeleteObject(hBrush);
   TlsSetValue(_dw_foreground, (LPVOID)(uintptr_t)foreground);
   TlsSetValue(_dw_hpen, CreatePen(PS_SOLID, 1, foreground));
   TlsSetValue(_dw_hbrush, CreateSolidBrush(foreground));
}

/* Sets the current background drawing color.
 * Parameters:
 *       red: red value.
 *       green: green value.
 *       blue: blue value.
 */
void API dw_color_background_set(unsigned long value)
{
   COLORREF background;

   value = _dw_internal_color(value);
   background = RGB(DW_RED_VALUE(value), DW_GREEN_VALUE(value), DW_BLUE_VALUE(value));

   if(value == DW_RGB_TRANSPARENT)
      TlsSetValue(_dw_background, (LPVOID)DW_RGB_TRANSPARENT);
   else
      TlsSetValue(_dw_background, (LPVOID)(uintptr_t)background);
}

/* Allows the user to choose a color using the system's color chooser dialog.
 * Parameters:
 *       value: current color
 * Returns:
 *       The selected color or the current color if cancelled.
 */
unsigned long API dw_color_choose(unsigned long value)
{
   CHOOSECOLOR cc;
   unsigned long newcolor;
   COLORREF acrCustClr[16] = {0};

   value = _dw_internal_color(value);
   if(value == DW_RGB_TRANSPARENT)
      newcolor = DW_RGB_TRANSPARENT;
   else
      newcolor = RGB(DW_RED_VALUE(value), DW_GREEN_VALUE(value), DW_BLUE_VALUE(value));
   ZeroMemory(&cc, sizeof(CHOOSECOLOR));
   cc.lStructSize = sizeof(CHOOSECOLOR);
   cc.rgbResult = newcolor;
   cc.hwndOwner = HWND_DESKTOP;
   cc.lpCustColors = (LPDWORD)acrCustClr;
   cc.Flags = CC_FULLOPEN | CC_RGBINIT;
   if (ChooseColor(&cc) == TRUE)
      newcolor = DW_RGB(DW_RED_VALUE(cc.rgbResult), DW_GREEN_VALUE(cc.rgbResult), DW_BLUE_VALUE(cc.rgbResult));
   return newcolor;
}

/* Draw a point on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       x: X coordinate.
 *       y: Y coordinate.
 */
void API dw_draw_point(HWND handle, HPIXMAP pixmap, int x, int y)
{
#ifdef GDIPLUS
   /* There doesn't seem to be an equivalent to SetPixel in GDI+ ...
    * so instead we call dw_draw_rect() with 1 for width and height.
    */
   dw_draw_rect(handle, pixmap, DW_DRAW_FILL | DW_DRAW_NOAA, x, y, 1, 1);
#else
   HDC hdcPaint;

   if(handle)
      hdcPaint = GetDC(handle);
   else if(pixmap)
      hdcPaint = pixmap->hdc;
   else
      return;

   SetPixel(hdcPaint, x, y, (COLORREF)TlsGetValue(_dw_foreground));
   if(!pixmap)
      ReleaseDC(handle, hdcPaint);
#endif
}

/* Draw a line on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       x1: First X coordinate.
 *       y1: First Y coordinate.
 *       x2: Second X coordinate.
 *       y2: Second Y coordinate.
 */
void API dw_draw_line(HWND handle, HPIXMAP pixmap, int x1, int y1, int x2, int y2)
{
#ifdef GDIPLUS
   GpGraphics *graphics = NULL;
   GpPen *pen = TlsGetValue(_dw_gppen);

   if(handle)
      GdipCreateFromHWND(handle, &graphics);
   else if(pixmap)
      GdipCreateFromHDC(pixmap->hdc, &graphics);
   else
      return;

   GdipSetSmoothingMode(graphics, SmoothingModeAntiAlias);
   GdipDrawLineI(graphics, pen, x1, y1, x2, y2);
   GdipDeleteGraphics(graphics);
#else
   HDC hdcPaint;
   HPEN oldPen;

   if(handle)
      hdcPaint = GetDC(handle);
   else if(pixmap)
      hdcPaint = pixmap->hdc;
   else
      return;

   oldPen = SelectObject(hdcPaint, TlsGetValue(_dw_hpen));
   MoveToEx(hdcPaint, x1, y1, NULL);
   LineTo(hdcPaint, x2, y2);
   SelectObject(hdcPaint, oldPen);
   /* For some reason Win98 (at least) fails
    * to draw the last pixel.  So I do it myself.
    */
   SetPixel(hdcPaint, x2, y2, (COLORREF)TlsGetValue(_dw_foreground));
   if(!pixmap)
      ReleaseDC(handle, hdcPaint);
#endif
}

/* Internal function to generate POINT arrays used by Windows APIs */
POINT *_makePoints(int *npoints, int *x, int *y)
{
   POINT *points;
   int i;

   /*
    * Allocate enough space for the number of points supplied plus 1.
    * Under windows, unless the first and last points are the same
    * the polygon won't be closed
    */
   points = (POINT *)malloc( ((*npoints)+1) * sizeof(POINT) );

   if(points)
   {
      for ( i = 0 ; i < *npoints ; i++ )
      {
         points[i].x = x[i];
         points[i].y = y[i];
      }
      if ( !( points[0].x == points[(*npoints)-1].x
      &&   points[0].y == points[(*npoints)-1].y ) )
      {
         /* set the last point to be the same as the first point... */
         points[*npoints].x = points[0].x;
         points[*npoints].y = points[0].y;
         /* ... and increment the number of points */
         (*npoints)++;
      }
   }
   return points;
}

/* Draw a closed polygon on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       flags: DW_DRAW_FILL (1) to fill the polygon or DW_DRAW_DEFAULT (0).
 *       number of points
 *       x[]: X coordinates.
 *       y[]: Y coordinates.
 */
void API dw_draw_polygon(HWND handle, HPIXMAP pixmap, int flags, int npoints, int *x, int *y)
{
   POINT *points = NULL;

   /* Sanity check */
   if(npoints < 1)
      return;

#ifdef GDIPLUS
   {
      GpGraphics *graphics = NULL;

      if(handle)
         GdipCreateFromHWND(handle, &graphics);
      else if(pixmap)
         GdipCreateFromHDC(pixmap->hdc, &graphics);
      else
         return;

      /* Enable antialiasing if the DW_DRAW_NOAA flag isn't set */
      if(!(flags & DW_DRAW_NOAA))
        GdipSetSmoothingMode(graphics, SmoothingModeAntiAlias);

      if((points = _makePoints(&npoints, x, y)))
      {
         if(flags & DW_DRAW_FILL)
         {
            GpBrush *brush = TlsGetValue(_dw_gpbrush);

            GdipFillPolygon2I(graphics, brush, points, npoints);
         }
         else
         {
            GpPen *pen = TlsGetValue(_dw_gppen);

            GdipDrawPolygonI(graphics, pen, points, npoints);
         }
      }
      GdipDeleteGraphics(graphics);
   }
#else
   {
      HDC hdcPaint;

      if ( handle )
         hdcPaint = GetDC( handle );
      else if ( pixmap )
         hdcPaint = pixmap->hdc;
      else
         return;

      if((points = _makePoints(&npoints, x, y)))
      {
         HBRUSH oldBrush = SelectObject( hdcPaint, TlsGetValue(_dw_hbrush) );
         HPEN oldPen = SelectObject( hdcPaint, TlsGetValue(_dw_hpen) );

         if ( flags & DW_DRAW_FILL )
            Polygon( hdcPaint, points, npoints );
         else
            Polyline( hdcPaint, points, npoints );

         SelectObject( hdcPaint, oldBrush );
         SelectObject( hdcPaint, oldPen );
      }

      if ( !pixmap )
         ReleaseDC( handle, hdcPaint );
   }
#endif
   if(points)
      free(points);
}

/* Draw a rectangle on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       flags: DW_DRAW_FILL (1) to fill the box or DW_DRAW_DEFAULT (0).
 *       x: X coordinate.
 *       y: Y coordinate.
 *       width: Width of rectangle.
 *       height: Height of rectangle.
 */
void API dw_draw_rect(HWND handle, HPIXMAP pixmap, int flags, int x, int y, int width, int height)
{
#ifdef GDIPLUS
   GpGraphics *graphics = NULL;

   if(handle)
      GdipCreateFromHWND(handle, &graphics);
   else if(pixmap)
      GdipCreateFromHDC(pixmap->hdc, &graphics);
   else
      return;

   /* Enable antialiasing if the DW_DRAW_NOAA flag isn't set */
   if(!(flags & DW_DRAW_NOAA))
     GdipSetSmoothingMode(graphics, SmoothingModeAntiAlias);

   if(flags & DW_DRAW_FILL)
   {
      GpBrush *brush = TlsGetValue(_dw_gpbrush);

      GdipFillRectangleI(graphics, brush, x, y, width, height);
   }
   else
   {
      GpPen *pen = TlsGetValue(_dw_gppen);

      GdipDrawRectangleI(graphics, pen, x, y, width, height);
   }
   GdipDeleteGraphics(graphics);
#else
   HDC hdcPaint;
   RECT Rect;

   if(handle)
      hdcPaint = GetDC(handle);
   else if(pixmap)
      hdcPaint = pixmap->hdc;
   else
      return;

   SetRect(&Rect, x, y, x + width , y + height );
   if(flags & DW_DRAW_FILL)
      FillRect(hdcPaint, &Rect, TlsGetValue(_dw_hbrush));
   else
      FrameRect(hdcPaint, &Rect, TlsGetValue(_dw_hbrush));
   if(!pixmap)
      ReleaseDC(handle, hdcPaint);
#endif
}

/* Draw an arc on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       flags: DW_DRAW_FILL (1) to fill the arc or DW_DRAW_DEFAULT (0).
 *              DW_DRAW_FULL will draw a complete circle/elipse.
 *       xorigin: X coordinate of center of arc.
 *       yorigin: Y coordinate of center of arc.
 *       x1: X coordinate of first segment of arc.
 *       y1: Y coordinate of first segment of arc.
 *       x2: X coordinate of second segment of arc.
 *       y2: Y coordinate of second segment of arc.
 */
void API dw_draw_arc(HWND handle, HPIXMAP pixmap, int flags, int xorigin, int yorigin, int x1, int y1, int x2, int y2)
{
#ifdef GDIPLUS
   GpGraphics *graphics = NULL;
   GpPen *pen = TlsGetValue(_dw_gppen);

   if(handle)
      GdipCreateFromHWND(handle, &graphics);
   else if(pixmap)
      GdipCreateFromHDC(pixmap->hdc, &graphics);
   else
      return;

   /* Enable antialiasing if the DW_DRAW_NOAA flag isn't set */
   if(!(flags & DW_DRAW_NOAA))
     GdipSetSmoothingMode(graphics, SmoothingModeAntiAlias);

   if(flags & DW_DRAW_FULL)
   {
      if(flags & DW_DRAW_FILL)
      {
         GpBrush *brush = TlsGetValue(_dw_gpbrush);

         GdipFillEllipseI(graphics, brush, x1 < x2 ? x1 : x2, y1 < y2 ? y1 : y2, abs(x1-x2), abs(y1-y2));
      }
      else
         GdipDrawEllipseI(graphics, pen, x1 < x2 ? x1 : x2, y1 < y2 ? y1 : y2, abs(x1-x2), abs(y1-y2));
   }
   else
   {
      double a1 = atan2((y1-yorigin), (x1-xorigin));
      double a2 = atan2((y2-yorigin), (x2-xorigin));
      double dx = xorigin - x1;
      double dy = yorigin - y1;
      double r = sqrt(dx*dx + dy*dy);
      double sweep;
      int ri = (int)r;

      /* Convert to degrees */
      a1 *= (180.0 / M_PI);
      a2 *= (180.0 / M_PI);
      sweep = fabs(a1 - a2);

      GdipDrawArcI(graphics, pen, xorigin-ri, yorigin-ri, ri*2, ri*2, (REAL)a1, (REAL)sweep);
   }
   GdipDeleteGraphics(graphics);
#else
   HDC hdcPaint;
   HBRUSH oldBrush;
   HPEN oldPen;
   double dx = xorigin - x1;
   double dy = yorigin - y1;
   double r = sqrt(dx*dx + dy*dy);
   int ri = (int)r;

   if(handle)
      hdcPaint = GetDC(handle);
   else if(pixmap)
      hdcPaint = pixmap->hdc;
   else
      return;

   if(flags & DW_DRAW_FILL)
      oldBrush = SelectObject( hdcPaint, TlsGetValue(_dw_hbrush) );
   else
      oldBrush = SelectObject( hdcPaint, GetStockObject(HOLLOW_BRUSH) );
   oldPen = SelectObject( hdcPaint, TlsGetValue(_dw_hpen) );
   if(flags & DW_DRAW_FULL)
      Ellipse(hdcPaint, x1, y1, x2, y2);
   else
      Arc(hdcPaint, xorigin-ri, yorigin-ri, xorigin+ri, yorigin+ri, x2, y2, x1, y1);
   SelectObject( hdcPaint, oldBrush );
   SelectObject( hdcPaint, oldPen );

   if(!pixmap)
      ReleaseDC(handle, hdcPaint);
#endif
}

#ifdef GDIPLUS
/* Internal function to increase or decrease coordinates/sizes
 * by the difference of the screen DPI (96) and the context DPI.
 */
void _dw_convert_dpi(HDC hdc, int *x, int *y, int mult)
{
   int ratiox = (int)GetDeviceCaps(hdc, LOGPIXELSX)/96;
   int ratioy = (int)GetDeviceCaps(hdc, LOGPIXELSY)/96;
   if(ratiox > 1 && ratioy > 1)
   {
      if(mult)
      {
         if(x)
            *x *= ratiox;
         if(y)
            *y *= ratioy;
      }
      else
      {
         if(x)
            *x /= ratiox;
         if(y)
            *y /= ratioy;
      }
   }
}
#endif

/* Draw text on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       x: X coordinate.
 *       y: Y coordinate.
 *       text: Text to be displayed.
 */
void API dw_draw_text(HWND handle, HPIXMAP pixmap, int x, int y, const char *text)
{
   HDC hdc;
   int mustdelete = 0;
   HFONT hFont = 0, oldFont = 0;
   ColorInfo *cinfo = NULL;
   COLORREF background;
   TCHAR *wtext = UTF8toWide(text);

   if(handle)
      hdc = GetDC(handle);
   else if(pixmap)
      hdc = pixmap->hdc;
   else
      return;

   if(handle)
      cinfo = _dw_window_get_cinfo(handle);
   else if(pixmap->font)
      hFont = pixmap->font;
   else if(pixmap->handle)
      cinfo = _dw_window_get_cinfo(pixmap->handle);

   if(cinfo)
   {
      hFont = _dw_acquire_font(handle, cinfo->fontname);
      mustdelete = 1;
   }

   background = (COLORREF)(uintptr_t)TlsGetValue(_dw_background);
   if(hFont)
      oldFont = SelectObject(hdc, hFont);
   SetTextColor(hdc, (COLORREF)(uintptr_t)TlsGetValue(_dw_foreground));
   if(background == DW_RGB_TRANSPARENT)
      SetBkMode(hdc, TRANSPARENT);
   else
   {
      SetBkMode(hdc, OPAQUE);
      SetBkColor(hdc, background);
   }
#ifdef GDIPLUS
   _dw_convert_dpi(hdc, &x, &y, TRUE);
#endif
   TextOut(hdc, x, y, wtext, (int)_tcslen(wtext));
   if(oldFont)
      SelectObject(hdc, oldFont);
   if(mustdelete)
      DeleteObject(hFont);
   if(!pixmap)
      ReleaseDC(handle, hdc);
}

/* Query the width and height of a text string.
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       text: Text to be queried.
 *       width: Pointer to a variable to be filled in with the width.
 *       height Pointer to a variable to be filled in with the height.
 */
void API dw_font_text_extents_get(HWND handle, HPIXMAP pixmap, const char *text, int *width, int *height)
{
   HDC hdc;
   int mustdelete = 0;
   HFONT hFont = NULL, oldFont;
   SIZE sz;
   TCHAR *wtext = UTF8toWide(text);

   if(handle)
      hdc = GetDC(handle);
   else if(pixmap)
      hdc = pixmap->hdc;
   else
      return;

   if(pixmap && pixmap->font)
   {
       hFont = pixmap->font;
   }
   else
   {
      ColorInfo *cinfo = NULL;

      if(handle)
         cinfo = _dw_window_get_cinfo(handle);
      else if(pixmap && pixmap->handle)
         cinfo = _dw_window_get_cinfo(pixmap->handle);

      if(cinfo)
      {
         hFont = _dw_acquire_font(handle, cinfo->fontname);
         mustdelete = 1;
      }
   }
   oldFont = SelectObject(hdc, hFont);

   GetTextExtentPoint32(hdc, wtext, (int)_tcslen(wtext), &sz);

   if(width)
      *width = sz.cx;

   if(height)
      *height = sz.cy;

#ifdef GDIPLUS
   _dw_convert_dpi(hdc, width, height, FALSE);
#endif

   SelectObject(hdc, oldFont);
   if(mustdelete)
      DeleteObject(hFont);
   if(!pixmap)
      ReleaseDC(handle, hdc);
}

/* Call this after drawing to the screen to make sure
 * anything you have drawn is visible.
 */
void API dw_flush(void)
{
}

/*
 * Creates a pixmap with given parameters.
 * Parameters:
 *       handle: Window handle the pixmap is associated with.
 *       width: Width of the pixmap in pixels.
 *       height: Height of the pixmap in pixels.
 *       depth: Color depth of the pixmap.
 * Returns:
 *       A handle to a pixmap or NULL on failure.
 */
HPIXMAP API dw_pixmap_new(HWND handle, unsigned long width, unsigned long height, int depth)
{
   HPIXMAP pixmap;
   HDC hdc;

   if (!(pixmap = calloc(1,sizeof(struct _hpixmap))))
      return NULL;

   hdc = GetDC(handle);

   pixmap->width = width; pixmap->height = height;

   pixmap->handle = handle;
   pixmap->hbm = CreateCompatibleBitmap(hdc, width, height);
   pixmap->hdc = CreateCompatibleDC(hdc);
   pixmap->transcolor = DW_RGB_TRANSPARENT;

   SelectObject(pixmap->hdc, pixmap->hbm);

   ReleaseDC(handle, hdc);

   return pixmap;
}

#ifndef GDIPLUS
/* Read the file bitmap header ourselves...
 * apparently we can't check the depth once loaded...
 * since it seems to normalize it to our screen depth.
 */
unsigned long _dw_read_bitmap_header(char *file)
{
    BITMAPFILEHEADER header;
    BITMAPINFO *info;
    FILE *fp;
    int infosize;
    int depth = 0;

    /* Try opening the file; use "rb" mode to read this *binary* file. */
    if((fp = fopen(file, "rb")) == NULL)
        return 0;

    /* Read the file header and any following bitmap information... */
    if(fread(&header, sizeof(BITMAPFILEHEADER), 1, fp) < 1)
    {
        /* Couldn't read the file header */
        fclose(fp);
        return 0;
    }

    if(header.bfType != 'MB') /* Check for BM reversed... */
    {
        /* Not a bitmap file */
        fclose(fp);
        return 0;
    }

    infosize = header.bfOffBits - sizeof(BITMAPFILEHEADER);
    if((info = (BITMAPINFO *)calloc(infosize, 1)) == NULL)
    {
        /* Couldn't allocate memory for bitmap info */
        fclose(fp);
        return 0;
    }

    if(fread(info, 1, infosize, fp) == infosize)
    {
        /* Read the bitmap header */
        depth = info->bmiHeader.biBitCount;
    }
    free(info);
    fclose(fp);
    return depth;
}
#endif

/*
 * Creates a pixmap from a file.
 * Parameters:
 *       handle: Window handle the pixmap is associated with.
 *       filename: Name of the file, omit extention to have
 *                 DW pick the appropriate file extension.
 *                 (BMP on OS/2 or Windows, XPM on Unix)
 * Returns:
 *       A handle to a pixmap or NULL on failure.
 */
HPIXMAP API dw_pixmap_new_from_file(HWND handle, const char *filename)
{
   HPIXMAP pixmap;
   BITMAP bm;
   HDC hdc;
#ifndef GDIPLUS
   char *file;
#endif

   if (!filename || !(pixmap = calloc(1,sizeof(struct _hpixmap))))
      return NULL;

#ifdef GDIPLUS
   pixmap->hbm = _dw_load_bitmap(filename, &pixmap->depth);
#else
   file = _alloca(strlen(filename) + 5);
   strcpy(file, filename);

   /* check if we can read from this file (it exists and read permission) */
   if(access(file, 04) != 0)
   {
      /* Try with .bmp extention */
      strcat(file, ".bmp");
      if(access(file, 04) != 0)
      {
         free(pixmap);
         free(file);
         return NULL;
      }
   }

   pixmap->hbm = (HBITMAP)LoadImage(NULL, UTF8toWide(file), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
   pixmap->depth = _dw_read_bitmap_header(file);
#endif

   pixmap->handle = handle;

   if ( !pixmap->hbm )
   {
      free(pixmap);
      return NULL;
   }

   hdc = GetDC(handle);

   pixmap->hdc = CreateCompatibleDC( hdc );
   GetObject( pixmap->hbm, sizeof(bm), &bm );
   pixmap->width = bm.bmWidth; pixmap->height = bm.bmHeight;
   SelectObject( pixmap->hdc, pixmap->hbm );
   ReleaseDC( handle, hdc );
   pixmap->transcolor = DW_RGB_TRANSPARENT;

   return pixmap;
}

/*
 * Creates a pixmap from memory.
 * Parameters:
 *       handle: Window handle the pixmap is associated with.
 *       data: Source of the image data
 *                 (BMP on OS/2 or Windows, XPM on Unix)
 *       le: length of data
 * Returns:
 *       A handle to a pixmap or NULL on failure.
 */
HPIXMAP API dw_pixmap_new_from_data(HWND handle, const char *data, int len)
{
   HPIXMAP pixmap;
   BITMAP bm;
   HDC hdc;
   char *file;
   FILE *fp;

   if ( !(pixmap = calloc(1,sizeof(struct _hpixmap))) )
   {
      return NULL;
   }

   hdc = GetDC(handle);

   pixmap->handle = handle;

   file = _tempnam( _dw_alternate_temp_dir, "dw" );
   if ( file != NULL )
   {
      fp = fopen( file, "wb" );
      if ( fp != NULL )
      {
         fwrite( data, 1, len, fp );
         fclose( fp );
#ifdef GDIPLUS
         pixmap->hbm = _dw_load_bitmap(file, NULL);
#else
         pixmap->hbm = (HBITMAP)LoadImage( NULL, UTF8toWide(file), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
         pixmap->depth = _dw_read_bitmap_header(file);
#endif
      }
      else
      {
         _unlink( file );
         free( file );
         free( pixmap );
         return NULL;
      }
      _unlink( file );
      free( file );
   }

   if ( !pixmap->hbm )
   {
      free( pixmap );
      ReleaseDC( handle, hdc );
      return NULL;
   }

   pixmap->hdc = CreateCompatibleDC( hdc );

   GetObject( pixmap->hbm, sizeof(bm), &bm );

   pixmap->width = bm.bmWidth; pixmap->height = bm.bmHeight;

   SelectObject( pixmap->hdc, pixmap->hbm );

   ReleaseDC( handle, hdc );
   pixmap->transcolor = DW_RGB_TRANSPARENT;

   return pixmap;
}

/*
 * Creates a bitmap mask for rendering bitmaps with transparent backgrounds
 */
void API dw_pixmap_set_transparent_color( HPIXMAP pixmap, ULONG color )
{
   if ( pixmap && pixmap->depth < 32)
   {
      pixmap->transcolor = _dw_internal_color(color);
   }
}

/*
 * Creates a pixmap from internal resource graphic specified by id.
 * Parameters:
 *       handle: Window handle the pixmap is associated with.
 *       id: Resource ID associated with requested pixmap.
 * Returns:
 *       A handle to a pixmap or NULL on failure.
 */
HPIXMAP API dw_pixmap_grab(HWND handle, ULONG id)
{
   HPIXMAP pixmap;
   BITMAP bm;
   HDC hdc;

   if (!(pixmap = calloc(1,sizeof(struct _hpixmap))))
      return NULL;

   hdc = GetDC(handle);

   pixmap->hbm = LoadBitmap(_DWInstance, MAKEINTRESOURCE(id));
   pixmap->hdc = CreateCompatibleDC(hdc);

   GetObject(pixmap->hbm, sizeof(BITMAP), (void *)&bm);

   pixmap->width = bm.bmWidth; pixmap->height = bm.bmHeight;
   pixmap->depth = bm.bmBitsPixel;

   SelectObject(pixmap->hdc, pixmap->hbm);

   ReleaseDC(handle, hdc);
   pixmap->transcolor = DW_RGB_TRANSPARENT;

   return pixmap;
}

/*
 * Sets the font used by a specified pixmap.
 * Normally the pixmap font is obtained from the associated window handle.
 * However this can be used to override that, or for pixmaps with no window.
 * Parameters:
 *          pixmap: Handle to a pixmap returned by dw_pixmap_new() or
 *                  passed to the application via a callback.
 *          fontname: Name and size of the font in the form "size.fontname"
 * Returns:
 *       DW_ERROR_NONE on success and DW_ERROR_GENERAL on failure.
 */
int API dw_pixmap_set_font(HPIXMAP pixmap, const char *fontname)
{
    if(pixmap)
    {
        HFONT hfont = _dw_acquire_font2(pixmap->hdc, fontname);

        if(hfont)
        {
            HFONT oldfont = pixmap->font;
            pixmap->font = hfont;
            if(oldfont)
                DeleteObject(oldfont);
            return DW_ERROR_NONE;
        }
    }
    return DW_ERROR_GENERAL;
}

/*
 * Destroys an allocated pixmap.
 * Parameters:
 *       pixmap: Handle to a pixmap returned by
 *               dw_pixmap_new..
 */
void API dw_pixmap_destroy(HPIXMAP pixmap)
{
   if(pixmap)
   {
      DeleteDC(pixmap->hdc);
      DeleteObject(pixmap->hbm);
      if(pixmap->font)
        DeleteObject(pixmap->font);
      free(pixmap);
   }
}

/*
 * Copies from one item to another.
 * Parameters:
 *       dest: Destination window handle.
 *       destp: Destination pixmap. (choose only one).
 *       xdest: X coordinate of destination.
 *       ydest: Y coordinate of destination.
 *       width: Width of area to copy.
 *       height: Height of area to copy.
 *       src: Source window handle.
 *       srcp: Source pixmap. (choose only one).
 *       xsrc: X coordinate of source.
 *       ysrc: Y coordinate of source.
 */
void API dw_pixmap_bitblt(HWND dest, HPIXMAP destp, int xdest, int ydest, int width, int height, HWND src, HPIXMAP srcp, int xsrc, int ysrc)
{
    dw_pixmap_stretch_bitblt(dest, destp, xdest, ydest, width, height, src, srcp, xsrc, ysrc, -1, -1);
}

/*
 * Copies from one surface to another allowing for stretching.
 * Parameters:
 *       dest: Destination window handle.
 *       destp: Destination pixmap. (choose only one).
 *       xdest: X coordinate of destination.
 *       ydest: Y coordinate of destination.
 *       width: Width of the target area.
 *       height: Height of the target area.
 *       src: Source window handle.
 *       srcp: Source pixmap. (choose only one).
 *       xsrc: X coordinate of source.
 *       ysrc: Y coordinate of source.
 *       srcwidth: Width of area to copy.
 *       srcheight: Height of area to copy.
 * Returns:
 *       DW_ERROR_NONE on success and DW_ERROR_GENERAL on failure.
 */
int API dw_pixmap_stretch_bitblt(HWND dest, HPIXMAP destp, int xdest, int ydest, int width, int height, HWND src, HPIXMAP srcp, int xsrc, int ysrc, int srcwidth, int srcheight)
{
   HDC hdcdest;
   HDC hdcsrc;
   static BLENDFUNCTION bf = { AC_SRC_OVER, 0, 0xFF, AC_SRC_ALPHA };
   int swidth = srcwidth, sheight = srcheight;

   /* Do some sanity checks */
   if ( dest )
      hdcdest = GetDC( dest );
   else if ( destp )
      hdcdest = destp->hdc;
   else
      return DW_ERROR_GENERAL;

   if ( src )
      hdcsrc = GetDC( src );
   else if ( srcp )
      hdcsrc = srcp->hdc;
   else
      return DW_ERROR_GENERAL;

   if((srcheight == -1 || srcwidth == -1) && srcheight != srcwidth)
      return DW_ERROR_GENERAL;

   if(srcheight == -1 && srcwidth == -1)
   {
       swidth = width;
       sheight = height;
   }

#ifdef GDIPLUS
   /* Do conversion on all the coordinates */
   _dw_convert_dpi(hdcdest, &xdest, &ydest, TRUE);
   _dw_convert_dpi(hdcdest, &width, &height, TRUE);
   _dw_convert_dpi(hdcsrc, &xsrc, &ysrc, TRUE);
   _dw_convert_dpi(hdcsrc, &swidth, &sheight, TRUE);
#endif

   /* If it is a 32bpp bitmap (with alpha) use AlphaBlend unless it fails */
   if ( srcp && srcp->depth == 32 && AlphaBlend( hdcdest, xdest, ydest, width, height, hdcsrc, xsrc, ysrc, swidth, sheight, bf ) )
   {
        /* Don't do anything */
   }
   /* Otherwise perform special bitblt with manual transparency */
   else if ( srcp && srcp->transcolor != DW_RGB_TRANSPARENT )
   {
      TransparentBlt( hdcdest, xdest, ydest, width, height, hdcsrc, xsrc, ysrc, swidth, sheight, RGB( DW_RED_VALUE(srcp->transcolor), DW_GREEN_VALUE(srcp->transcolor), DW_BLUE_VALUE(srcp->transcolor)) );
   }
   else
   {
      /* Finally fall back to the classic BitBlt */
      if( srcwidth == -1 && srcheight == -1)
         BitBlt( hdcdest, xdest, ydest, width, height, hdcsrc, xsrc, ysrc, SRCCOPY );
      else
         StretchBlt( hdcdest, xdest, ydest, width, height, hdcsrc, xsrc, ysrc, swidth, sheight, SRCCOPY );
   }
   if ( !destp )
      ReleaseDC( dest, hdcdest );
   if ( !srcp )
      ReleaseDC( src, hdcsrc );

   return DW_ERROR_NONE;
}

/* Run Beep() in a separate thread so it doesn't block */
void _dw_beep_thread(void *data)
{
   int *info = (int *)data;

   if(data)
   {
      Beep(info[0], info[1]);
      free(data);
   }
}

/*
 * Emits a beep.
 * Parameters:
 *       freq: Frequency.
 *       dur: Duration.
 */
void API dw_beep(int freq, int dur)
{
   int *info = malloc(sizeof(int) * 2);

   if(info)
   {
      info[0] = freq;
      info[1] = dur;

      _beginthread(_dw_beep_thread, 100, (void *)info);
   }
}

/* Open a shared library and return a handle.
 * Parameters:
 *         name: Base name of the shared library.
 *         handle: Pointer to a module handle,
 *                 will be filled in with the handle.
 */
int API dw_module_load(const char *name, HMOD *handle)
{
   if(!handle)
      return DW_ERROR_UNKNOWN;

   *handle = LoadLibrary(UTF8toWide(name));
   return (NULL == *handle);
}

/* Queries the address of a symbol within open handle.
 * Parameters:
 *         handle: Module handle returned by dw_module_load()
 *         name: Name of the symbol you want the address of.
 *         func: A pointer to a function pointer, to obtain
 *               the address.
 */
int API dw_module_symbol(HMOD handle, const char *name, void**func)
{
   if(!func || !name)
      return DW_ERROR_UNKNOWN;

   if(0 == strlen(name))
      return DW_ERROR_UNKNOWN;

   *func = (void*)GetProcAddress(handle, name);
   return   (NULL == *func);
}

/* Frees the shared library previously opened.
 * Parameters:
 *         handle: Module handle returned by dw_module_load()
 */
int API dw_module_close(HMOD handle)
{
   return FreeLibrary(handle);
}

/*
 * Returns the handle to an unnamed mutex semaphore.
 */
HMTX API dw_mutex_new(void)
{
   return (HMTX)CreateMutex(NULL, FALSE, NULL);
}

/*
 * Closes a semaphore created by dw_mutex_new().
 * Parameters:
 *       mutex: The handle to the mutex returned by dw_mutex_new().
 */
void API dw_mutex_close(HMTX mutex)
{
   CloseHandle((HANDLE)mutex);
}

/*
 * Tries to gain access to the semaphore, if it can't it blocks.
 * Parameters:
 *       mutex: The handle to the mutex returned by dw_mutex_new().
 */
void API dw_mutex_lock(HMTX mutex)
{
   if(_dwtid == dw_thread_id())
   {
      int rc = WaitForSingleObject((HANDLE)mutex, 0);

      while(rc == WAIT_TIMEOUT)
      {
         dw_main_sleep(1);
         rc = WaitForSingleObject((HANDLE)mutex, 0);
      }
   }
    else
      WaitForSingleObject((HANDLE)mutex, INFINITE);
}

/*
 * Tries to gain access to the semaphore.
 * Parameters:
 *       mutex: The handle to the mutex returned by dw_mutex_new().
 * Returns:
 *       DW_ERROR_NONE on success, DW_ERROR_TIMEOUT if it is already locked.
 */
int API dw_mutex_trylock(HMTX mutex)
{
    if(WaitForSingleObject((HANDLE)mutex, 0) == WAIT_OBJECT_0)
        return DW_ERROR_NONE;
    return DW_ERROR_TIMEOUT;
}

/*
 * Reliquishes the access to the semaphore.
 * Parameters:
 *       mutex: The handle to the mutex returned by dw_mutex_new().
 */
void API dw_mutex_unlock(HMTX mutex)
{
   ReleaseMutex((HANDLE)mutex);
}

/*
 * Returns the handle to an unnamed event semaphore.
 */
HEV API dw_event_new(void)
{
    return CreateEvent(NULL, TRUE, FALSE, NULL);
}

/*
 * Resets a semaphore created by dw_event_new().
 * Parameters:
 *       eve: The handle to the event returned by dw_event_new().
 */
int API dw_event_reset(HEV eve)
{
   if(ResetEvent(eve))
      return DW_ERROR_NONE;
   return DW_ERROR_GENERAL;
}

/*
 * Posts a semaphore created by dw_event_new(). Causing all threads
 * waiting on this event in dw_event_wait to continue.
 * Parameters:
 *       eve: The handle to the event returned by dw_event_new().
 */
int API dw_event_post(HEV eve)
{
   if(SetEvent(eve))
      return DW_ERROR_NONE;
   return DW_ERROR_GENERAL;
}

/*
 * Waits on a semaphore created by dw_event_new(), until the
 * event gets posted or until the timeout expires.
 * Parameters:
 *       eve: The handle to the event returned by dw_event_new().
 */
int API dw_event_wait(HEV eve, unsigned long timeout)
{
   int rc;

   rc = WaitForSingleObject(eve, timeout != -1 ? timeout : INFINITE);
   if(rc == WAIT_OBJECT_0)
      return DW_ERROR_NONE;
   if(rc == WAIT_TIMEOUT)
      return DW_ERROR_TIMEOUT;
   if(rc == WAIT_ABANDONED)
      return DW_ERROR_INTERRUPT;
   return DW_ERROR_GENERAL;
}

/*
 * Closes a semaphore created by dw_event_new().
 * Parameters:
 *       eve: The handle to the event returned by dw_event_new().
 */
int API dw_event_close(HEV *eve)
{
   if(eve && CloseHandle(*eve))
      return DW_ERROR_NONE;
   return DW_ERROR_GENERAL;
}

/* Create a named event semaphore which can be
 * opened from other processes.
 * Parameters:
 *         eve: Pointer to an event handle to receive handle.
 *         name: Name given to semaphore which can be opened
 *               by other processes.
 */
HEV API dw_named_event_new(const char *name)
{
   SECURITY_ATTRIBUTES sa;

   sa.nLength = sizeof( SECURITY_ATTRIBUTES);
   sa.lpSecurityDescriptor = &_dwsd;
   sa.bInheritHandle = FALSE;

   return CreateEvent(&sa, TRUE, FALSE, UTF8toWide(name));
}

/* Destroy this semaphore.
 * Parameters:
 *         eve: Handle to the semaphore obtained by
 *              a create call.
 */
HEV API dw_named_event_get(const char *name)
{
   return OpenEvent(EVENT_ALL_ACCESS, FALSE, UTF8toWide(name));
}

/* Resets the event semaphore so threads who call wait
 * on this semaphore will block.
 * Parameters:
 *         eve: Handle to the semaphore obtained by
 *              an open or create call.
 */
int API dw_named_event_reset(HEV eve)
{
   if(ResetEvent(eve))
      return DW_ERROR_NONE;
   return DW_ERROR_GENERAL;
}

/* Sets the posted state of an event semaphore, any threads
 * waiting on the semaphore will no longer block.
 * Parameters:
 *         eve: Handle to the semaphore obtained by
 *              an open or create call.
 */
int API dw_named_event_post(HEV eve)
{
   if(SetEvent(eve))
      return DW_ERROR_NONE;
   return DW_ERROR_GENERAL;
}

/* Waits on the specified semaphore until it becomes
 * posted, or returns immediately if it already is posted.
 * Parameters:
 *         eve: Handle to the semaphore obtained by
 *              an open or create call.
 *         timeout: Number of milliseconds before timing out
 *                  or -1 if indefinite.
 */
int API dw_named_event_wait(HEV eve, unsigned long timeout)
{
   int rc;

   rc = WaitForSingleObject(eve, timeout != -1 ? timeout : INFINITE);
   if(rc == WAIT_OBJECT_0)
      return DW_ERROR_NONE;
   if(rc == WAIT_TIMEOUT)
      return DW_ERROR_TIMEOUT;
   if(rc == WAIT_ABANDONED)
      return DW_ERROR_INTERRUPT;
   return DW_ERROR_GENERAL;
}

/* Release this semaphore, if there are no more open
 * handles on this semaphore the semaphore will be destroyed.
 * Parameters:
 *         eve: Handle to the semaphore obtained by
 *              an open or create call.
 */
int API dw_named_event_close(HEV eve)
{
   if(CloseHandle(eve))
      return DW_ERROR_NONE;
   return DW_ERROR_GENERAL;
}

/*
 * Allocates a shared memory region with a name.
 * Parameters:
 *         handle: A pointer to receive a SHM identifier.
 *         dest: A pointer to a pointer to receive the memory address.
 *         size: Size in bytes of the shared memory region to allocate.
 *         name: A string pointer to a unique memory name.
 */
HSHM API dw_named_memory_new(void **dest, int size, const char *name)
{
   SECURITY_ATTRIBUTES sa;
   HSHM handle;

   sa.nLength = sizeof(SECURITY_ATTRIBUTES);
   sa.lpSecurityDescriptor = &_dwsd;
   sa.bInheritHandle = FALSE;

   handle = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, size, UTF8toWide(name));

   if(!handle)
      return 0;

   *dest = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);

   if(!*dest)
   {
      CloseHandle(handle);
      return 0;
   }

   return handle;
}

/*
 * Aquires shared memory region with a name.
 * Parameters:
 *         dest: A pointer to a pointer to receive the memory address.
 *         size: Size in bytes of the shared memory region to requested.
 *         name: A string pointer to a unique memory name.
 */
HSHM API dw_named_memory_get(void **dest, int size, const char *name)
{
   HSHM handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, UTF8toWide(name));

   if(!handle)
      return 0;

   *dest = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);

   if(!*dest)
   {
      CloseHandle(handle);
      return 0;
   }

   return handle;
}

/*
 * Frees a shared memory region previously allocated.
 * Parameters:
 *         handle: Handle obtained from DB_named_memory_allocate.
 *         ptr: The memory address aquired with DB_named_memory_allocate.
 */
int API dw_named_memory_free(HSHM handle, void *ptr)
{
   UnmapViewOfFile(ptr);
   CloseHandle(handle);
   return 0;
}

/*
 * Generally an internal function called from a newly created
 * thread to setup the Dynamic Windows environment for the thread.
 * However it is exported so language bindings can call it when
 * they create threads that require access to Dynamic Windows.
 */
void API _dw_init_thread(void)
{
    COLORREF foreground = RGB(128,128,128);
    COLORREF background = DW_RGB_TRANSPARENT;
#ifdef GDIPLUS
    ARGB gpfore = MAKEARGB(255, 128, 128, 128);
    GpBrush *brush;
    GpPen *pen;

    GdipCreatePen1(gpfore, 1.0, UnitPixel, &pen);
    TlsSetValue(_dw_gppen, (LPVOID)pen);
    GdipCreateSolidFill(gpfore, &brush);
    TlsSetValue(_dw_gpbrush, brush);
#endif
    TlsSetValue(_dw_foreground, DW_UINT_TO_POINTER(foreground));
    TlsSetValue(_dw_background, DW_UINT_TO_POINTER(background));
    TlsSetValue(_dw_hpen, CreatePen(PS_SOLID, 1, foreground));
    TlsSetValue(_dw_hbrush, CreateSolidBrush(foreground));
}

/*
 * Generally an internal function called from a terminating
 * thread to cleanup the Dynamic Windows environment for the thread.
 * However it is exported so language bindings can call it when
 * they exit threads that require access to Dynamic Windows.
 */
void API _dw_deinit_thread(void)
{
   HPEN hPen;
   HBRUSH hBrush;
#ifdef GDIPLUS
   GpBrush *brush;
   GpPen *pen;
#endif

   if((hPen = TlsGetValue(_dw_hpen)))
       DeleteObject(hPen);
   if((hBrush = TlsGetValue(_dw_hbrush)))
       DeleteObject(hBrush);
#ifdef GDIPLUS
   if((brush = TlsGetValue(_dw_gpbrush)))
      GdipDeleteBrush(brush);
   if((pen = TlsGetValue(_dw_gppen)))
      GdipDeletePen(pen);
#endif
}

/*
 * Encapsulate thread creation on Win32.
 */
void _dwthreadstart(void *data)
{
   void (* threadfunc)(void *) = NULL;
   void **tmp = (void **)data;

   _dw_init_thread();

   threadfunc = (void (*)(void *))tmp[0];
   threadfunc(tmp[1]);

   free(tmp);
   _dw_deinit_thread();
}

/*
 * Creates a new thread with a starting point of func.
 * Parameters:
 *       func: Function which will be run in the new thread.
 *       data: Parameter(s) passed to the function.
 *       stack: Stack size of new thread (OS/2 and Windows only).
 */
DWTID API dw_thread_new(void *func, void *data, int stack)
{
#if defined(__CYGWIN__)
   return 0;
#else
   void **tmp = malloc(sizeof(void *) * 2);

   tmp[0] = func;
   tmp[1] = data;

   return (DWTID)_beginthread((void(*)(void *))_dwthreadstart, stack, tmp);
#endif
}

/*
 * Ends execution of current thread immediately.
 */
void API dw_thread_end(void)
{
#if !defined(__CYGWIN__)
   _dw_deinit_thread();
   _endthread();
#endif
}

/*
 * Returns the current thread's ID.
 */
DWTID API dw_thread_id(void)
{
#if defined(__CYGWIN__)
   return 0;
#else
   return (DWTID)GetCurrentThreadId();
#endif
}

/*
 * Cleanly terminates a DW session, should be signal handler safe.
 */
void API dw_shutdown(void)
{
   OleUninitialize();
#ifdef AEROGLASS
   /* Free any in use libraries */
   FreeLibrary(hdwm);
#endif
   FreeLibrary(huxtheme);
   DestroyWindow(_dw_tooltip);
}

/*
 * Cleanly terminates a DW session, should be signal handler safe.
 * Parameters:
 *       exitcode: Exit code reported to the operating system.
 */
void API dw_exit(int exitcode)
{
   dw_shutdown();
   exit(exitcode);
}

/*
 * Creates a splitbar window (widget) with given parameters.
 * Parameters:
 *       type: Value can be DW_VERT or DW_HORZ.
 *       topleft: Handle to the window to be top or left.
 *       bottomright:  Handle to the window to be bottom or right.
 * Returns:
 *       A handle to a splitbar window or NULL on failure.
 */
HWND API dw_splitbar_new(int type, HWND topleft, HWND bottomright, unsigned long id)
{
   HWND tmp = CreateWindow(SplitbarClassName,
                     NULL,
                     WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN,
                     0,0,0,0,
                     DW_HWND_OBJECT,
                     (HMENU)(uintptr_t)id,
                     _DWInstance,
                     NULL);

   if(tmp)
   {
      HWND tmpbox = dw_box_new(DW_VERT, 0);
      float *percent = (float *)malloc(sizeof(float));

      dw_box_pack_start(tmpbox, topleft, 1, 1, TRUE, TRUE, 0);
      SetParent(tmpbox, tmp);
      dw_window_set_data(tmp, "_dw_topleft", DW_POINTER(tmpbox));

      tmpbox = dw_box_new(DW_VERT, 0);
      dw_box_pack_start(tmpbox, bottomright, 1, 1, TRUE, TRUE, 0);
      SetParent(tmpbox, tmp);
      dw_window_set_data(tmp, "_dw_bottomright", DW_POINTER(tmpbox));
      *percent = 50.0;
      dw_window_set_data(tmp, "_dw_percent", DW_POINTER(percent));
      dw_window_set_data(tmp, "_dw_type", DW_INT_TO_POINTER(type));
   }
   return tmp;
}

/*
 * Sets the position of a splitbar (pecentage).
 * Parameters:
 *       handle: The handle to the splitbar returned by dw_splitbar_new().
 */
void API dw_splitbar_set(HWND handle, float percent)
{
   float *mypercent = (float *)dw_window_get_data(handle, "_dw_percent");
   int type = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_type"));
   unsigned long width, height;

   if(mypercent)
      *mypercent = percent;

   dw_window_get_pos_size(handle, NULL, NULL, &width, &height);

   if(width > 0 && height > 0)
      _dw_handle_splitbar_resize(handle, percent, type, width, height);
}

/*
 * Gets the position of a splitbar (pecentage).
 * Parameters:
 *       handle: The handle to the splitbar returned by dw_splitbar_new().
 */
float API dw_splitbar_get(HWND handle)
{
   float *percent = (float *)dw_window_get_data(handle, "_dw_percent");

   if(percent)
      return *percent;
   return 0.0;
}

/*
 * Creates a calendar window (widget) with given parameters.
 * Parameters:
 *       type: Value can be DW_VERT or DW_HORZ.
 *       topleft: Handle to the window to be top or left.
 *       bottomright:  Handle to the window to be bottom or right.
 * Classname: SysMonthCal32
 * Returns:
 *       A handle to a calendar window or NULL on failure.
 */
HWND API dw_calendar_new(unsigned long id)
{
   RECT rc;
   MONTHDAYSTATE mds[3];
   HWND tmp = CreateWindowEx(WS_EX_CLIENTEDGE,
                           MONTHCAL_CLASS,
                           NULL,
                           WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | MCS_DAYSTATE,
                           0,0,0,0,
                           DW_HWND_OBJECT,
                           (HMENU)(uintptr_t)id,
                           _DWInstance,
                           NULL);
   if ( tmp )
   {
      // Get the size required to show an entire month.
      MonthCal_GetMinReqRect(tmp, &rc);
      // Resize the control now that the size values have been obtained.
      SetWindowPos(tmp, NULL, 0, 0,
               rc.right, rc.bottom,
               SWP_NOZORDER | SWP_NOMOVE);
      mds[0] = mds[1] = mds[2] = (MONTHDAYSTATE)0;
      MonthCal_SetDayState(tmp,3,mds);
      return tmp;
   }
   else
      return NULL;
}

/*
 * Sets the current date of a calendar
 * Parameters:
 *       handle: The handle to the calendar returned by dw_calendar_new().
 *       year:   The year to set the date to
 *       month:  The month to set the date to
 *       day:    The day to set the date to
 */
void API dw_calendar_set_date(HWND handle, unsigned int year, unsigned int month, unsigned int day)
{
   MONTHDAYSTATE mds[3];
   SYSTEMTIME date;
   date.wYear = year;
   date.wMonth = month;
   date.wDay = day;
   if ( MonthCal_SetCurSel( handle, &date ) )
   {
   }
   else
   {
   }
   mds[0] = mds[1] = mds[2] = (MONTHDAYSTATE)0;
   MonthCal_SetDayState(handle,3,mds);
}

/*
 * Gets the date from the calendar
 * Parameters:
 *       handle: The handle to the calendar returned by dw_calendar_new().
 *       year:   Pointer to the year to get the date to
 *       month:  Pointer to the month to get the date to
 *       day:    Pointer to the day to get the date to
 */
void API dw_calendar_get_date(HWND handle, unsigned int *year, unsigned int *month, unsigned int *day)
{
   SYSTEMTIME date;
   if ( MonthCal_GetCurSel( handle, &date ) )
   {
      *year = date.wYear;
      *month = date.wMonth;
      *day = date.wDay;
   }
   else
   {
      *year = *month = *day = 0;
   }
}

/* Internal function to set the focus from the window thread */
void _dw_set_focus(HWND handle)
{
    SetFocus(handle);
}

/*
 * Sets the current focus item for a window/dialog.
 * Parameters:
 *         handle: Handle to the dialog item to be focused.
 * Remarks:
 *          This is for use after showing the window/dialog.
 */
void API dw_window_set_focus(HWND handle)
{
    if(_dwtid == (DWTID)-1)
        SetFocus(handle);
    else
        dw_window_function(handle, (void *)_dw_set_focus, handle);
}

/*
 * Sets the default focus item for a window/dialog.
 * Parameters:
 *         window: Toplevel window or dialog.
 *         defaultitem: Handle to the dialog item to be default.
 * Remarks:
 *          This is for use before showing the window/dialog.
 */
void API dw_window_default(HWND window, HWND defaultitem)
{
   Box *thisbox = (Box *)GetWindowLongPtr(window, GWLP_USERDATA);

   if(thisbox)
      thisbox->defaultitem = defaultitem;
}

/*
 * Sets window to click the default dialog item when an ENTER is pressed.
 * Parameters:
 *         window: Window (widget) to look for the ENTER press.
 *         next: Window (widget) to move to next (or click)
 */
void API dw_window_click_default(HWND window, HWND next)
{
   ColorInfo *cinfo = _dw_window_get_cinfo(window);

   if(cinfo)
      cinfo->clickdefault = next;
}

/*
 * Gets the contents of the default clipboard as text.
 * Parameters:
 *       None.
 * Returns:
 *       Pointer to an allocated string of text or NULL if clipboard empty or contents could not
 *       be converted to text.
 */
char * API dw_clipboard_get_text(void)
{
   HANDLE handle;
   char *ret = NULL;
   TCHAR *tmp;
#ifdef UNICODE
   int type = CF_UNICODETEXT;
#else
   int type = CF_TEXT;
#endif

   if ( !OpenClipboard( NULL ) )
      return ret;

   if ( ( handle = GetClipboardData(type) ) == NULL )
   {
      CloseClipboard();
      return ret;
   }

   if ( (tmp = GlobalLock(handle)) && _tcslen(tmp) )
   {
        ret = _strdup(WideToUTF8(tmp));
        GlobalUnlock(handle);
   }
   CloseClipboard();
   return ret;
}

/*
 * Sets the contents of the default clipboard to the supplied text.
 * Parameters:
 *       Text.
 */
void API dw_clipboard_set_text(const char *str, int len)
{
   HGLOBAL ptr1;
   LPTSTR ptr2;
   TCHAR *buf;
#ifdef UNICODE
   int type = CF_UNICODETEXT;
   char *src = calloc(len + 1, 1);

   memcpy(src, str, len);
   buf = UTF8toWide(src);
   free(src);
   len = (int)_tcslen(buf);
#else
   int type = CF_TEXT;

   buf = str;
#endif

   if ( !OpenClipboard( NULL ) )
      return;

   ptr1 = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, (len + 1) * sizeof(TCHAR) );

   if ( !ptr1 )
      return;

   ptr2 = GlobalLock( ptr1 );

   memcpy(ptr2, buf, (len + 1) * sizeof(TCHAR));
   GlobalUnlock( ptr1 );
   EmptyClipboard();

   SetClipboardData( type, ptr1 );

   CloseClipboard();
   GlobalFree( ptr1 );
}

/*
 * Creates a new system notification if possible.
 * Parameters:
 *         title: The short title of the notification.
 *         imagepath: Path to an image to display or NULL if none.
 *         description: A longer description of the notification,
 *                      or NULL if none is necessary.
 * Returns:
 *         A handle to the notification which can be used to attach a "clicked" event if desired,
 *         or NULL if it fails or notifications are not supported by the system.
 * Remarks:
 *          This will create a system notification that will show in the notifaction panel
 *          on supported systems, which may be clicked to perform another task.
 */
HWND API dw_notification_new(const char *title, const char *imagepath, const char *description, ...)
{
#ifdef BUILD_TOAST
   char outbuf[1025] = {0};

   if(description)
   {
      va_list args;

      va_start(args, description);
      vsnprintf(outbuf, 1024, description, args);
      va_end(args);
   }
   return (HWND)_dw_notification_new(UTF8toWide(title), imagepath ? UTF8toWide(imagepath) : NULL, UTF8toWide(outbuf));
#else
   return NULL;
#endif
}

/*
 * Sends a notification created by dw_notification_new() after attaching signal handler.
 * Parameters:
 *         notification: The handle to the notification returned by dw_notification_new().
 * Returns:
 *         DW_ERROR_NONE on success, DW_ERROR_UNKNOWN on error or not supported.
 */
int API dw_notification_send(HWND notification)
{
#ifdef BUILD_TOAST
   return _dw_notification_send((void *)notification);
#else
   return DW_ERROR_UNKNOWN;
#endif
}

/*
 * Returns some information about the current operating environment.
 * Parameters:
 *       env: Pointer to a DWEnv struct.
 */
void API dw_environment_query(DWEnv *env)
{
   if(!env)
      return;

   /* Get the Windows version. */

   env->MajorVersion =  (DWORD)(LOBYTE(LOWORD(_dwVersion)));
   env->MinorVersion =  (DWORD)(HIBYTE(LOWORD(_dwVersion)));

   /* Get the build number for Windows NT/Windows 2000. */

   env->MinorBuild =  0;

   if (_dwVersion < 0x80000000)
   {
      env->MajorBuild = (DWORD)(HIWORD(_dwVersion));

      if(env->MajorVersion == 5 && env->MinorVersion == 0)
         strcpy(env->osName, "Windows 2000");
      else if(env->MajorVersion == 5 && env->MinorVersion > 0)
         strcpy(env->osName, "Windows XP");
      else if(env->MajorVersion == 6 && env->MinorVersion == 0)
         strcpy(env->osName, "Windows Vista");
      else if(env->MajorVersion == 6 && env->MinorVersion == 1)
         strcpy(env->osName, "Windows 7");
      else if(env->MajorVersion == 6 && env->MinorVersion > 1)
         strcpy(env->osName, "Windows 8");
      else if(env->MajorVersion == 10)
      {
         if(env->MajorBuild < 20000)
            strcpy(env->osName, "Windows 10");
         else 
            strcpy(env->osName, "Windows 11");
      }
      else
         strcpy(env->osName, "Windows NT");
   }
   else
   {
      strcpy(env->osName, "Windows 95/98/ME");
      env->MajorBuild =  0;
   }

   strcpy(env->buildDate, __DATE__);
   strcpy(env->buildTime, __TIME__);
#if (defined(BUILD_DLL) || defined(BUILD_HTML))
#  ifdef BUILD_EDGE
   strcpy(env->htmlEngine, _DW_EDGE_DETECTED ? "EDGE" : "IE");
#  else
   strcpy(env->htmlEngine, "IE");
#  endif
#else
   strcpy(env->htmlEngine, "N/A");
#endif

   env->DWMajorVersion = DW_MAJOR_VERSION;
   env->DWMinorVersion = DW_MINOR_VERSION;
#ifdef VER_REV
   env->DWSubVersion = VER_REV;
#else
   env->DWSubVersion = DW_SUB_VERSION;
#endif
}

/* Helper to make sure all /s are \s */
void _dw_to_dos(TCHAR *dst, TCHAR *src)
{
   int x = 0;

   while(src[x])
   {
      if(src[x] == TEXT('/'))
         dst[x] = TEXT('\\');
      else
         dst[x] = src[x];
      x++;
   }
   dst[x] = 0;
}

#define BROWSEBUFSIZE 1000

/*
 * Opens a file dialog and queries user selection.
 * Parameters:
 *       title: Title bar text for dialog.
 *       defpath: The default path of the open dialog.
 *       ext: Default file extention.
 *       flags: DW_FILE_OPEN or DW_FILE_SAVE or DW_DIRECTORY_OPEN.
 * Returns:
 *       NULL on error. A malloced buffer containing
 *       the file path on success.
 *
 */
char * API dw_file_browse(const char *title, const char *defpath, const char *ext, int flags)
{
   OPENFILENAME of = {0};
   TCHAR filenamebuf[BROWSEBUFSIZE+1] = {0}, *fbuf = filenamebuf;
   TCHAR filterbuf[BROWSEBUFSIZE+1] = {0};
   TCHAR *exten = UTF8toWide(ext);
   TCHAR *dpath = UTF8toWide(defpath);
   int rc;

   if ( flags == DW_DIRECTORY_OPEN )
   {
   /* If we aren't building a DLL, use the more simple browser */
#ifndef BUILD_DLL
      BROWSEINFO bi = {0};
      TCHAR szDir[MAX_PATH];
      LPITEMIDLIST pidl;
      LPMALLOC pMalloc;

      if (SUCCEEDED(SHGetMalloc(&pMalloc)))
      {
         bi.hwndOwner = NULL;
         bi.pszDisplayName = 0;
         bi.pidlRoot = 0;
         bi.lpszTitle = UTF8toWide(title);
         bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
         bi.lpfn = NULL; /*BrowseCallbackProc*/

         pidl = SHBrowseForFolder(&bi);
         if (pidl)
         {
            if (SHGetPathFromIDList(pidl,szDir))
            {
               _tcsncpy(filenamebuf,szDir,BROWSEBUFSIZE);
            }

            /* In C++: pMalloc->Free(pidl); pMalloc->Release(); */
            pMalloc->lpVtbl->Free(pMalloc,pidl);
            pMalloc->lpVtbl->Release(pMalloc);
            return _strdup(WideToUTF8(filenamebuf));
         }
      }
#else
     if ( XBrowseForFolder( NULL,
                            (LPCTSTR)dpath,
                            -1,
                            (LPCTSTR)UTF8toWide(title),
                            (LPTSTR)filenamebuf,
                            BROWSEBUFSIZE,
                            FALSE ) )
     {
        return _strdup( WideToUTF8(fbuf) );
     }
#endif
   }
   else
   {
      DWORD att = defpath ? GetFileAttributes(UTF8toWide(defpath)) : INVALID_FILE_ATTRIBUTES;

      if (ext)
      {
         /*
          * The following mess is because sprintf() trunates at first \0
          * and format of filter is eg: "c files (*.c)\0*.c\0All Files\0*.*\0\0"
          */
         int len;
         TCHAR *ptr = filterbuf;
         TCHAR *start = filterbuf;

         len = _sntprintf( ptr, BROWSEBUFSIZE - (ptr - start), TEXT("%s Files (*.%s)"), exten, exten );
         ptr = ptr + len + 1; /* past first \0 */
         len = _sntprintf( ptr, BROWSEBUFSIZE - (ptr - start), TEXT("*.%s"), exten );
         ptr = ptr + len + 1; /* past next \0 */
         len = _sntprintf( ptr, BROWSEBUFSIZE - (ptr - start), TEXT("All Files") );
         ptr = ptr + len + 1; /* past next \0 */
         len = _sntprintf( ptr, BROWSEBUFSIZE - (ptr - start), TEXT("*.*") );
      }

      memset( &of, 0, sizeof(OPENFILENAME) );

      of.lStructSize = sizeof(OPENFILENAME);
      of.hwndOwner = HWND_DESKTOP;
      of.hInstance = _DWInstance;
      of.lpstrTitle = UTF8toWide(title);
      of.lpstrInitialDir = TEXT(".");
      if(att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY))
        of.lpstrInitialDir = dpath;
      else if(defpath)
        _dw_to_dos(filenamebuf, dpath);
      of.lpstrFile = filenamebuf;
      of.lpstrFilter = filterbuf;
      of.nFilterIndex = 1;
      of.nMaxFile = BROWSEBUFSIZE;
      /*of.lpstrDefExt = ext;*/
      of.Flags = OFN_NOCHANGEDIR;

      if (flags & DW_FILE_SAVE)
      {
         of.Flags |= OFN_OVERWRITEPROMPT;
         rc = GetSaveFileName(&of);
      }
      else
      {
         of.Flags |= OFN_FILEMUSTEXIST;
         rc = GetOpenFileName(&of);
      }

      if (rc)
         return _strdup(WideToUTF8(of.lpstrFile));
   }
   return NULL;
}

/*
 * Execute and external program in a seperate session.
 * Parameters:
 *       program: Program name with optional path.
 *       type: Either DW_EXEC_CON or DW_EXEC_GUI.
 *       params: An array of pointers to string arguements.
 * Returns:
 *       -1 on error.
 */
int API dw_exec(const char *program, int type, char **params)
{
   char **newparams;
   int retcode, count = 0, z;

   while(params[count])
   {
      count++;
   }

   newparams = (char **)malloc(sizeof(char *) * (count+1));

   for(z=0;z<count;z++)
   {
      if(strchr(params[z], ' '))
      {		
         newparams[z] = malloc(strlen(params[z])+3);
         strcpy(newparams[z], "\"");
         strcat(newparams[z], params[z]);
         strcat(newparams[z], "\"");
      }
      else
         newparams[z] = _strdup(params[z]);
   }
   newparams[count] = NULL;

   /* Why does this return intptr_t ... can the PID exceed an integer value? */
   retcode = (int)_spawnvp(P_NOWAIT, program, (const char * const *)newparams);

   for(z=0;z<count;z++)
   {
      free(newparams[z]);
   }
   free(newparams);

   return retcode;
}

/*
 * Loads a web browser pointed at the given URL.
 * Parameters:
 *       url: Uniform resource locator.
 */
int API dw_browse(const char *url)
{
   char *browseurl = _strdup(url);
   int retcode;

   if(strlen(browseurl) > 7 && strncmp(browseurl, "file://", 7) == 0)
   {
      int len, z;

      browseurl = &browseurl[7];
      len = (int)strlen(browseurl);

      for(z=0;z<len;z++)
      {
         if(browseurl[z] == '|')
            browseurl[z] = ':';
         if(browseurl[z] == '/')
            browseurl[z] = '\\';
      }
   }

   retcode = DW_POINTER_TO_INT(ShellExecute(NULL, TEXT("open"), UTF8toWide(browseurl), NULL, NULL, SW_SHOWNORMAL));
   free(browseurl);
   if(retcode<33 && retcode != 2)
      return DW_ERROR_UNKNOWN;
   return DW_ERROR_NONE;
}

typedef struct _dwprint
{
    PRINTDLG pd;
    DOCINFO di;
    int (DWSIGNAL *drawfunc)(HPRINT, HPIXMAP, int, void *);
    void *drawdata;
    unsigned long flags;
} DWPrint;

/*
 * Creates a new print object.
 * Parameters:
 *       jobname: Name of the print job to show in the queue.
 *       flags: Flags to initially configure the print object.
 *       pages: Number of pages to print.
 *       drawfunc: The pointer to the function to be used as the callback.
 *       drawdata: User data to be passed to the handler function.
 * Returns:
 *       A handle to the print object or NULL on failure.
 */
HPRINT API dw_print_new(const char *jobname, unsigned long flags, unsigned int pages, void *drawfunc, void *drawdata)
{
    DWPrint *print;

    if(!drawfunc || !(print = calloc(1, sizeof(DWPrint))))
        return NULL;

    print->drawfunc = drawfunc;
    print->drawdata = drawdata;
    print->pd.lStructSize = sizeof(PRINTDLG);
    print->pd.hwndOwner = HWND_DESKTOP;
    print->pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC;
    print->pd.nCopies = 1;
    print->pd.nFromPage = 1;
    print->pd.nToPage = pages;
    print->pd.nMinPage = 1;
    print->pd.nMaxPage = pages;

    if(!PrintDlg(&(print->pd)))
    {
        free(print);
        return NULL;
    }

    print->di.cbSize = sizeof(DOCINFO);
    print->di.lpszDocName = jobname ? UTF8toWide(jobname) : TEXT("Dynamic Windows Print Job");
    return print;
}

/*
 * Runs the print job, causing the draw page callbacks to fire.
 * Parameters:
 *       print: Handle to the print object returned by dw_print_new().
 *       flags: Flags to run the print job.
 * Returns:
 *       DW_ERROR_UNKNOWN on error or DW_ERROR_NONE on success.
 */
int API dw_print_run(HPRINT print, unsigned long flags)
{
    DWPrint *p = print;
    HPIXMAP pixmap;
    int x, width, height, result = DW_ERROR_UNKNOWN;

    if(!p)
        return result;

    if (!(pixmap = calloc(1,sizeof(struct _hpixmap))))
        return result;

    width = GetDeviceCaps(p->pd.hDC, HORZRES);
    height = GetDeviceCaps(p->pd.hDC, VERTRES);

    pixmap->hbm = CreateCompatibleBitmap(p->pd.hDC, width, height);
    pixmap->hdc = p->pd.hDC;
    pixmap->transcolor = DW_RGB_TRANSPARENT;

#ifdef GDIPLUS
   /* Convert the size based on the DPI */
   _dw_convert_dpi(pixmap->hdc, &width, &height, FALSE);
#endif

    pixmap->width = width;
    pixmap->height = height;

    SelectObject(pixmap->hdc, pixmap->hbm);

    /* Start the job */
    StartDoc(p->pd.hDC, &(p->di));

    /* Cycle through each page */
    for(x=p->pd.nFromPage-1; x<p->pd.nToPage && p->drawfunc; x++)
    {
        StartPage(p->pd.hDC);
        p->drawfunc(print, pixmap, x, p->drawdata);
        EndPage(p->pd.hDC);
    }
    if(p->drawfunc)
    {
        result = DW_ERROR_NONE;
        EndDoc(p->pd.hDC);
    }
    else
        AbortDoc(p->pd.hDC);
    /* Free memory */
    dw_pixmap_destroy(pixmap);
    free(p);
    return result;
}

/*
 * Cancels the print job, typically called from a draw page callback.
 * Parameters:
 *       print: Handle to the print object returned by dw_print_new().
 */
void API dw_print_cancel(HPRINT print)
{
    DWPrint *p = print;

    if(p)
        p->drawfunc = NULL;
}

/*
 * Returns a pointer to a static buffer which containes the
 * current user directory.  Or the root directory (C:\ on
 * OS/2 and Windows).
 */
char * API dw_user_dir(void)
{
    static char _user_dir[1024] = {0};

    if(!_user_dir[0])
    {
        HANDLE hToken = 0;

        /* Use the Windows API to get the user's profile directory */
        if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
        {
            DWORD BufSize = 1024;
            TCHAR TmpBuf[1024], *Buf = TmpBuf;

            GetUserProfileDirectory(hToken, Buf, &BufSize);
            CloseHandle(hToken);
            strncpy(_user_dir, WideToUTF8(Buf), 1023);
        }
        /* If it fails set it to the root directory */
        if(!_user_dir[0])
        {
            strcpy(_user_dir, "C:\\");
        }
    }
    return _user_dir;
}

/*
 * Returns a pointer to a static buffer which containes the
 * private application data directory.
 */
char * API dw_app_dir(void)
{
    return _dw_exec_dir;
}

/*
 * Sets the application ID used by this Dynamic Windows application instance.
 * Parameters:
 *         appid: A string typically in the form: com.company.division.application
 *         appname: The application name used on Windows or NULL.
 * Returns:
 *         DW_ERROR_NONE after successfully setting the application ID.
 *         DW_ERROR_UNKNOWN if unsupported on this system.
 *         DW_ERROR_GENERAL if the application ID is not allowed.
 * Remarks:
 *          This must be called before dw_init().  If dw_init() is called first
 *          it will create a unique ID in the form: org.dbsoft.dwindows.application
 *          or if the application name cannot be detected: org.dbsoft.dwindows.pid.#
 *          The appname is only required on Windows.  If NULL is passed the detected
 *          application name will be used, but a prettier name may be desired.
 */
int API dw_app_id_set(const char *appid, const char *appname)
{
    strncpy(_dw_app_id, appid, _DW_APP_ID_SIZE);
    if(appname)
        strncpy(_dw_app_name, appname, _DW_APP_ID_SIZE);
    return DW_ERROR_NONE;
}

/*
 * Call a function from the window (widget)'s context.
 * Parameters:
 *       handle: Window handle of the widget.
 *       function: Function pointer to be called.
 *       data: Pointer to the data to be passed to the function.
 */
void API dw_window_function(HWND handle, void *function, void *data)
{
   SendMessage(_dw_toplevel_window(handle), WM_USER, (WPARAM)function, (LPARAM)data);
}

/* Functions for managing the user data lists that are associated with
 * a given window handle.  Used in dw_window_set_data() and
 * dw_window_get_data().
 */
UserData *_dw_find_userdata(UserData **root, const char *varname)
{
   UserData *tmp = *root;

   while(tmp)
   {
      if(_stricmp(tmp->varname, varname) == 0)
         return tmp;
      tmp = tmp->next;
   }
   return NULL;
}

int _dw_new_userdata(UserData **root, const char *varname, void *data)
{
   UserData *new = _dw_find_userdata(root, varname);

   if(new)
   {
      new->data = data;
      return TRUE;
   }
   else
   {
      new = malloc(sizeof(UserData));
      if(new)
      {
         new->varname = _strdup(varname);
         new->data = data;

         new->next = NULL;

         if (!*root)
            *root = new;
         else
         {
            UserData *prev = *root, *tmp = prev->next;

            while(tmp)
            {
               prev = tmp;
               tmp = tmp->next;
            }
            prev->next = new;
         }
         return TRUE;
      }
   }
   return FALSE;
}

int _dw_remove_userdata(UserData **root, const char *varname, int all)
{
   UserData *prev = NULL, *tmp = *root;

   while(tmp)
   {
      if(all || _stricmp(tmp->varname, varname) == 0)
      {
         if(!prev)
         {
            *root = tmp->next;
            free(tmp->varname);
            free(tmp);
            if(!all)
               return 0;
            tmp = *root;
         }
         else
         {
            /* If all is true we should
             * never get here.
             */
            prev->next = tmp->next;
            free(tmp->varname);
            free(tmp);
            return 0;
         }
      }
      else
      {
         prev = tmp;
         tmp = tmp->next;
      }
   }
   return 0;
}

/*
 * Add a named user data item to a window handle.
 * Parameters:
 *       window: Window handle of signal to be called back.
 *       dataname: A string pointer identifying which signal to be hooked.
 *       data: User data to be passed to the handler function.
 */
void API dw_window_set_data(HWND window, const char *dataname, void *data)
{
   ColorInfo *cinfo = _dw_window_get_cinfo(window);

   if(!cinfo)
   {
      if(!dataname)
         return;

      cinfo = _dw_window_new_cinfo(window, FALSE);
   }

   if(cinfo)
   {
      if(data)
         _dw_new_userdata(&(cinfo->root), dataname, data);
      else
      {
         if(dataname)
            _dw_remove_userdata(&(cinfo->root), dataname, FALSE);
         else
            _dw_remove_userdata(&(cinfo->root), NULL, TRUE);
      }
   }
}

/*
 * Gets a named user data item to a window handle.
 * Parameters:
 *       window: Window handle of signal to be called back.
 *       dataname: A string pointer identifying which signal to be hooked.
 *       data: User data to be passed to the handler function.
 */
void * API dw_window_get_data(HWND window, const char *dataname)
{
   ColorInfo *cinfo = _dw_window_get_cinfo(window);

   if(cinfo && cinfo->root && dataname)
   {
      UserData *ud = _dw_find_userdata(&(cinfo->root), dataname);
      if(ud)
         return ud->data;
   }
   return NULL;
}

/*
 * Compare two window handles.
 * Parameters:
 *       window1: First window handle to compare.
 *       window2: Second window handle to compare.
 * Returns:
 *       TRUE if the windows are the same object, FALSE if not.
 */
int API dw_window_compare(HWND window1, HWND window2)
{
    /* If anything special is require to compare... do it
     * here otherwise just compare the handles.
     */
    if(window1 && window2 && window1 == window2)
        return TRUE;
    return FALSE;
}

/*
 * Add a callback to a timer event.
 * Parameters:
 *       interval: Milliseconds to delay between calls.
 *       sigfunc: The pointer to the function to be used as the callback.
 *       data: User data to be passed to the handler function.
 * Returns:
 *       Timer ID for use with dw_timer_disconnect(), 0 on error.
 */
HTIMER API dw_timer_connect(int interval, void *sigfunc, void *data)
{
   if(sigfunc)
   {
      /* Warning: This seems to return UINT_PTR on some systems...
       * which may exceed the storage of int that our API uses.
       */
      int timerid = (int)SetTimer(NULL, 0, interval, _dw_timerproc);

      if(timerid)
      {
         _dw_new_signal(WM_TIMER, NULL, timerid, sigfunc, NULL, data);
         return timerid;
      }
   }
   return 0;
}

/*
 * Removes timer callback.
 * Parameters:
 *       id: Timer ID returned by dw_timer_connect().
 */
void API dw_timer_disconnect(HTIMER id)
{
   DWSignalHandler *prev = NULL, *tmp = Root;

   /* 0 is an invalid timer ID */
   if(!id)
      return;

   KillTimer(NULL, id);

   while(tmp)
   {
      if(tmp->id == id)
      {
         if(prev)
         {
            prev->next = tmp->next;
            free(tmp);
            tmp = prev->next;
         }
         else
         {
            Root = tmp->next;
            free(tmp);
            tmp = Root;
         }
      }
      else
      {
         prev = tmp;
         tmp = tmp->next;
      }
   }
}

/*
 * Add a callback to a window event.
 * Parameters:
 *       window: Window handle of signal to be called back.
 *       signame: A string pointer identifying which signal to be hooked.
 *       sigfunc: The pointer to the function to be used as the callback.
 *       data: User data to be passed to the handler function.
 */
void API dw_signal_connect(HWND window, const char *signame, void *sigfunc, void *data)
{
    dw_signal_connect_data(window, signame, sigfunc, NULL, data);
}

/*
 * Add a callback to a window event with a closure callback.
 * Parameters:
 *       window: Window handle of signal to be called back.
 *       signame: A string pointer identifying which signal to be hooked.
 *       sigfunc: The pointer to the function to be used as the callback.
 *       discfunc: The pointer to the function called when this handler is removed.
 *       data: User data to be passed to the handler function.
 */
void API dw_signal_connect_data(HWND window, const char *signame, void *sigfunc, void *discfunc, void *data)
{
   ULONG message = 0, id = 0;

   if (window && signame && sigfunc)
   {
      if (_stricmp(signame, DW_SIGNAL_SET_FOCUS) == 0)
         window = _dw_normalize_handle(window);

      if ((message = _dw_findsigmessage(signame)) != 0)
      {
         /* Handle special case of the menu item */
         if (message == WM_COMMAND && window < (HWND)65536)
         {
            char buffer[16];
            HWND owner;

            _snprintf(buffer, 15, "_dw_id%d", (int)(intptr_t)window);
            owner = (HWND)dw_window_get_data(DW_HWND_OBJECT, buffer);

            /* Make sure there are no dupes from popups */
            dw_signal_disconnect_by_window(window);

            if (owner)
            {
               id = (ULONG)(uintptr_t)window;
               window = owner;
            }
         }
         _dw_new_signal(message, window, id, sigfunc, discfunc, data);
      }
   }
}

/*
 * Removes callbacks for a given window with given name.
 * Parameters:
 *       window: Window handle of callback to be removed.
 */
void API dw_signal_disconnect_by_name(HWND window, const char *signame)
{
   DWSignalHandler *prev = NULL, *tmp = Root;
   ULONG message;

   if(!window || !signame || (message = _dw_findsigmessage(signame)) == 0)
      return;

   while(tmp)
   {
      if(((window < (HWND)65536 && (int)(intptr_t)window == tmp->id) || tmp->window == window) && tmp->message == message)
      {
         void (DWSIGNAL *discfunc)(HWND, void *) = (void (*)(HWND, void *))tmp->discfunction;

         if(discfunc)
         {
             discfunc(tmp->window, tmp->data);
         }

         if(prev)
         {
            prev->next = tmp->next;
            free(tmp);
            tmp = prev->next;
         }
         else
         {
            Root = tmp->next;
            free(tmp);
            tmp = Root;
         }
      }
      else
      {
         prev = tmp;
         tmp = tmp->next;
      }
   }
}

/*
 * Removes all callbacks for a given window.
 * Parameters:
 *       window: Window handle of callback to be removed.
 */
void API dw_signal_disconnect_by_window(HWND window)
{
   DWSignalHandler *prev = NULL, *tmp = Root;

   while(tmp)
   {
      if((window < (HWND)65536 && (int)(intptr_t)window == tmp->id) || tmp->window == window)
      {
         void (DWSIGNAL *discfunc)(HWND, void *) = (void (*)(HWND, void *))tmp->discfunction;

         if(discfunc)
         {
             discfunc(tmp->window, tmp->data);
         }

         if(prev)
         {
            prev->next = tmp->next;
            free(tmp);
            tmp = prev->next;
         }
         else
         {
            Root = tmp->next;
            free(tmp);
            tmp = Root;
         }
      }
      else
      {
         prev = tmp;
         tmp = tmp->next;
      }
   }
}

/*
 * Removes all callbacks for a given window with specified data.
 * Parameters:
 *       window: Window handle of callback to be removed.
 *       data: Pointer to the data to be compared against.
 */
void API dw_signal_disconnect_by_data(HWND window, void *data)
{
   DWSignalHandler *prev = NULL, *tmp = Root;

   while(tmp)
   {
      if(((window < (HWND)65536 && (int)(intptr_t)window == tmp->id) || tmp->window == window) && tmp->data == data)
      {
         void (DWSIGNAL *discfunc)(HWND, void *) = (void (*)(HWND, void *))tmp->discfunction;

         if(discfunc)
         {
             discfunc(tmp->window, tmp->data);
         }

        if(prev)
         {
            prev->next = tmp->next;
            free(tmp);
            tmp = prev->next;
         }
         else
         {
            Root = tmp->next;
            free(tmp);
            tmp = Root;
         }
      }
      else
      {
         prev = tmp;
         tmp = tmp->next;
      }
   }
}

/*
 * Converts a UTF-8 encoded string into a wide string.
 * Parameters:
 *       utf8string: UTF-8 encoded source string.
 * Returns:
 *       Wide string that needs to be freed with dw_free()
 *       or NULL on failure.
 */
wchar_t * API dw_utf8_to_wchar(const char *utf8string)
{
 #ifdef UNICODE
    return _dw_UTF8toWide(utf8string, malloc(MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, NULL, 0) * sizeof(WCHAR)));
#else
    return NULL;
#endif
}

/*
 * Converts a wide string into a UTF-8 encoded string.
 * Parameters:
 *       wstring: Wide source string.
 * Returns:
 *       UTF-8 encoded string that needs to be freed with dw_free()
 *       or NULL on failure.
 */
char * API dw_wchar_to_utf8(const wchar_t *wstring)
{
#ifdef UNICODE
    return _dw_WideToUTF8(wstring, malloc(WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL)));
#else
    return NULL;
#endif
}

/*
 * Gets the state of the requested library feature.
 * Parameters:
 *       feature: The requested feature for example DW_FEATURE_DARK_MODE
 * Returns:
 *       DW_FEATURE_UNSUPPORTED if the library or OS does not support the feature.
 *       DW_FEATURE_DISABLED if the feature is supported but disabled.
 *       DW_FEATURE_ENABLED if the feature is supported and enabled.
 *       Other value greater than 1, same as enabled but with extra info.
 */
int API dw_feature_get(DWFEATURE feature)
{
    switch(feature)
    {
#ifdef UNICODE
        case DW_FEATURE_UTF8_UNICODE:
#endif
#if (defined(BUILD_DLL) || defined(BUILD_HTML))
        case DW_FEATURE_HTML:
        case DW_FEATURE_HTML_RESULT:
#endif
        case DW_FEATURE_CONTAINER_STRIPE:
        case DW_FEATURE_MDI:
        case DW_FEATURE_TASK_BAR:  
        case DW_FEATURE_TREE:
        case DW_FEATURE_WINDOW_PLACEMENT:
            return DW_FEATURE_ENABLED;
#ifdef BUILD_TOAST
        case DW_FEATURE_NOTIFICATION:
        {
            if(_dw_toast_is_compatible())
                return DW_FEATURE_ENABLED;
            return DW_FEATURE_UNSUPPORTED;
        }
#endif
#ifdef AEROGLASS
        case DW_FEATURE_WINDOW_TRANSPARENCY:
        {
            if(_dw_composition)
                return DW_FEATURE_ENABLED;
            return DW_FEATURE_DISABLED;
        }
        case DW_FEATURE_DARK_MODE:
        {
            if(_DW_DARK_MODE_SUPPORTED)
            {
                /* Special case for Full dark mode setting... return DW_DARK_MODE_BASIC
                 * with DW_DARK_MODE_FULL requested but the system is in light mode.
                 */
                if(_DW_DARK_MODE_ALLOWED == DW_DARK_MODE_FULL && !_DW_DARK_MODE_ENABLED)
                    return DW_DARK_MODE_BASIC;
                return _DW_DARK_MODE_ALLOWED;
            }
            return DW_FEATURE_UNSUPPORTED;
        }
#endif
#ifdef RICHEDIT
        /* Word wrap support on Windows is tied to Rich Edit,
         * if Rich Edit MLE is unsupported or disabled...
         * Word Wrap will return Unsupported.
         */
        case DW_FEATURE_MLE_WORD_WRAP:
        {
            if(_DW_MLE_RICH_EDIT == DW_FEATURE_ENABLED && (hrichedit || hmsftedit))
                return DW_FEATURE_ENABLED;
            return DW_FEATURE_UNSUPPORTED;
        }
        case DW_FEATURE_MLE_RICH_EDIT:
        {
            if(hmsftedit || hrichedit)
                return _DW_MLE_RICH_EDIT;
            return DW_FEATURE_UNSUPPORTED;
        }
#endif
        default:
            return DW_FEATURE_UNSUPPORTED;
    }
}

/*
 * Sets the state of the requested library feature.
 * Parameters:
 *       feature: The requested feature for example DW_FEATURE_DARK_MODE
 *       state: DW_FEATURE_DISABLED, DW_FEATURE_ENABLED or any value greater than 1.
 * Returns:
 *       DW_FEATURE_UNSUPPORTED if the library or OS does not support the feature.
 *       DW_ERROR_NONE if the feature is supported and successfully configured.
 *       DW_ERROR_GENERAL if the feature is supported but could not be configured.
 * Remarks: 
 *       These settings are typically used during dw_init() so issue before 
 *       setting up the library with dw_init().
 */
int API dw_feature_set(DWFEATURE feature, int state)
{
    switch(feature)
    {
        /* These features are supported but not configurable */
#ifdef UNICODE
        case DW_FEATURE_UTF8_UNICODE:
#endif
#if (defined(BUILD_DLL) || defined(BUILD_HTML))
        case DW_FEATURE_HTML:
        case DW_FEATURE_HTML_RESULT:
#endif
#ifdef AEROGLASS
        case DW_FEATURE_WINDOW_TRANSPARENCY:
#endif
        case DW_FEATURE_CONTAINER_STRIPE:
        case DW_FEATURE_MDI:
        case DW_FEATURE_TASK_BAR:  
        case DW_FEATURE_TREE:
        case DW_FEATURE_WINDOW_PLACEMENT:
            return DW_ERROR_GENERAL;
#ifdef BUILD_TOAST
        case DW_FEATURE_NOTIFICATION:
        {
            if(_dw_toast_is_compatible())
                return DW_ERROR_GENERAL;
            return DW_FEATURE_UNSUPPORTED;
        }
#endif
        /* These features are supported and configurable */
#ifdef AEROGLASS
        case DW_FEATURE_DARK_MODE:
        {
            if(state >= DW_DARK_MODE_DISABLED && state <= DW_DARK_MODE_FORCED)
            {
                _DW_DARK_MODE_ALLOWED = state;
                return DW_ERROR_NONE;
            }
            return DW_ERROR_GENERAL;
        }
#endif
#ifdef RICHEDIT
        /* Word wrap support on Windows is tied to Rich Edit,
         * if Rich Edit MLE is unsupported or disabled...
         * Word Wrap will return Unsupported. If supported, 
         * it is not configurable, configure Rich Edit instead.
         */
        case DW_FEATURE_MLE_WORD_WRAP:
        {
            if(_DW_MLE_RICH_EDIT == DW_FEATURE_ENABLED && (hrichedit || hmsftedit))
                return DW_ERROR_GENERAL;
            return DW_FEATURE_UNSUPPORTED;
        }
        case DW_FEATURE_MLE_RICH_EDIT:
        {
            if(state >= DW_FEATURE_DISABLED && state <= DW_FEATURE_ENABLED)
            {
                _DW_MLE_RICH_EDIT = state;
                return DW_ERROR_NONE;
            }
            return DW_ERROR_GENERAL;
        }
#endif
        default:
            return DW_FEATURE_UNSUPPORTED;
    }
}