/*
 * win32dynamicdialog.cpp - Dynamic dialog creation
 *
 * Originally part of Pythonwin as win32dlgdyn.cpp, then moved into
 * win32\src\win32dynamicdialog.cpp, and shared between Pythonwin
 * and the lighter-weight win32gui module.
 *
 * Copyright (C) 1995 by Motek Information Systems, Beverly Hills, CA, USA
 *
 *                       All Rights Reserved
 *
 * Permission to use, copy, modify, and distribute this software and its 
 * documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice and the two paragraphs following
 * it appear in all copies, and that the name of Motek Information Systems
 * not be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * 
 * MOTEK INFORMATION SYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL MOTEK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: Curt Hagenlocher <curt@hagenlocher.org>
 */

// @doc - Autoduck!

#define WIN32GUI
#ifdef WIN32GUI // being compiled from WIN32GUI
#define PYW_EXPORT
#include "python.h"
#undef PyHANDLE
#include <windows.h>
#include "commctrl.h"
#include "windowsx.h" // For edit control hacks.

#ifdef MS_WINCE
#include "winbase.h"
#endif
#include "win32types.h"
#include "pywinobjects.h"
#include "tchar.h"

#define BASED_CODE
#ifndef ASSERT
#define ASSERT(a)
#endif

#else // else being compiled from WIN32UI
#include "stdafx.h"
#endif

#include "win32dynamicdialog.h"
static void DwordAlign(PCHAR *ptr)
{
	size_t offset = ((ULONG_PTR)*ptr) & 0x03;
	if (offset > 0)
	{
		*ptr += (4 - offset);
	}
}

/*
 * CPythonDialogTemplate
 */
CPythonDialogTemplate::CPythonDialogTemplate(LPCWSTR caption, DLGTEMPLATE *tmpl, WORD fontsize,
											 LPCWSTR font, LPCWSTR menu, LPCWSTR wclass)
{
	LPCWSTR thefont = font ? font : L"MS Sans Serif";

	m_alloc = sizeof(DLGTEMPLATE) +
			  (wcslen(caption) * sizeof(caption[0])) +
			  (wcslen(thefont) * sizeof(thefont[0])) +
			  20;
	if (!IS_INTRESOURCE(menu))
		m_alloc += wcslen(menu) * sizeof(menu[0]);
	if (!IS_INTRESOURCE(wclass))
		m_alloc += wcslen(wclass) * sizeof(wclass[0]);

	m_h = GlobalAlloc(GHND, m_alloc);
	DLGTEMPLATE *hdr = (DLGTEMPLATE *)GlobalLock(m_h);
	memcpy(hdr, tmpl, sizeof(DLGTEMPLATE));
	hdr->cdit = 0;
	WCHAR *ptr = (WCHAR *)((BYTE *)hdr + sizeof(DLGTEMPLATE));

	if (!IS_INTRESOURCE(menu))
	{
		wcscpy(ptr, menu);
		ptr += wcslen(menu) + 1;
	}
	else if (menu)
	{
		*ptr++ = (WCHAR)-1;
		*ptr++ = LOWORD(menu);
	}
	else
		*ptr++ = 0;

	if (!IS_INTRESOURCE(wclass))
	{
		wcscpy(ptr, wclass);
		ptr += wcslen(wclass) + 1;
	}
	else if (wclass)
	{
		*ptr++ = (WCHAR)-1;
		*ptr++ = LOWORD(wclass);
	}
	else
		*ptr++ = 0;

	wcscpy(ptr, caption);
	ptr += wcslen(caption) + 1;
	if (hdr->style & DS_SETFONT)
	{
		*ptr++ = fontsize;
		wcscpy(ptr, thefont);
		ptr += wcslen(thefont) + 1;
	}
	DwordAlign((PCHAR *)&ptr);
	size_t len = (BYTE *)ptr - (BYTE *)hdr;
	ASSERT(len <= m_alloc);
	m_len = len;
	m_ptr = hdr;
}

BOOL CPythonDialogTemplate::Add(LPCWSTR wclass, DLGITEMTEMPLATE *tmpl, LPCWSTR txt,
								int datalen, BYTE *data)
{
	GlobalUnlock(m_h);
	size_t len = sizeof(DLGITEMTEMPLATE) + wcslen(wclass) * sizeof(wclass[0]) + datalen + 20;
	if (txt)
	{
		len += wcslen(txt) * sizeof(txt[0]);
	}
	if (m_len + len > m_alloc)
	{
		m_alloc += len;
		m_h = GlobalReAlloc(m_h, m_alloc, 0);
		ASSERT(m_h);
	}

	DLGTEMPLATE *hdr = (DLGTEMPLATE *)GlobalLock(m_h);
	hdr->cdit++;
	DLGITEMTEMPLATE *ctrl = (DLGITEMTEMPLATE *)((char *)hdr + m_len);
	memcpy(ctrl, tmpl, sizeof(DLGITEMTEMPLATE));
	WCHAR *ptr = (WCHAR *)((char *)ctrl + sizeof(DLGITEMTEMPLATE));
	wcscpy(ptr, wclass);
	ptr += wcslen(ptr) + 1;
	if (txt)
	{
		wcscpy(ptr, txt);
		ptr += wcslen(ptr) + 1;
	}
	else
	{
		*ptr++ = 0;
	}

	*ptr++ = (WORD)datalen;
	if (datalen)
	{
		ASSERT(data);
		memcpy(ptr, data, datalen);
		ptr = (WCHAR *)(((BYTE *)ptr) + datalen);
	}
	DwordAlign((PCHAR *)&ptr);
	len = (BYTE *)ptr - (BYTE *)hdr;
	ASSERT(len <= m_alloc);
	m_len = len;
	m_ptr = hdr;
	return TRUE;
}

BOOL CPythonDialogTemplate::Add(WORD wclass, DLGITEMTEMPLATE *tmpl, LPCWSTR txt)
{
	GlobalUnlock(m_h);
	size_t len = sizeof(DLGITEMTEMPLATE) + 20;
	if (txt)
	{
		len += wcslen(txt) * sizeof(txt[0]);
	}
	if (m_len + len > m_alloc)
	{
		m_alloc += len;
		m_h = GlobalReAlloc(m_h, m_alloc, 0);
		ASSERT(m_h);
	}

	DLGTEMPLATE *hdr = (DLGTEMPLATE *)GlobalLock(m_h);
	hdr->cdit++;
	DLGITEMTEMPLATE *ctrl = (DLGITEMTEMPLATE *)((char *)hdr + m_len);
	memcpy(ctrl, tmpl, sizeof(DLGITEMTEMPLATE));
	WCHAR *ptr = (WCHAR *)((char *)ctrl + sizeof(DLGITEMTEMPLATE));
	*ptr++ = (WORD)-1;
	*ptr++ = wclass;

	if (txt)
	{
		wcscpy(ptr, txt);
		ptr += wcslen(txt) + 1;
	}
	else
	{
		*ptr++ = 0;
	}
	*ptr++ = 0;
	DwordAlign((PCHAR *)&ptr);
	len = (BYTE *)ptr - (BYTE *)hdr;
	ASSERT(len <= m_alloc);
	m_len = len;
	m_ptr = hdr;
	return TRUE;
}

void CPythonDialogTemplate::Get(DLGTEMPLATE *tmpl)
{
	DLGTEMPLATE *hdr = (DLGTEMPLATE *)GlobalLock(m_h);
	memcpy(tmpl, hdr, sizeof(DLGTEMPLATE));
	GlobalUnlock(m_h);
}

void CPythonDialogTemplate::Set(DLGTEMPLATE *tmpl)
{
	DLGTEMPLATE *hdr = (DLGTEMPLATE *)GlobalLock(m_h);
	memcpy(hdr, tmpl, sizeof(DLGTEMPLATE));
	GlobalUnlock(m_h);
}

HGLOBAL CPythonDialogTemplate::ClaimTemplate()
{
	register HGLOBAL h = m_h;
	m_h = NULL;
	m_alloc = 0;
	m_len = 0;
	m_ptr = 0;
	GlobalUnlock(h);
	return h;
}

CPythonDialogTemplate::~CPythonDialogTemplate()
{
	if (m_h)
	{
		GlobalUnlock(m_h);
		GlobalFree(m_h);
	}
}

///////////////////////////////////////////////////////////////////////////
// Python-specific dialog code

// Extracts a string or resource id/atom from a dialog template buffer
// and updates the pointer param to reflect how many bytes were consumed.
static PyObject *MakeResName(WCHAR **val)
{
	WCHAR *ptr = *val;
	PyObject *obj = NULL;
	if (*ptr == (WORD)-1)
	{
		ptr++;
		obj = PyInt_FromLong((WORD)*ptr++);
	}
	else if (*ptr != (WORD)0)
	{
		obj = PyWinObject_FromWCHAR(ptr);
		ptr += wcslen(ptr) + 1;
	}
	else
	{
		// This used to return None instead of 0 for reasons unknown
		obj = PyLong_FromLong(0);
		ptr++;
	}
	*val = ptr;
	return obj;
}

// Given a pointer to a dialog hdr template, return a Python list to match it
/* Also need to handle extended templates (DLGTEMPLATEEX), quoting from MSDN:
	To distinguish between a standard template and an extended template,
	check the first 16-bits of a dialog box template.
	In an extended template, the first WORD is 0xFFFF;
	any other value indicates a standard template. 
*/
static PyObject *MakeListFromDlgHdr(LPVOID *tplin, int &items)
{
	PyObject *obitem;
	PyObject *ret = PyList_New(7);
	if (ret == NULL)
		return NULL;
	DLGTEMPLATE *tpl = (LPDLGTEMPLATE)*tplin;
	WCHAR *ptr = (WCHAR *)((char *)tpl + sizeof(DLGTEMPLATE));

	// Parse menu and window class out first, since they have to be consumed to get to the
	//	caption, which is used as first item
	// Parameter 5 - Menu
	obitem = MakeResName(&ptr);
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 5, obitem);

	// Parameter 6 - Window Class
	obitem = MakeResName(&ptr);
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 6, obitem);

	// Parameter 0 - Caption
	obitem = PyWinObject_FromWCHAR(ptr);
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 0, obitem);
	ptr += wcslen(ptr) + 1;

	// Parameter 1 - Bounds
	obitem = Py_BuildValue("(hhhh)", tpl->x, tpl->y, tpl->cx, tpl->cy);
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 1, obitem);

	// Parameter 2 - Style
	obitem = PyLong_FromUnsignedLong(tpl->style);
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 2, obitem);

	// Parameter 3 - Extended Style
	obitem = PyLong_FromUnsignedLong(tpl->dwExtendedStyle);
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 3, obitem);

	// Parameter 4 - Font tuple
	if (tpl->style & DS_SETFONT)
	{
		WORD fontsize = (WORD)*ptr++;
		obitem = Py_BuildValue("HN", fontsize, PyWinObject_FromWCHAR(ptr));
		ptr += wcslen(ptr) + 1;
	}
	else
	{
		obitem = Py_None;
		Py_INCREF(Py_None);
	}
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 4, obitem);

	DwordAlign((PCHAR *)&ptr);
	*tplin = ptr;
	items = tpl->cdit;
	return ret;
}

// Given a pointer to a dialog item template, return a Python list to match it
static PyObject *MakeListFromDlgItem(LPVOID *tplin)
{
	PyObject *obitem;
	PyObject *ret = PyList_New(7);
	if (ret == NULL)
		return NULL;
	DLGITEMTEMPLATE *tpl = (LPDLGITEMTEMPLATE)*tplin;

	// Parameter 0 - Window class
	WCHAR *ptr = (WCHAR *)((char *)tpl + sizeof(DLGITEMTEMPLATE));
	if (*ptr == (WCHAR)-1)
	{
		ptr++;
		obitem = PyInt_FromLong((WORD)*ptr++);
	}
	else
	{
		LPWSTR wc = LPWSTR(ptr);
		obitem = PyWinObject_FromWCHAR(wc);
		ptr += wcslen(wc) + 1;
	}
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 0, obitem);

	// Parameter 1 - Text
	LPWSTR txt = LPWSTR(ptr);
	obitem = PyWinObject_FromWCHAR(txt);
	ptr += wcslen(txt) + 1;
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 1, obitem);

	// Parameter 2 - ID
	obitem = PyInt_FromLong(tpl->id);
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 2, obitem);

	// Parameter 3 - Bounds
	obitem = Py_BuildValue("(hhhh)", tpl->x, tpl->y, tpl->cx, tpl->cy);
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 3, obitem);

	// Parameter 4 - Style
	obitem = PyLong_FromUnsignedLong(tpl->style);
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 4, obitem);

	// Parameter 5 - Extended Style
	obitem = PyLong_FromUnsignedLong(tpl->dwExtendedStyle);
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 5, obitem);

	// Parameter 6 - Extra data
	WORD datalen = *ptr++;
	if (datalen > 0)
	{
		obitem = PyString_FromStringAndSize((char *)ptr, datalen);
		ptr = (WCHAR *)(((char *)ptr) + datalen);
	}
	else
	{
		obitem = Py_None;
		Py_INCREF(Py_None);
	}
	if (obitem == NULL)
	{
		Py_DECREF(ret);
		return NULL;
	}
	PyList_SET_ITEM(ret, 6, obitem);

	DwordAlign((PCHAR *)&ptr);
	*tplin = ptr;

	return ret;
}

// Given a dialog resource, return a Python object to match it
PYW_EXPORT PyObject *MakeDlgListFromResource(HGLOBAL res)
{
	void *t = (void *)res;
	// Extended dialog templates have a different format which is not handled yet.
	//	They can be identified by 0xFFFF in the second WORD of data.
	DWORD tmp = *(DWORD *)t;
	if (HIWORD(tmp) == 0xFFFF)
	{
		PyErr_SetString(PyExc_NotImplementedError, "Extended dialog templates are not yet supported");
		return NULL;
	}

	PyObject *list = PyList_New(0);
	if (list == NULL)
		return NULL;
	int n = 0;
	PyObject *obitem = MakeListFromDlgHdr(&t, n);
	if (obitem == NULL || PyList_Append(list, obitem) == -1)
	{
		Py_DECREF(list);
		Py_XDECREF(obitem);
		return NULL;
	}
	Py_DECREF(obitem);
	for (int i = 0; i < n; i++)
	{
		obitem = MakeListFromDlgItem(&t);
		if (obitem == NULL || PyList_Append(list, obitem) == -1)
		{
			Py_DECREF(list);
			Py_XDECREF(obitem);
			return NULL;
		}
		Py_DECREF(obitem);
	}
	return list;
}

// Given a Python dialog header list, parse out a matching CPythonDialogTemplate
// @object PyDLGTEMPLATE|A tuple of items describing a dialog box, that can be used to create the dialog.
// @pyseeapi DLGTEMPLATE
static CPythonDialogTemplate *ParseDlgHdrList(PyObject *tmpl)
{
	PyObject *obhdr = PySequence_Tuple(tmpl);
	if (obhdr == NULL)
		return NULL;
	WCHAR *caption = NULL, *menu = NULL, *wclass = NULL, *fontname = NULL;
	PyObject *obcaption, *obfontname, *obfont = Py_None, *obmenu = Py_None, *obwclass = Py_None;
	PyObject *obexstyle = Py_None;
	DLGTEMPLATE tpl = {0, 0, 0, 0, 0, 0, 0};
	WORD fontsize;

	CPythonDialogTemplate *ret = NULL;

	if (!PyArg_ParseTuple(obhdr, "O(hhhh)k|OOOO:DLGTEMPLATE",
						  &obcaption,
						  &tpl.x, &tpl.y, &tpl.cx, &tpl.cy,
						  &tpl.style,
						  &obexstyle,
						  &obfont,
						  &obmenu,
						  &obwclass))
		goto cleanup;

	// @tupleitem 0|string|caption|The caption for the window
	// @tupleitem 1|(int,int,int,int)|(x,y,cx,cy)|The bounding rectange for the dialog.
	// @tupleitem 2|int|style|The style bits for the dialog.  Combination of WS_* and DS_* constants.
	// Note that the DS_SETFONT style need never be specified - it is determined by the font item (below)
	// <nl>See MSDN documentation on Dialog Boxes for allowable values.
	// @tupleitem 3|int|extStyle|The extended style bits for the dialog. Defaults to 0 if not passed and None is supported for backwards compatibility.
	// @tupleitem 4|(int, string)|(fontSize, fontName)|A tuple describing the font, or None if the system default font is to be used.
	// @tupleitem 5|<o PyResourceId>|menuResource|The resource ID of the menu to be used for the dialog, or None for no menu.
	// @tupleitem 6|<o PyResourceId>|windowClass|Window class name or atom as returned from RegisterWindowClass.  Defaults to None.

	if (!PyWinObject_AsWCHAR(obcaption, &caption, FALSE))
		goto cleanup;
	if (!PyWinObject_AsResourceIdW(obmenu, &menu, TRUE))
		goto cleanup;
	if (!PyWinObject_AsResourceIdW(obwclass, &wclass, TRUE))
		goto cleanup;
	if (obexstyle != Py_None)
	{
		tpl.dwExtendedStyle = PyLong_AsUnsignedLong(obexstyle);
		if (tpl.dwExtendedStyle == -1 && PyErr_Occurred())
			goto cleanup;
	}

	tpl.style &= ~DS_SETFONT;
	if (obfont != Py_None)
	{
		if (!PyArg_ParseTuple(obfont, "HO", &fontsize, &obfontname))
			goto cleanup;
		if (!PyWinObject_AsWCHAR(obfontname, &fontname, TRUE))
			goto cleanup;
		tpl.style |= DS_SETFONT;
	}
	ret = new CPythonDialogTemplate(caption, &tpl, fontsize, fontname, menu, wclass);

cleanup:
	Py_DECREF(obhdr);
	PyWinObject_FreeWCHAR(caption);
	PyWinObject_FreeWCHAR(fontname);
	PyWinObject_FreeResourceId(menu);
	PyWinObject_FreeResourceId(wclass);
	return ret;
}

// Given a Python dialog item list, parse out a dialog item
// @object PyDLGITEMTEMPLATE|A tuple describing a control in a dialog box.
// @pyseeapi DLGITEMTEMPLATE
static BOOL ParseDlgItemList(CPythonDialogTemplate *dlg, PyObject *tmpl)
{
	// @tupleitem 0|string/int|windowClass|The window class.  If not a string, it must be in integer defining one of the built-in Windows controls.
	// If a string, it must be a pre-registered windows class name, a built-in class, or the CLSID of an OLE controls.
	// Built-in classes include:
	// @flagh Control Type|String Class Name
	// @flag Check Box|Button
	// @flag Combo Box|ComboBox
	// @flag Command Button|Button
	// @flag Header|SysHeader32
	// @flag Label|Static
	// @flag List Box|ListBox<nl>SysListView32
	// @flag Option Button|Button
	// @flag Tab|SysTabControl32
	// @flag Text Box|Edit<nl>RICHEDIT
	// @flag Tool Bar|ToolbarWindow32
	// @flag Tool Tips|tooltips_class32<nl>tooltips_class
	// @flag Tree View|SysTreeView32
	// The built-in windows controls are:
	// @flagh Integer Value|Window Type
	// @flag 0x0080|Button
	// @flag 0x0081|Edit
	// @flag 0x0082|Static
	// @flag 0x0083|List box
	// @flag 0x0084|Scroll bar
	// @flag 0x0085|Combo box

	// @tupleitem 1|<o PyUnicode>|caption|Caption for the control, or None
	// @tupleitem 2|int|ID|The child ID of this control.  All children should have unique
	// IDs.  This ID can be used by <om PyCDialog.GetDlgItem> to retrieve the actual control
	// object at runtime.
	// @tupleitem 3|(int,int,int,int)|(x,y,cx,cy)|The bounding rectange for the control, relative to the upper left of the dialog, in dialog units.
	// @tupleitem 4|int|style|The window style of the control (WS_* constants). Depending on the type of control,
	// other constants may also be valid (eg, BS_* for Button, ES_* for Edit controls, etc).
	// @tupleitem 5|int|extStyle|The extended style of the control.
	// @tupleitem 6|buffer|extraData|A byte string or buffer used as extra data for the control.  The value depends on the control.

	BOOL ret = FALSE;
	PyObject *obwclass, *obcaption, *obdata = Py_None;
	LPWSTR wclass = NULL, caption = NULL;
	DLGITEMTEMPLATE tpl = {0, 0, 0, 0, 0, 0, 0};
	BYTE *data = NULL;
	DWORD datalen = 0;

	PyObject *obitem = PySequence_Tuple(tmpl);
	if (obitem == NULL)
		return FALSE;
	if (!PyArg_ParseTuple(obitem, "OOH|(hhhh)kkO:DLGITEMTEMPLATE",
						  &obwclass,
						  &obcaption,
						  &tpl.id,
						  &tpl.x, &tpl.y, &tpl.cx, &tpl.cy,
						  &tpl.style,
						  &tpl.dwExtendedStyle,
						  &obdata))
		goto cleanup;
	if (!PyWinObject_AsResourceIdW(obwclass, &wclass, FALSE))
		goto cleanup;
	if (!PyWinObject_AsWCHAR(obcaption, &caption, TRUE))
		goto cleanup;
	if (!PyWinObject_AsReadBuffer(obdata, (void **)&data, &datalen, TRUE))
		goto cleanup;

	if (IS_INTRESOURCE(wclass))
		ret = dlg->Add((WORD)wclass, &tpl, caption);
	else
		ret = dlg->Add(wclass, &tpl, caption, datalen, data);

cleanup:
	PyWinObject_FreeResourceId(wclass);
	PyWinObject_FreeWCHAR(caption);
	Py_DECREF(obitem);
	return ret;
}

// Given a Python dialog template object, parse out a dialog resource
// @object PyDialogTemplate|Sequence of items defining a dialog.
//	The first item is a <o PyDLGTEMPLATE> describing the dialog, followed by
//	zero or more <o PyDLGITEMTEMPLATE>s describing controls within the dialog.
PYW_EXPORT HGLOBAL MakeResourceFromDlgList(PyObject *tmpl)
{
	PyObject *obdlg = PySequence_Tuple(tmpl);
	if (obdlg == NULL)
		return NULL;
	Py_ssize_t size = PyTuple_GET_SIZE(obdlg);
	if (size == 0)
	{
		PyErr_SetString(PyExc_ValueError, "Dialog template must contain at least a PyDLGTEMPLATE");
		return NULL;
	}

	PyObject *o = PyTuple_GET_ITEM(obdlg, 0);
	CPythonDialogTemplate *dlg = ParseDlgHdrList(o);
	if (dlg == NULL)
		return NULL;

	for (register Py_ssize_t i = 1; i < size; i++)
	{
		if (!ParseDlgItemList(dlg, PyTuple_GET_ITEM(obdlg, i)))
		{
			delete dlg;
			Py_DECREF(obdlg);
			return NULL;
		}
	}
	HGLOBAL h = dlg->ClaimTemplate();
	delete dlg;
	Py_DECREF(obdlg);
	return h;
}
