/* Copyright (C) 2002 Amir Szekely This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "DialogTemplate.h" ////////////////////////////////////////////////////////////////////// // Utilities ////////////////////////////////////////////////////////////////////// #define ALIGN(dwToAlign, dwAlignOn) dwToAlign = (dwToAlign%dwAlignOn == 0) ? dwToAlign : dwToAlign - (dwToAlign%dwAlignOn) + dwAlignOn // returns the number of WCHARs in str including null charcter inline DWORD WCStrLen(WCHAR* szwStr) { int i; for (i = 0; szwStr[i]; i++); return i+1; } // Reads a variany length array from seeker into readInto and advances seeker void ReadVarLenArr(BYTE* &seeker, char* &readInto) { WORD* arr = (WORD*)seeker; switch (arr[0]) { case 0x0000: readInto = 0; seeker += sizeof(WORD); break; case 0xFFFF: readInto = MAKEINTRESOURCE(arr[1]); seeker += 2*sizeof(WORD); break; default: { int iStrLen = WideCharToMultiByte(CP_ACP, 0, (WCHAR*)arr, -1, 0, 0, 0, 0); readInto = new char[iStrLen]; WideCharToMultiByte(CP_ACP, 0, (WCHAR*)arr, -1, readInto, iStrLen, 0, 0); seeker += WCStrLen((WCHAR*)arr)*sizeof(WCHAR); } break; } } // A macro that writes a given string (that can be a number too) into the buffer #define WriteStringOrId(x) \ if (x) \ if (IS_INTRESOURCE(x)) { \ *(WORD*)seeker = 0xFFFF; \ seeker += sizeof(WORD); \ *(WORD*)seeker = WORD(x); \ seeker += sizeof(WORD); \ } \ else { \ int us = MultiByteToWideChar(CP_ACP, 0, x, -1, (WCHAR*)seeker, dwSize); \ seeker += us*sizeof(WCHAR); \ } \ else \ seeker += sizeof(WORD); // A macro that adds the size of x (which can be a string a number, or nothing) to dwSize #define AddStringOrIdSize(x) dwSize += x ? (IS_INTRESOURCE(x) ? sizeof(DWORD) : (lstrlen(x)+1)*sizeof(WCHAR)) : sizeof(WORD) ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CDialogTemplate::CDialogTemplate(BYTE* pbData) { m_szClass = 0; m_szFont = 0; m_szMenu = 0; m_szTitle = 0; WORD wItems = 0; if (*(DWORD*)pbData == 0xFFFF0001) { // Extended dialog template signature m_bExtended = true; DLGTEMPLATEEX* dTemplateEx = (DLGTEMPLATEEX*)pbData; m_dwHelpId = dTemplateEx->helpID; m_dwStyle = dTemplateEx->style; m_dwExtStyle = dTemplateEx->exStyle; m_sX = dTemplateEx->x; m_sY = dTemplateEx->y; m_sWidth = dTemplateEx->cx; m_sHeight = dTemplateEx->cy; wItems = dTemplateEx->cDlgItems; } else { m_bExtended = false; DLGTEMPLATE* dTemplate = (DLGTEMPLATE*)pbData; m_dwStyle = dTemplate->style; m_dwExtStyle = dTemplate->dwExtendedStyle; m_sX = dTemplate->x; m_sY = dTemplate->y; m_sWidth = dTemplate->cx; m_sHeight = dTemplate->cy; wItems = dTemplate->cdit; } BYTE* seeker = pbData + (m_bExtended ? sizeof(DLGTEMPLATEEX) : sizeof(DLGTEMPLATE)); // Read menu variant length array ReadVarLenArr(seeker, m_szMenu); // Read class variant length array ReadVarLenArr(seeker, m_szClass); // Read title variant length array ReadVarLenArr(seeker, m_szTitle); // Read font size and variant length array (only if style DS_SETFONT is used!) if (m_dwStyle & DS_SETFONT) { m_sFontSize = *(short*)seeker; seeker += sizeof(short); ReadVarLenArr(seeker, m_szFont); } // Read items for (int i = 0; i < wItems; i++) { // DLGITEMTEMPLATE[EX]s must be aligned on DWORD boundry if (DWORD(seeker - pbData) % sizeof(DWORD)) seeker += sizeof(WORD); DialogItemTemplate* item = new DialogItemTemplate; ZeroMemory(item, sizeof(DialogItemTemplate)); if (m_bExtended) { DLGITEMTEMPLATEEX* rawItem = (DLGITEMTEMPLATEEX*)seeker; item->dwHelpId = rawItem->helpID; item->dwStyle = rawItem->style; item->dwExtStyle = rawItem->exStyle; item->sX = rawItem->x; item->sY = rawItem->y; item->sWidth = rawItem->cx; item->sHeight = rawItem->cy; item->wId = rawItem->id; seeker += sizeof(DLGITEMTEMPLATEEX); } else { DLGITEMTEMPLATE* rawItem = (DLGITEMTEMPLATE*)seeker; item->dwStyle = rawItem->style; item->dwExtStyle = rawItem->dwExtendedStyle; item->sX = rawItem->x; item->sY = rawItem->y; item->sWidth = rawItem->cx; item->sHeight = rawItem->cy; item->wId = rawItem->id; seeker += sizeof(DLGITEMTEMPLATE); } // Read class variant length array ReadVarLenArr(seeker, item->szClass); // Read title variant length array ReadVarLenArr(seeker, item->szTitle); // Read creation data variant length array // First read the size of the array (no null termination) item->wCreateDataSize = *(WORD*)seeker; seeker += sizeof(WORD); // Then read the array it self (if size is not 0) if (item->wCreateDataSize) { item->wCreateDataSize -= sizeof(WORD); // Size includes size field itself... item->szCreationData = new char[item->wCreateDataSize]; CopyMemory(item->szCreationData, seeker, item->wCreateDataSize); seeker += item->wCreateDataSize; } // Add the item to the vector m_vItems.push_back(item); } } CDialogTemplate::~CDialogTemplate() { if (m_szMenu && !IS_INTRESOURCE(m_szMenu)) delete [] m_szMenu; if (m_szClass && !IS_INTRESOURCE(m_szClass)) delete [] m_szClass; if (m_szTitle) delete [] m_szTitle; if (m_szFont) delete [] m_szTitle; for (int i = 0; i < m_vItems.size(); i++) { if (m_vItems[i]->szClass && !IS_INTRESOURCE(m_vItems[i]->szClass)) delete [] m_vItems[i]->szClass; if (m_vItems[i]->szTitle && !IS_INTRESOURCE(m_vItems[i]->szTitle)) delete [] m_vItems[i]->szTitle; if (m_vItems[i]->szCreationData) delete [] m_vItems[i]->szCreationData; } } ////////////////////////////////////////////////////////////////////// // Methods ////////////////////////////////////////////////////////////////////// // Returns info about the item with the id wId DialogItemTemplate* CDialogTemplate::GetItem(WORD wId) { for (int i = 0; i < m_vItems.size(); i++) if (m_vItems[i]->wId == wId) return m_vItems[i]; return 0; } // Returns info about the item with the indexed i DialogItemTemplate* CDialogTemplate::GetItemByIdx(DWORD i) { if (i >= m_vItems.size()) return 0; return m_vItems[i]; } // Removes an item void CDialogTemplate::RemoveItem(WORD wId) { for (int i = 0; i < m_vItems.size(); i++) if (m_vItems[i]->wId == wId) m_vItems.erase(m_vItems.begin() + i); } // Sets the font of the dialog void CDialogTemplate::SetFont(char* szFaceName, WORD wFontSize) { m_dwStyle |= DS_SETFONT; if (m_szFont) delete [] m_szFont; m_szFont = new char[lstrlen(szFaceName)]; lstrcpy(m_szFont, szFaceName); m_sFontSize = wFontSize; } // Adds an item to the dialog void CDialogTemplate::AddItem(DialogItemTemplate item) { DialogItemTemplate* newItem = new DialogItemTemplate; CopyMemory(newItem, &item, sizeof(DialogItemTemplate)); if (item.szClass && !IS_INTRESOURCE(item.szClass)) { newItem->szClass = new char[lstrlen(item.szClass)+1]; lstrcpy(newItem->szClass, item.szClass); } if (item.szTitle && !IS_INTRESOURCE(item.szTitle)) { newItem->szTitle = new char[lstrlen(item.szTitle)+1]; lstrcpy(newItem->szTitle, item.szTitle); } if (item.wCreateDataSize) { newItem->szCreationData = new char[item.wCreateDataSize]; memcpy(newItem->szCreationData, item.szCreationData, item.wCreateDataSize); } m_vItems.push_back(newItem); } // Moves all of the items in the dialog by (x,y) and resizes the dialog by (x,y) void CDialogTemplate::MoveAllAndResize(short x, short y) { // Move all items for (int i = 0; i < m_vItems.size(); i++) { m_vItems[i]->sX += x; m_vItems[i]->sY += y; } // Resize m_sWidth += x; m_sHeight += y; } // Converts pixels to this dialog's units void CDialogTemplate::PixelsToDlgUnits(short& x, short& y) { DWORD dwTemp; BYTE* pbDlg = Save(dwTemp); HWND hDlg = CreateDialogIndirect(GetModuleHandle(0), (DLGTEMPLATE*)pbDlg, 0, 0); delete [] pbDlg; if (!hDlg) throw runtime_error("Can't create dialog from template!"); RECT r = {0, 0, 1024, 1024}; MapDialogRect(hDlg, &r); DestroyWindow(hDlg); x = float(x) / (float(r.right)/1024); y = float(y) / (float(r.bottom)/1024); } // Converts pixels to this dialog's units void CDialogTemplate::DlgUnitsToPixels(short& x, short& y) { DWORD dwTemp; BYTE* pbDlg = Save(dwTemp); HWND hDlg = CreateDialogIndirect(GetModuleHandle(0), (DLGTEMPLATE*)pbDlg, 0, 0); delete [] pbDlg; if (!hDlg) throw runtime_error("Can't create dialog from template!"); RECT r = {0, 0, 1024, 1024}; MapDialogRect(hDlg, &r); DestroyWindow(hDlg); x = float(x) * (float(r.right)/1024); y = float(y) * (float(r.bottom)/1024); } // Saves the dialog in the form of DLGTEMPLATE[EX] BYTE* CDialogTemplate::Save(DWORD& dwSize) { // We need the size first to know how much memory to allocate dwSize = GetSize(); BYTE* pbDlg = new BYTE[dwSize]; ZeroMemory(pbDlg, dwSize); BYTE* seeker = pbDlg; if (m_bExtended) { DLGTEMPLATEEX dh = { 0x0001, 0xFFFF, m_dwHelpId, m_dwExtStyle, m_dwStyle, m_vItems.size(), m_sX, m_sY, m_sWidth, m_sHeight }; CopyMemory(seeker, &dh, sizeof(DLGTEMPLATEEX)); seeker += sizeof(DLGTEMPLATEEX); } else { DLGTEMPLATE dh = { m_dwStyle, m_dwExtStyle, m_vItems.size(), m_sX, m_sY, m_sWidth, m_sHeight }; CopyMemory(seeker, &dh, sizeof(DLGTEMPLATE)); seeker += sizeof(DLGTEMPLATE); } // Write menu variant length array WriteStringOrId(m_szMenu); // Write class variant length array WriteStringOrId(m_szClass); // Write title variant length array WriteStringOrId(m_szTitle); // Write font variant length array, size, and extended info (if needed) if (m_dwStyle & DS_SETFONT) { *(short*)seeker = m_sFontSize; seeker += sizeof(short); if (m_bExtended) { *(short*)seeker = m_sFontWeight; seeker += sizeof(short); *(short*)seeker = m_bItalic ? TRUE : FALSE; seeker += sizeof(short); } WriteStringOrId(m_szFont); } // Write all of the items for (int i = 0; i < m_vItems.size(); i++) { // DLGITEMTEMPLATE[EX]s must be aligned on DWORD boundry if (DWORD(seeker - pbDlg) % sizeof(DWORD)) seeker += sizeof(WORD); if (m_bExtended) { DLGITEMTEMPLATEEX dih = { m_vItems[i]->dwHelpId, m_vItems[i]->dwExtStyle, m_vItems[i]->dwStyle, m_vItems[i]->sX, m_vItems[i]->sY, m_vItems[i]->sWidth, m_vItems[i]->sHeight, m_vItems[i]->wId }; CopyMemory(seeker, &dih, sizeof(DLGITEMTEMPLATEEX)); seeker += sizeof(DLGITEMTEMPLATEEX); } else { DLGITEMTEMPLATE dih = { m_vItems[i]->dwStyle, m_vItems[i]->dwExtStyle, m_vItems[i]->sX, m_vItems[i]->sY, m_vItems[i]->sWidth, m_vItems[i]->sHeight, m_vItems[i]->wId }; CopyMemory(seeker, &dih, sizeof(DLGITEMTEMPLATE)); seeker += sizeof(DLGITEMTEMPLATE); } // Write class variant length array WriteStringOrId(m_vItems[i]->szClass); // Write title variant length array WriteStringOrId(m_vItems[i]->szTitle); // Write creation data variant length array // First write its size if (m_vItems[i]->wCreateDataSize) m_vItems[i]->wCreateDataSize += sizeof(WORD); *(WORD*)seeker = m_vItems[i]->wCreateDataSize; seeker += sizeof(WORD); // If size is nonzero write the data too if (m_vItems[i]->wCreateDataSize) { CopyMemory(seeker, m_vItems[i]->szCreationData, m_vItems[i]->wCreateDataSize); seeker += m_vItems[i]->wCreateDataSize; } } // DONE! return pbDlg; } // Returns the size that the DLGTEMPLATE[EX] will take when saved DWORD CDialogTemplate::GetSize() { DWORD dwSize = m_bExtended ? sizeof(DLGTEMPLATEEX) : sizeof(DLGTEMPLATE); // Menu AddStringOrIdSize(m_szMenu); // Class AddStringOrIdSize(m_szClass); // Title AddStringOrIdSize(m_szTitle); // Font if (m_dwStyle & DS_SETFONT) { dwSize += sizeof(WORD) + (m_bExtended ? 2*sizeof(short) : 0); AddStringOrIdSize(m_szFont); } for (int i = 0; i < m_vItems.size(); i++) { // DLGITEMTEMPLATE[EX]s must be aligned on DWORD boundry ALIGN(dwSize, sizeof(DWORD)); dwSize += m_bExtended ? sizeof(DLGITEMTEMPLATEEX) : sizeof(DLGITEMTEMPLATE); // Class AddStringOrIdSize(m_vItems[i]->szClass); // Title AddStringOrIdSize(m_vItems[i]->szTitle); dwSize += sizeof(WORD) + m_vItems[i]->wCreateDataSize; } return dwSize; }