From e9ce57739d810673edbd41fd96a6bf13c55cfcc4 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Tue, 6 Sep 2005 09:20:42 +0000 Subject: [PATCH] - Add the CHM Utility API. - Load help window values from the CHM file. --- dlls/hhctrl.ocx/Makefile.in | 1 + dlls/hhctrl.ocx/chm.c | 186 ++++++++++++++++++++++++++++++++++++ dlls/hhctrl.ocx/chm.h | 38 ++++++++ dlls/hhctrl.ocx/help.c | 102 +++++++++++++++----- 4 files changed, 303 insertions(+), 24 deletions(-) create mode 100644 dlls/hhctrl.ocx/chm.c create mode 100644 dlls/hhctrl.ocx/chm.h diff --git a/dlls/hhctrl.ocx/Makefile.in b/dlls/hhctrl.ocx/Makefile.in index 80bedc7ab65..0ce6d65c246 100644 --- a/dlls/hhctrl.ocx/Makefile.in +++ b/dlls/hhctrl.ocx/Makefile.in @@ -6,6 +6,7 @@ MODULE = hhctrl.ocx IMPORTS = advapi32 comctl32 shell32 ole32 user32 gdi32 kernel32 C_SRCS = \ + chm.c \ help.c \ hhctrl.c \ main.c \ diff --git a/dlls/hhctrl.ocx/chm.c b/dlls/hhctrl.ocx/chm.c new file mode 100644 index 00000000000..56d9e4a21d9 --- /dev/null +++ b/dlls/hhctrl.ocx/chm.c @@ -0,0 +1,186 @@ +/* + * CHM Utility API + * + * Copyright 2005 James Hawkins + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "ole2.h" +#include "htmlhelp.h" + +#include "initguid.h" +#include "chm.h" + +static LPWSTR CHM_ANSIToUnicode(LPCSTR ansi) +{ + LPWSTR unicode; + int count; + + count = MultiByteToWideChar(CP_ACP, 0, ansi, -1, NULL, 0); + unicode = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, ansi, -1, unicode, count); + + return unicode; +} + +/* Reads a string from the #STRINGS section in the CHM file */ +LPWSTR CHM_ReadString(CHMInfo *pChmInfo, DWORD dwOffset) +{ + LARGE_INTEGER liOffset; + IStorage *pStorage = pChmInfo->pStorage; + IStream *pStream; + DWORD cbRead; + ULONG iPos; + DWORD dwSize; + LPSTR szString; + LPWSTR stringW; + + const int CB_READ_BLOCK = 64; + static const WCHAR stringsW[] = {'#','S','T','R','I','N','G','S',0}; + + dwSize = CB_READ_BLOCK; + szString = HeapAlloc(GetProcessHeap(), 0, dwSize); + + if (FAILED(IStorage_OpenStream(pStorage, stringsW, NULL, STGM_READ, 0, &pStream))) + return NULL; + + liOffset.QuadPart = dwOffset; + + if (FAILED(IStream_Seek(pStream, liOffset, STREAM_SEEK_SET, NULL))) + { + IStream_Release(pStream); + return NULL; + } + + while (SUCCEEDED(IStream_Read(pStream, szString, CB_READ_BLOCK, &cbRead))) + { + if (!cbRead) + return NULL; + + for (iPos = 0; iPos < cbRead; iPos++) + { + if (!szString[iPos]) + { + stringW = CHM_ANSIToUnicode(szString); + HeapFree(GetProcessHeap(), 0, szString); + return stringW; + } + } + + dwSize *= 2; + szString = HeapReAlloc(GetProcessHeap(), 0, szString, dwSize); + szString += cbRead; + } + + /* didn't find a string */ + return NULL; +} + +/* Loads the HH_WINTYPE data from the CHM file + * + * FIXME: There may be more than one window type in the file, so + * add the ability to choose a certain window type + */ +BOOL CHM_LoadWinTypeFromCHM(CHMInfo *pChmInfo, HH_WINTYPEW *pHHWinType) +{ + LARGE_INTEGER liOffset; + IStorage *pStorage = pChmInfo->pStorage; + IStream *pStream; + HRESULT hr; + DWORD cbRead; + + static const WCHAR windowsW[] = {'#','W','I','N','D','O','W','S',0}; + + hr = IStorage_OpenStream(pStorage, windowsW, NULL, STGM_READ, 0, &pStream); + if (FAILED(hr)) + return FALSE; + + /* jump past the #WINDOWS header */ + liOffset.QuadPart = sizeof(DWORD) * 2; + + hr = IStream_Seek(pStream, liOffset, STREAM_SEEK_SET, NULL); + if (FAILED(hr)) goto done; + + /* read the HH_WINTYPE struct data */ + hr = IStream_Read(pStream, pHHWinType, sizeof(*pHHWinType), &cbRead); + if (FAILED(hr)) goto done; + + /* convert the #STRINGS offsets to actual strings */ + pHHWinType->pszType = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszType); + pHHWinType->pszCaption = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszCaption); + pHHWinType->pszToc = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszToc); + pHHWinType->pszIndex = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszIndex); + pHHWinType->pszFile = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszFile); + pHHWinType->pszHome = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszHome); + pHHWinType->pszJump1 = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszJump1); + pHHWinType->pszJump2 = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszJump2); + pHHWinType->pszUrlJump1 = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszUrlJump1); + pHHWinType->pszUrlJump2 = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszUrlJump2); + + /* FIXME: pszCustomTabs is a list of multiple zero-terminated strings so ReadString won't + * work in this case + */ +#if 0 + pHHWinType->pszCustomTabs = CHM_ReadString(pChmInfo, (DWORD)pHHWinType->pszCustomTabs); +#endif + +done: + IStream_Release(pStream); + + return SUCCEEDED(hr); +} + +/* Opens the CHM file for reading */ +BOOL CHM_OpenCHM(CHMInfo *pChmInfo, LPCWSTR szFile) +{ + pChmInfo->szFile = szFile; + + if (FAILED(CoCreateInstance(&CLSID_ITStorage, NULL, CLSCTX_INPROC_SERVER, + &IID_IITStorage, (void **) &pChmInfo->pITStorage))) + return FALSE; + + if (FAILED(IITStorage_StgOpenStorage(pChmInfo->pITStorage, szFile, NULL, + STGM_READ | STGM_SHARE_DENY_WRITE, + NULL, 0, &pChmInfo->pStorage))) + return FALSE; + + return TRUE; +} + +void CHM_CloseCHM(CHMInfo *pCHMInfo) +{ + IITStorage_Release(pCHMInfo->pITStorage); + IStorage_Release(pCHMInfo->pStorage); +} + +/* Creates a Url of a CHM file that can be used with WB_Navigate */ +void CHM_CreateITSUrl(CHMInfo *pChmInfo, LPCWSTR szIndex, LPWSTR szUrl) +{ + static const WCHAR formatW[] = { + 'i','t','s',':','%','s',':',':','%','s',0 + }; + + wsprintfW(szUrl, formatW, pChmInfo->szFile, szIndex); +} diff --git a/dlls/hhctrl.ocx/chm.h b/dlls/hhctrl.ocx/chm.h new file mode 100644 index 00000000000..28a7110c7e8 --- /dev/null +++ b/dlls/hhctrl.ocx/chm.h @@ -0,0 +1,38 @@ +/* + * CHM Utility API + * + * Copyright 2005 James Hawkins + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CHM_H +#define CHM_H + +#include "wine/itss.h" + +typedef struct CHMInfo +{ + IITStorage *pITStorage; + IStorage *pStorage; + LPCWSTR szFile; +} CHMInfo; + +BOOL CHM_OpenCHM(CHMInfo *pCHMInfo, LPCWSTR szFile); +BOOL CHM_LoadWinTypeFromCHM(CHMInfo *pCHMInfo, HH_WINTYPEW *pHHWinType); +void CHM_CloseCHM(CHMInfo *pCHMInfo); +void CHM_CreateITSUrl(CHMInfo *pCHMInfo, LPCWSTR szIndex, LPWSTR szUrl); + +#endif diff --git a/dlls/hhctrl.ocx/help.c b/dlls/hhctrl.ocx/help.c index d3154d027e0..fb631fd6427 100644 --- a/dlls/hhctrl.ocx/help.c +++ b/dlls/hhctrl.ocx/help.c @@ -31,6 +31,7 @@ #include "wine/unicode.h" #include "resource.h" +#include "chm.h" /* Window type defaults */ @@ -43,6 +44,7 @@ typedef struct tagHHInfo { HH_WINTYPEW *pHHWinType; + CHMInfo *pCHMInfo; HINSTANCE hInstance; LPWSTR szCmdLine; HWND hwndTabCtrl; @@ -219,15 +221,17 @@ static BOOL HH_AddToolbar(HHInfo *pHHInfo) { HWND hToolbar; HWND hwndParent = pHHInfo->pHHWinType->hwndHelp; - DWORD toolbarFlags = pHHInfo->pHHWinType->fsToolBarFlags; + DWORD toolbarFlags; TBBUTTON buttons[IDTB_TOC_PREV - IDTB_EXPAND]; TBADDBITMAP tbAB; DWORD dwStyles, dwExStyles; DWORD dwNumButtons, dwIndex; - /* FIXME: Remove the following line once we read the CHM file */ - toolbarFlags = HHWIN_BUTTON_EXPAND | HHWIN_BUTTON_BACK | HHWIN_BUTTON_STOP | - HHWIN_BUTTON_REFRESH | HHWIN_BUTTON_HOME | HHWIN_BUTTON_PRINT; + if (pHHInfo->pHHWinType->fsWinProperties & HHWIN_PARAM_TB_FLAGS) + toolbarFlags = pHHInfo->pHHWinType->fsToolBarFlags; + else + toolbarFlags = HHWIN_DEF_BUTTONS; + TB_AddButtonsFromFlags(buttons, toolbarFlags, &dwNumButtons); dwStyles = WS_CHILDWINDOW | WS_VISIBLE | TBSTYLE_FLAT | @@ -329,11 +333,17 @@ static BOOL HH_AddNavigationPane(HHInfo *pHHInfo) if (!hwndTabCtrl) return FALSE; - /* FIXME: Check which tabs to include when we read the CHM file */ - NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_CONTENTS, dwIndex++); - NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_INDEX, dwIndex++); - NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_SEARCH, dwIndex++); - NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_FAVORITES, dwIndex++); + if (pHHInfo->pHHWinType->pszToc) + NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_CONTENTS, dwIndex++); + + if (pHHInfo->pHHWinType->pszIndex) + NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_INDEX, dwIndex++); + + if (pHHInfo->pHHWinType->fsWinProperties & HHWIN_PROP_TAB_SEARCH) + NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_SEARCH, dwIndex++); + + if (pHHInfo->pHHWinType->fsWinProperties & HHWIN_PROP_TAB_FAVORITES) + NP_CreateTab(pHHInfo->hInstance, hwndTabCtrl, IDS_FAVORITES, dwIndex++); SendMessageW(hwndTabCtrl, WM_SETFONT, (WPARAM)pHHInfo->hFont, TRUE); @@ -444,6 +454,7 @@ static BOOL HH_CreateHelpWindow(HHInfo *pHHInfo) { HWND hWnd; HINSTANCE hInstance = pHHInfo->hInstance; + RECT winPos = pHHInfo->pHHWinType->rcWindowPos; WNDCLASSEXW wcex; DWORD dwStyles, dwExStyles; DWORD x, y, width, height; @@ -452,10 +463,6 @@ static BOOL HH_CreateHelpWindow(HHInfo *pHHInfo) 'H','H',' ', 'P','a','r','e','n','t',0 }; - static const WCHAR windowTitleW[] = { - 'H','T','M','L',' ','H','e','l','p',0 - }; - wcex.cbSize = sizeof(WNDCLASSEXW); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)Help_WndProc; @@ -471,18 +478,36 @@ static BOOL HH_CreateHelpWindow(HHInfo *pHHInfo) RegisterClassExW(&wcex); - dwStyles = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | - WS_EX_WINDOWEDGE | WS_EX_APPWINDOW; + /* Read in window parameters if available */ + if (pHHInfo->pHHWinType->fsValidMembers & HHWIN_PARAM_STYLES) + dwStyles = pHHInfo->pHHWinType->dwStyles; + else + dwStyles = WS_OVERLAPPEDWINDOW | WS_VISIBLE | + WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - /* these will be loaded from the CHM file in the future if they're provided */ - x = WINTYPE_DEFAULT_X; - y = WINTYPE_DEFAULT_Y; - width = WINTYPE_DEFAULT_WIDTH; - height = WINTYPE_DEFAULT_HEIGHT; + if (pHHInfo->pHHWinType->fsValidMembers & HHWIN_PARAM_EXSTYLES) + dwExStyles = pHHInfo->pHHWinType->dwExStyles; + else + dwExStyles = WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_APPWINDOW | + WS_EX_WINDOWEDGE | WS_EX_RIGHTSCROLLBAR; - hWnd = CreateWindowExW(dwExStyles, windowClassW, windowTitleW, dwStyles, - x, y, width, height, NULL, NULL, hInstance, NULL); + if (pHHInfo->pHHWinType->fsValidMembers & HHWIN_PARAM_RECT) + { + x = winPos.left; + y = winPos.top; + width = winPos.right - x; + height = winPos.bottom - y; + } + else + { + x = WINTYPE_DEFAULT_X; + y = WINTYPE_DEFAULT_Y; + width = WINTYPE_DEFAULT_WIDTH; + height = WINTYPE_DEFAULT_HEIGHT; + } + + hWnd = CreateWindowExW(dwExStyles, windowClassW, pHHInfo->pHHWinType->pszCaption, + dwStyles, x, y, width, height, NULL, NULL, hInstance, NULL); if (!hWnd) return FALSE; @@ -546,6 +571,7 @@ static HHInfo *HH_OpenHH(HINSTANCE hInstance, LPWSTR szCmdLine) HHInfo *pHHInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HHInfo)); pHHInfo->pHHWinType = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HH_WINTYPEW)); + pHHInfo->pCHMInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(CHMInfo)); pHHInfo->hInstance = hInstance; pHHInfo->szCmdLine = szCmdLine; @@ -557,10 +583,38 @@ static void HH_Close(HHInfo *pHHInfo) if (!pHHInfo) return; + /* Free allocated strings */ + if (pHHInfo->pHHWinType) + { + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszType); + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszCaption); + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszToc); + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszType); + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszIndex); + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszFile); + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszHome); + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszJump1); + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszJump2); + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszUrlJump1); + HeapFree(GetProcessHeap(), 0, (LPWSTR)pHHInfo->pHHWinType->pszUrlJump2); + } + HeapFree(GetProcessHeap(), 0, pHHInfo->pHHWinType); + HeapFree(GetProcessHeap(), 0, pHHInfo->pCHMInfo); HeapFree(GetProcessHeap(), 0, pHHInfo->szCmdLine); } +static BOOL HH_OpenCHM(HHInfo *pHHInfo) +{ + if (!CHM_OpenCHM(pHHInfo->pCHMInfo, pHHInfo->szCmdLine)) + return FALSE; + + if (!CHM_LoadWinTypeFromCHM(pHHInfo->pCHMInfo, pHHInfo->pHHWinType)) + return FALSE; + + return TRUE; +} + /* FIXME: Check szCmdLine for bad arguments */ int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine) { @@ -571,7 +625,7 @@ int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine) return -1; pHHInfo = HH_OpenHH(hInstance, HH_ANSIToUnicode(szCmdLine)); - if (!pHHInfo || !HH_CreateViewer(pHHInfo)) + if (!pHHInfo || !HH_OpenCHM(pHHInfo) || !HH_CreateViewer(pHHInfo)) { OleUninitialize(); return -1;