[HHCTRL.OCX]

sync hhctrl.ocx to wine 1.1.40

svn path=/trunk/; revision=45990
This commit is contained in:
Christoph von Wittich 2010-03-07 12:48:05 +00:00
parent fcaa30c541
commit 96363863d3
31 changed files with 1590 additions and 202 deletions

View file

@ -21,6 +21,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_CZECH, SUBLANG_DEFAULT
/* Czech strings in CP1250 */

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_DANISH, SUBLANG_DEFAULT
STRINGTABLE

View file

@ -19,6 +19,10 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
#pragma code_page(65001)
LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
STRINGTABLE
@ -33,14 +37,14 @@ STRINGTABLE
BEGIN
IDTB_EXPAND "Anzeigen"
IDTB_CONTRACT "Verstecken"
IDTB_STOP "Stop"
IDTB_STOP "Stopp"
IDTB_REFRESH "Aktualisieren"
IDTB_BACK "Zurück"
IDTB_BACK "Zurück"
IDTB_HOME "Startseite"
IDTB_SYNC "Synchronisieren"
IDTB_PRINT "Drucken"
IDTB_OPTIONS "Einstellungen"
IDTB_FORWARD "Vorwärts"
IDTB_FORWARD "Vorwärts"
IDTB_NOTES "IDTB_NOTES"
IDTB_BROWSE_FWD "IDTB_BROWSE_FWD"
IDTB_BROWSE_BACK "IDT_BROWSE_BACK"
@ -52,7 +56,7 @@ BEGIN
IDTB_JUMP1 "Sprung1"
IDTB_JUMP2 "Sprung2"
IDTB_CUSTOMIZE "Anpassen"
IDTB_ZOOM "Vergrößern"
IDTB_ZOOM "Vergrößern"
IDTB_TOC_NEXT "IDTB_TOC_NEXT"
IDTB_TOC_PREV "IDTB_TOC_PREV"
END

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_GREEK, SUBLANG_DEFAULT
STRINGTABLE

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
STRINGTABLE
@ -56,3 +58,13 @@ BEGIN
IDTB_TOC_NEXT "IDTB_TOC_NEXT"
IDTB_TOC_PREV "IDTB_TOC_PREV"
END
LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
STRINGTABLE
BEGIN
IDS_CONTENTS "&Contents"
IDS_INDEX "I&ndex"
IDS_SEARCH "&Search"
IDS_FAVORITES "Favour&ites"
END

View file

@ -0,0 +1,63 @@
/*
* HTML Help resources
* Spanish Language Support
*
* Copyright 2010 José Manuel Ferrer Ortiz
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
/* UTF-8 */
#pragma code_page(65001)
LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
STRINGTABLE
BEGIN
IDS_CONTENTS "&Contenido"
IDS_INDEX "Í&ndice"
IDS_SEARCH "&Buscar"
IDS_FAVORITES "Favor&itos"
END
STRINGTABLE
BEGIN
IDTB_EXPAND "Mostrar"
IDTB_CONTRACT "Ocultar"
IDTB_STOP "Parar"
IDTB_REFRESH "Recargar"
IDTB_BACK "Atrás"
IDTB_HOME "Inicio"
IDTB_SYNC "Sincronizar"
IDTB_PRINT "Imprimir"
IDTB_OPTIONS "Opciones"
IDTB_FORWARD "Adelante"
IDTB_NOTES "IDTB_NOTES"
IDTB_BROWSE_FWD "IDTB_BROWSE_FWD"
IDTB_BROWSE_BACK "IDT_BROWSE_BACK"
IDTB_CONTENTS "IDTB_CONTENTS"
IDTB_INDEX "IDTB_INDEX"
IDTB_SEARCH "IDTB_SEARCH"
IDTB_HISTORY "IDTB_HISTORY"
IDTB_FAVORITES "IDTB_FAVORITES"
IDTB_JUMP1 "Jump1"
IDTB_JUMP2 "Jump2"
IDTB_CUSTOMIZE "Personalizar"
IDTB_ZOOM "Zoom"
IDTB_TOC_NEXT "IDTB_TOC_NEXT"
IDTB_TOC_PREV "IDTB_TOC_PREV"
END

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_FINNISH, SUBLANG_DEFAULT
STRINGTABLE

View file

@ -19,6 +19,11 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
/* UTF-8 */
#pragma code_page(65001)
LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
STRINGTABLE
@ -33,9 +38,9 @@ STRINGTABLE
BEGIN
IDTB_EXPAND "Afficher"
IDTB_CONTRACT "Cacher"
IDTB_STOP "Arrêter"
IDTB_STOP "Arrêter"
IDTB_REFRESH "A&ctualiser"
IDTB_BACK "Précédent"
IDTB_BACK "Précédent"
IDTB_HOME "Sommaire"
IDTB_SYNC "Synchroniser"
IDTB_PRINT "Imprimer"
@ -49,8 +54,8 @@ BEGIN
IDTB_SEARCH "IDTB_SEARCH"
IDTB_HISTORY "IDTB_HISTORY"
IDTB_FAVORITES "IDTB_FAVORITES"
IDTB_JUMP1 "Jump1"
IDTB_JUMP2 "Jump2"
IDTB_JUMP1 "Saut1"
IDTB_JUMP2 "Saut2"
IDTB_CUSTOMIZE "Personnaliser"
IDTB_ZOOM "Zoom"
IDTB_TOC_NEXT "IDTB_TOC_NEXT"

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_HUNGARIAN, SUBLANG_DEFAULT
STRINGTABLE

View file

@ -20,6 +20,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT
STRINGTABLE

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
/* UTF-8 */
#pragma code_page(65001)
@ -59,5 +61,3 @@ BEGIN
IDTB_TOC_NEXT "IDTB_TOC_NEXT"
IDTB_TOC_PREV "IDTB_TOC_PREV"
END
#pragma code_page(default)

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_DUTCH, SUBLANG_NEUTRAL
STRINGTABLE

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL
STRINGTABLE

View file

@ -20,6 +20,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_POLISH, SUBLANG_DEFAULT
STRINGTABLE

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL
STRINGTABLE

View file

@ -19,40 +19,45 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
/* UTF-8 */
#pragma code_page(65001)
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
STRINGTABLE
BEGIN
IDS_CONTENTS "&Содержание"
IDS_INDEX "&Оглавление"
IDS_SEARCH "&Поиск"
IDS_FAVORITES "&Избранное"
IDS_CONTENTS "&Содержание"
IDS_INDEX "&Оглавление"
IDS_SEARCH "&Поиск"
IDS_FAVORITES "&Избранное"
END
STRINGTABLE
BEGIN
IDTB_EXPAND "Показать"
IDTB_CONTRACT "Спрятать"
IDTB_STOP "Остановить"
IDTB_REFRESH "Обновить"
IDTB_BACK "Назад"
IDTB_HOME "В начало"
IDTB_SYNC "Синхронизировать"
IDTB_PRINT "Печать"
IDTB_OPTIONS "Настройки"
IDTB_FORWARD "Вперёд"
IDTB_NOTES "Записки"
IDTB_BROWSE_FWD "Просмотр вперёд"
IDTB_BROWSE_BACK "Просмотр назад"
IDTB_CONTENTS "Содержание"
IDTB_INDEX "Оглавление"
IDTB_SEARCH "Поиск"
IDTB_HISTORY "История"
IDTB_FAVORITES "Избранное"
IDTB_JUMP1 "Переход 1"
IDTB_JUMP2 "Переход 2"
IDTB_CUSTOMIZE "Персонализовать"
IDTB_ZOOM "Масштаб"
IDTB_TOC_NEXT "Следующая глава"
IDTB_TOC_PREV "Предыдущая глава"
IDTB_EXPAND "Показать"
IDTB_CONTRACT "Спрятать"
IDTB_STOP "Остановить"
IDTB_REFRESH "Обновить"
IDTB_BACK "Назад"
IDTB_HOME "В начало"
IDTB_SYNC "Синхронизировать"
IDTB_PRINT "Печать"
IDTB_OPTIONS "Настройки"
IDTB_FORWARD "Вперёд"
IDTB_NOTES "Записки"
IDTB_BROWSE_FWD "Просмотр вперёд"
IDTB_BROWSE_BACK "Просмотр назад"
IDTB_CONTENTS "Содержание"
IDTB_INDEX "Оглавление"
IDTB_SEARCH "Поиск"
IDTB_HISTORY "История"
IDTB_FAVORITES "Избранное"
IDTB_JUMP1 "Переход 1"
IDTB_JUMP2 "Переход 2"
IDTB_CUSTOMIZE "Персонализовать"
IDTB_ZOOM "Масштаб"
IDTB_TOC_NEXT "Следующая глава"
IDTB_TOC_PREV "Предыдущая глава"
END

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
#pragma code_page(65001)
LANGUAGE LANG_SLOVENIAN, SUBLANG_DEFAULT
@ -58,5 +60,3 @@ BEGIN
IDTB_TOC_NEXT "IDTB_TOC_NEXT"
IDTB_TOC_PREV "IDTB_TOC_PREV"
END
#pragma code_page(default)

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL
STRINGTABLE

View file

@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
LANGUAGE LANG_TURKISH, SUBLANG_DEFAULT
STRINGTABLE

View file

@ -0,0 +1,75 @@
/*
* HTML Help resources
* Ukrainian Language Support
*
* Copyright 2005 James Hawkins
* Copyright 2007 Artem Reznikov
* Copyright 2010 Igor Paliychuk
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
/* UTF-8 */
#pragma code_page(65001)
LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT
STRINGTABLE
BEGIN
IDS_CONTENTS "&Зміст"
IDS_INDEX "&Вказівник"
IDS_SEARCH "&Пошук"
IDS_FAVORITES "&Обране"
END
STRINGTABLE
BEGIN
IDTB_EXPAND "Показувати"
IDTB_CONTRACT "Приховати"
IDTB_STOP "Зупинити"
IDTB_REFRESH "Оновити"
IDTB_BACK "Назад"
IDTB_HOME "Додому"
IDTB_SYNC "Синхронізувати"
IDTB_PRINT "Друк"
IDTB_OPTIONS "Параметри"
IDTB_FORWARD "Вперед"
IDTB_NOTES "IDTB_NOTES"
IDTB_BROWSE_FWD "IDTB_BROWSE_FWD"
IDTB_BROWSE_BACK "IDT_BROWSE_BACK"
IDTB_CONTENTS "IDTB_CONTENTS"
IDTB_INDEX "IDTB_INDEX"
IDTB_SEARCH "IDTB_SEARCH"
IDTB_HISTORY "IDTB_HISTORY"
IDTB_FAVORITES "IDTB_FAVORITES"
IDTB_JUMP1 "Jump1"
IDTB_JUMP2 "Jump2"
IDTB_CUSTOMIZE "Налаштування"
IDTB_ZOOM "Збільшення"
IDTB_TOC_NEXT "IDTB_TOC_NEXT"
IDTB_TOC_PREV "IDTB_TOC_PREV"
END
LANGUAGE LANG_UKRAINIAN, SUBLANG_NEUTRAL
STRINGTABLE
BEGIN
IDS_CONTENTS "&Зміст"
IDS_INDEX "&Вказівник"
IDS_SEARCH "&Пошук"
IDS_FAVORITES "&Обране"
END

View file

@ -18,6 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "resource.h"
/* Chinese text is encoded in UTF-8 */
#pragma code_page(65001)
@ -96,5 +98,3 @@ BEGIN
IDTB_TOC_NEXT "後一項"
IDTB_TOC_PREV "前一項"
END
#pragma code_page(default)

View file

@ -38,12 +38,13 @@ static LPCSTR GetChmString(CHMInfo *chm, DWORD offset)
return NULL;
if(chm->strings_size <= (offset >> BLOCK_BITS)) {
chm->strings_size = (offset >> BLOCK_BITS)+1;
if(chm->strings)
chm->strings = heap_realloc_zero(chm->strings,
chm->strings_size = ((offset >> BLOCK_BITS)+1)*sizeof(char*));
chm->strings_size*sizeof(char*));
else
chm->strings = heap_alloc_zero(
chm->strings_size = ((offset >> BLOCK_BITS)+1)*sizeof(char*));
chm->strings_size*sizeof(char*));
}
@ -388,7 +389,6 @@ CHMInfo *OpenCHM(LPCWSTR szFile)
WARN("Could not open storage: %08x\n", hres);
return CloseCHM(ret);
}
hres = IStorage_OpenStream(ret->pStorage, wszSTRINGS, NULL, STGM_READ, 0,
&ret->strings_stream);
if(FAILED(hres)) {

View file

@ -20,13 +20,12 @@
#define NONAMELESSSTRUCT
#include "hhctrl.h"
#include "stream.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
#define BLOCK_SIZE 0x1000
typedef enum {
INSERT_NEXT,
INSERT_CHILD
@ -50,134 +49,6 @@ static void free_content_item(ContentItem *item)
}
}
typedef struct {
char *buf;
int size;
int len;
} strbuf_t;
static void strbuf_init(strbuf_t *buf)
{
buf->size = 8;
buf->len = 0;
buf->buf = heap_alloc(buf->size);
}
static void strbuf_zero(strbuf_t *buf)
{
buf->len = 0;
}
static void strbuf_free(strbuf_t *buf)
{
heap_free(buf->buf);
}
static void strbuf_append(strbuf_t *buf, const char *data, int len)
{
if(buf->len+len > buf->size) {
buf->size = buf->len+len;
buf->buf = heap_realloc(buf->buf, buf->size);
}
memcpy(buf->buf+buf->len, data, len);
buf->len += len;
}
typedef struct {
IStream *str;
char buf[BLOCK_SIZE];
ULONG size;
ULONG p;
} stream_t;
static void stream_init(stream_t *stream, IStream *str)
{
memset(stream, 0, sizeof(stream_t));
stream->str = str;
}
static BOOL stream_chr(stream_t *stream, strbuf_t *buf, char c)
{
BOOL b = TRUE;
ULONG i;
while(b) {
for(i=stream->p; i<stream->size; i++) {
if(stream->buf[i] == c) {
b = FALSE;
break;
}
}
if(buf && i > stream->p)
strbuf_append(buf, stream->buf+stream->p, i-stream->p);
stream->p = i;
if(stream->p == stream->size) {
stream->p = 0;
IStream_Read(stream->str, stream->buf, sizeof(stream->buf), &stream->size);
if(!stream->size)
break;
}
}
return stream->size != 0;
}
static void get_node_name(strbuf_t *node, strbuf_t *name)
{
const char *ptr = node->buf+1;
strbuf_zero(name);
while(*ptr != '>' && !isspace(*ptr))
ptr++;
strbuf_append(name, node->buf+1, ptr-node->buf-1);
strbuf_append(name, "", 1);
}
static BOOL next_node(stream_t *stream, strbuf_t *buf)
{
if(!stream_chr(stream, NULL, '<'))
return FALSE;
if(!stream_chr(stream, buf, '>'))
return FALSE;
strbuf_append(buf, ">", 2);
return TRUE;
}
static const char *get_attr(const char *node, const char *name, int *len)
{
const char *ptr, *ptr2;
char name_buf[32];
int nlen;
nlen = strlen(name);
memcpy(name_buf, name, nlen);
name_buf[nlen++] = '=';
name_buf[nlen++] = '\"';
name_buf[nlen] = 0;
ptr = strstr(node, name_buf);
if(!ptr) {
WARN("name not found\n");
return NULL;
}
ptr += nlen;
ptr2 = strchr(ptr, '\"');
if(!ptr2)
return NULL;
*len = ptr2-ptr;
return ptr;
}
static void parse_obj_node_param(ContentItem *item, ContentItem *hhc_root, const char *text)
{
const char *ptr;

View file

@ -44,6 +44,7 @@ static LRESULT Help_OnSize(HWND hWnd);
#define TAB_TOP_PADDING 8
#define TAB_RIGHT_PADDING 4
#define TAB_MARGIN 8
#define EDIT_HEIGHT 20
static const WCHAR szEmpty[] = {0};
@ -320,8 +321,10 @@ static LRESULT Child_OnPaint(HWND hWnd)
return 0;
}
static void ResizeTabChild(HHInfo *info, HWND hwnd)
static void ResizeTabChild(HHInfo *info, int tab)
{
HWND hwnd = info->tabs[tab].hwnd;
INT width, height;
RECT rect, tabrc;
DWORD cnt;
@ -333,9 +336,47 @@ static void ResizeTabChild(HHInfo *info, HWND hwnd)
rect.top = TAB_TOP_PADDING + cnt*(tabrc.bottom-tabrc.top) + TAB_MARGIN;
rect.right -= TAB_RIGHT_PADDING + TAB_MARGIN;
rect.bottom -= TAB_MARGIN;
width = rect.right-rect.left;
height = rect.bottom-rect.top;
SetWindowPos(hwnd, NULL, rect.left, rect.top, rect.right-rect.left,
rect.bottom-rect.top, SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(hwnd, NULL, rect.left, rect.top, width, height,
SWP_NOZORDER | SWP_NOACTIVATE);
switch (tab)
{
case TAB_INDEX: {
int scroll_width = GetSystemMetrics(SM_CXVSCROLL);
int border_width = GetSystemMetrics(SM_CXBORDER);
int edge_width = GetSystemMetrics(SM_CXEDGE);
/* Resize the tab widget column to perfectly fit the tab window and
* leave sufficient space for the scroll widget.
*/
SendMessageW(info->tabs[TAB_INDEX].hwnd, LVM_SETCOLUMNWIDTH, 0,
width-scroll_width-2*border_width-2*edge_width);
break;
}
case TAB_SEARCH: {
int scroll_width = GetSystemMetrics(SM_CXVSCROLL);
int border_width = GetSystemMetrics(SM_CXBORDER);
int edge_width = GetSystemMetrics(SM_CXEDGE);
int top_pos = 0;
SetWindowPos(info->search.hwndEdit, NULL, 0, top_pos, width,
EDIT_HEIGHT, SWP_NOZORDER | SWP_NOACTIVATE);
top_pos += EDIT_HEIGHT + TAB_MARGIN;
SetWindowPos(info->search.hwndList, NULL, 0, top_pos, width,
height-top_pos, SWP_NOZORDER | SWP_NOACTIVATE);
/* Resize the tab widget column to perfectly fit the tab window and
* leave sufficient space for the scroll widget.
*/
SendMessageW(info->search.hwndList, LVM_SETCOLUMNWIDTH, 0,
width-scroll_width-2*border_width-2*edge_width);
break;
}
}
}
static LRESULT Child_OnSize(HWND hwnd)
@ -351,7 +392,8 @@ static LRESULT Child_OnSize(HWND hwnd)
rect.right - TAB_RIGHT_PADDING,
rect.bottom - TAB_TOP_PADDING, SWP_NOMOVE);
ResizeTabChild(info, info->tabs[TAB_CONTENTS].hwnd);
ResizeTabChild(info, TAB_CONTENTS);
ResizeTabChild(info, TAB_INDEX);
return 0;
}
@ -375,29 +417,101 @@ static LRESULT OnTabChange(HWND hwnd)
return 0;
}
static LRESULT OnTopicChange(HWND hwnd, ContentItem *item)
static LRESULT OnTopicChange(HHInfo *info, void *user_data)
{
HHInfo *info = (HHInfo*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
LPCWSTR chmfile = NULL;
ContentItem *iter = item;
LPCWSTR chmfile = NULL, name = NULL, local = NULL;
ContentItem *citer;
SearchItem *siter;
IndexItem *iiter;
if(!item || !info)
if(!user_data || !info)
return 0;
TRACE("name %s loal %s\n", debugstr_w(item->name), debugstr_w(item->local));
while(iter) {
if(iter->merge.chm_file) {
chmfile = iter->merge.chm_file;
break;
switch (info->current_tab)
{
case TAB_CONTENTS:
citer = (ContentItem *) user_data;
name = citer->name;
local = citer->local;
while(citer) {
if(citer->merge.chm_file) {
chmfile = citer->merge.chm_file;
break;
}
citer = citer->parent;
}
iter = iter->parent;
break;
case TAB_INDEX:
iiter = (IndexItem *) user_data;
if(iiter->nItems == 0) {
FIXME("No entries for this item!\n");
return 0;
}
if(iiter->nItems > 1) {
int i = 0;
LVITEMW lvi;
SendMessageW(info->popup.hwndList, LVM_DELETEALLITEMS, 0, 0);
for(i=0;i<iiter->nItems;i++) {
IndexSubItem *item = &iiter->items[i];
WCHAR *name = iiter->keyword;
if(item->name)
name = item->name;
memset(&lvi, 0, sizeof(lvi));
lvi.iItem = i;
lvi.mask = LVIF_TEXT|LVIF_PARAM;
lvi.cchTextMax = strlenW(name)+1;
lvi.pszText = name;
lvi.lParam = (LPARAM) item;
SendMessageW(info->popup.hwndList, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
}
ShowWindow(info->popup.hwndPopup, SW_SHOW);
return 0;
}
name = iiter->items[0].name;
local = iiter->items[0].local;
chmfile = iiter->merge.chm_file;
break;
case TAB_SEARCH:
siter = (SearchItem *) user_data;
name = siter->filename;
local = siter->filename;
chmfile = info->pCHMInfo->szFile;
break;
default:
FIXME("Unhandled operation for this tab!\n");
return 0;
}
NavigateToChm(info, chmfile, item->local);
if(!chmfile)
{
FIXME("No help file found for this item!\n");
return 0;
}
TRACE("name %s loal %s\n", debugstr_w(name), debugstr_w(local));
NavigateToChm(info, chmfile, local);
return 0;
}
/* Capture the Enter/Return key and send it up to Child_WndProc as an NM_RETURN message */
static LRESULT CALLBACK EditChild_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WNDPROC editWndProc = (WNDPROC)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
if(message == WM_KEYUP && wParam == VK_RETURN)
{
NMHDR nmhdr;
nmhdr.hwndFrom = hWnd;
nmhdr.code = NM_RETURN;
SendMessageW(GetParent(GetParent(hWnd)), WM_NOTIFY, wParam, (LPARAM)&nmhdr);
}
return editWndProc(hWnd, message, wParam, lParam);
}
static LRESULT CALLBACK Child_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
@ -407,12 +521,71 @@ static LRESULT CALLBACK Child_WndProc(HWND hWnd, UINT message, WPARAM wParam, LP
case WM_SIZE:
return Child_OnSize(hWnd);
case WM_NOTIFY: {
HHInfo *info = (HHInfo*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
NMHDR *nmhdr = (NMHDR*)lParam;
switch(nmhdr->code) {
case TCN_SELCHANGE:
return OnTabChange(hWnd);
case TVN_SELCHANGEDW:
return OnTopicChange(hWnd, (ContentItem*)((NMTREEVIEWW *)lParam)->itemNew.lParam);
return OnTopicChange(info, (void*)((NMTREEVIEWW *)lParam)->itemNew.lParam);
case NM_DBLCLK:
if(!info)
return 0;
switch(info->current_tab)
{
case TAB_INDEX:
return OnTopicChange(info, (void*)((NMITEMACTIVATE *)lParam)->lParam);
case TAB_SEARCH:
return OnTopicChange(info, (void*)((NMITEMACTIVATE *)lParam)->lParam);
}
break;
case NM_RETURN:
if(!info)
return 0;
switch(info->current_tab) {
case TAB_INDEX: {
HWND hwndList = info->tabs[TAB_INDEX].hwnd;
LVITEMW lvItem;
lvItem.iItem = (int) SendMessageW(hwndList, LVM_GETSELECTIONMARK, 0, 0);
lvItem.mask = TVIF_PARAM;
SendMessageW(hwndList, LVM_GETITEMW, 0, (LPARAM)&lvItem);
OnTopicChange(info, (void*) lvItem.lParam);
return 0;
}
case TAB_SEARCH: {
if(nmhdr->hwndFrom == info->search.hwndEdit) {
char needle[100];
DWORD i, len;
len = GetWindowTextA(info->search.hwndEdit, needle, sizeof(needle));
if(!len)
{
FIXME("Unable to get search text.\n");
return 0;
}
/* Convert the requested text for comparison later against the
* lower case version of HTML file contents.
*/
for(i=0;i<len;i++)
needle[i] = tolower(needle[i]);
InitSearch(info, needle);
return 0;
}else if(nmhdr->hwndFrom == info->search.hwndList) {
HWND hwndList = info->search.hwndList;
LVITEMW lvItem;
lvItem.iItem = (int) SendMessageW(hwndList, LVM_GETSELECTIONMARK, 0, 0);
lvItem.mask = TVIF_PARAM;
SendMessageW(hwndList, LVM_GETITEMW, 0, (LPARAM)&lvItem);
OnTopicChange(info, (void*) lvItem.lParam);
return 0;
}
break;
}
}
break;
}
break;
}
@ -729,6 +902,8 @@ static BOOL HH_AddHTMLPane(HHInfo *pHHInfo)
static BOOL AddContentTab(HHInfo *info)
{
if(info->tabs[TAB_CONTENTS].id == -1)
return TRUE; /* No "Contents" tab */
info->tabs[TAB_CONTENTS].hwnd = CreateWindowExW(WS_EX_CLIENTEDGE, WC_TREEVIEWW,
szEmpty, WS_CHILD | WS_BORDER | 0x25, 50, 50, 100, 100,
info->WinType.hwndNavigation, NULL, hhctrl_hinstance, NULL);
@ -737,12 +912,293 @@ static BOOL AddContentTab(HHInfo *info)
return FALSE;
}
ResizeTabChild(info, info->tabs[TAB_CONTENTS].hwnd);
ResizeTabChild(info, TAB_CONTENTS);
ShowWindow(info->tabs[TAB_CONTENTS].hwnd, SW_SHOW);
return TRUE;
}
static BOOL AddIndexTab(HHInfo *info)
{
char hidden_column[] = "Column";
LVCOLUMNA lvc;
if(info->tabs[TAB_INDEX].id == -1)
return TRUE; /* No "Index" tab */
info->tabs[TAB_INDEX].hwnd = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW,
szEmpty, WS_CHILD | WS_BORDER | LVS_SINGLESEL | LVS_REPORT | LVS_NOCOLUMNHEADER, 50, 50, 100, 100,
info->WinType.hwndNavigation, NULL, hhctrl_hinstance, NULL);
if(!info->tabs[TAB_INDEX].hwnd) {
ERR("Could not create ListView control\n");
return FALSE;
}
memset(&lvc, 0, sizeof(lvc));
lvc.mask = LVCF_TEXT;
lvc.pszText = hidden_column;
if(SendMessageW(info->tabs[TAB_INDEX].hwnd, LVM_INSERTCOLUMNA, 0, (LPARAM) &lvc) == -1)
{
ERR("Could not create ListView column\n");
return FALSE;
}
ResizeTabChild(info, TAB_INDEX);
ShowWindow(info->tabs[TAB_INDEX].hwnd, SW_HIDE);
return TRUE;
}
static BOOL AddSearchTab(HHInfo *info)
{
HWND hwndList, hwndEdit, hwndContainer;
char hidden_column[] = "Column";
WNDPROC editWndProc;
LVCOLUMNA lvc;
if(info->tabs[TAB_SEARCH].id == -1)
return TRUE; /* No "Search" tab */
hwndContainer = CreateWindowExW(WS_EX_CONTROLPARENT, szChildClass, szEmpty,
WS_CHILD, 0, 0, 0, 0, info->WinType.hwndNavigation,
NULL, hhctrl_hinstance, NULL);
if(!hwndContainer) {
ERR("Could not create search window container control.\n");
return FALSE;
}
hwndEdit = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, szEmpty, WS_CHILD
| WS_VISIBLE | ES_LEFT | SS_NOTIFY, 0, 0, 0, 0,
hwndContainer, NULL, hhctrl_hinstance, NULL);
if(!hwndEdit) {
ERR("Could not create search ListView control.\n");
return FALSE;
}
if(SendMessageW(hwndEdit, WM_SETFONT, (WPARAM) info->hFont, (LPARAM) FALSE) == -1)
{
ERR("Could not set font for edit control.\n");
return FALSE;
}
editWndProc = (WNDPROC) SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC, (LONG_PTR)EditChild_WndProc);
if(!editWndProc) {
ERR("Could not redirect messages for edit control.\n");
return FALSE;
}
SetWindowLongPtrW(hwndEdit, GWLP_USERDATA, (LONG_PTR)editWndProc);
hwndList = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW, szEmpty,
WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_SINGLESEL
| LVS_REPORT | LVS_NOCOLUMNHEADER, 0, 0, 0, 0,
hwndContainer, NULL, hhctrl_hinstance, NULL);
if(!hwndList) {
ERR("Could not create search ListView control.\n");
return FALSE;
}
memset(&lvc, 0, sizeof(lvc));
lvc.mask = LVCF_TEXT;
lvc.pszText = hidden_column;
if(SendMessageW(hwndList, LVM_INSERTCOLUMNA, 0, (LPARAM) &lvc) == -1)
{
ERR("Could not create ListView column\n");
return FALSE;
}
info->search.hwndEdit = hwndEdit;
info->search.hwndList = hwndList;
info->search.hwndContainer = hwndContainer;
info->tabs[TAB_SEARCH].hwnd = hwndContainer;
SetWindowLongPtrW(hwndContainer, GWLP_USERDATA, (LONG_PTR)info);
ResizeTabChild(info, TAB_SEARCH);
return TRUE;
}
/* The Index tab's sub-topic popup */
static void ResizePopupChild(HHInfo *info)
{
int scroll_width = GetSystemMetrics(SM_CXVSCROLL);
int border_width = GetSystemMetrics(SM_CXBORDER);
int edge_width = GetSystemMetrics(SM_CXEDGE);
INT width, height;
RECT rect;
if(!info)
return;
GetClientRect(info->popup.hwndPopup, &rect);
SetWindowPos(info->popup.hwndCallback, HWND_TOP, 0, 0,
rect.right, rect.bottom, SWP_NOMOVE);
rect.left = TAB_MARGIN;
rect.top = TAB_TOP_PADDING + TAB_MARGIN;
rect.right -= TAB_RIGHT_PADDING + TAB_MARGIN;
rect.bottom -= TAB_MARGIN;
width = rect.right-rect.left;
height = rect.bottom-rect.top;
SetWindowPos(info->popup.hwndList, NULL, rect.left, rect.top, width, height,
SWP_NOZORDER | SWP_NOACTIVATE);
SendMessageW(info->popup.hwndList, LVM_SETCOLUMNWIDTH, 0,
width-scroll_width-2*border_width-2*edge_width);
}
static LRESULT CALLBACK HelpPopup_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HHInfo *info = (HHInfo *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
switch (message)
{
case WM_SIZE:
ResizePopupChild(info);
return 0;
case WM_DESTROY:
DestroyWindow(hWnd);
return 0;
case WM_CLOSE:
ShowWindow(hWnd, SW_HIDE);
return 0;
default:
return DefWindowProcW(hWnd, message, wParam, lParam);
}
return 0;
}
static LRESULT CALLBACK PopupChild_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_NOTIFY: {
NMHDR *nmhdr = (NMHDR*)lParam;
switch(nmhdr->code)
{
case NM_DBLCLK: {
HHInfo *info = (HHInfo*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
IndexSubItem *iter;
if(info == 0 || lParam == 0)
return 0;
iter = (IndexSubItem*) ((NMITEMACTIVATE *)lParam)->lParam;
if(iter == 0)
return 0;
NavigateToChm(info, info->index->merge.chm_file, iter->local);
ShowWindow(info->popup.hwndPopup, SW_HIDE);
return 0;
}
case NM_RETURN: {
HHInfo *info = (HHInfo*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
IndexSubItem *iter;
LVITEMW lvItem;
if(info == 0)
return 0;
lvItem.iItem = (int) SendMessageW(info->popup.hwndList, LVM_GETSELECTIONMARK, 0, 0);
lvItem.mask = TVIF_PARAM;
SendMessageW(info->popup.hwndList, LVM_GETITEMW, 0, (LPARAM)&lvItem);
iter = (IndexSubItem*) lvItem.lParam;
NavigateToChm(info, info->index->merge.chm_file, iter->local);
ShowWindow(info->popup.hwndPopup, SW_HIDE);
return 0;
}
}
break;
}
default:
return DefWindowProcW(hWnd, message, wParam, lParam);
}
return 0;
}
static BOOL AddIndexPopup(HHInfo *info)
{
static const WCHAR szPopupChildClass[] = {'H','H',' ','P','o','p','u','p',' ','C','h','i','l','d',0};
static const WCHAR windowCaptionW[] = {'S','e','l','e','c','t',' ','T','o','p','i','c',':',0};
static const WCHAR windowClassW[] = {'H','H',' ','P','o','p','u','p',0};
HWND hwndList, hwndPopup, hwndCallback;
char hidden_column[] = "Column";
WNDCLASSEXW wcex;
LVCOLUMNA lvc;
if(info->tabs[TAB_INDEX].id == -1)
return TRUE; /* No "Index" tab */
wcex.cbSize = sizeof(WNDCLASSEXW);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = HelpPopup_WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hhctrl_hinstance;
wcex.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
wcex.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_MENU + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = windowClassW;
wcex.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
RegisterClassExW(&wcex);
wcex.cbSize = sizeof(WNDCLASSEXW);
wcex.style = 0;
wcex.lpfnWndProc = PopupChild_WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hhctrl_hinstance;
wcex.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
wcex.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szPopupChildClass;
wcex.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
RegisterClassExW(&wcex);
hwndPopup = CreateWindowExW(WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_APPWINDOW
| WS_EX_WINDOWEDGE | WS_EX_RIGHTSCROLLBAR,
windowClassW, windowCaptionW, WS_POPUPWINDOW
| WS_OVERLAPPEDWINDOW | WS_VISIBLE
| WS_CLIPSIBLINGS | WS_CLIPCHILDREN, CW_USEDEFAULT,
CW_USEDEFAULT, 300, 200, info->WinType.hwndHelp,
NULL, hhctrl_hinstance, NULL);
if (!hwndPopup)
return FALSE;
hwndCallback = CreateWindowExW(WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
szPopupChildClass, szEmpty, WS_CHILDWINDOW | WS_VISIBLE,
0, 0, 0, 0,
hwndPopup, NULL, hhctrl_hinstance, NULL);
if (!hwndCallback)
return FALSE;
ShowWindow(hwndPopup, SW_HIDE);
hwndList = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW, szEmpty,
WS_CHILD | WS_BORDER | LVS_SINGLESEL | LVS_REPORT
| LVS_NOCOLUMNHEADER, 50, 50, 100, 100,
hwndCallback, NULL, hhctrl_hinstance, NULL);
if(!hwndList) {
ERR("Could not create popup ListView control\n");
return FALSE;
}
memset(&lvc, 0, sizeof(lvc));
lvc.mask = LVCF_TEXT;
lvc.pszText = hidden_column;
if(SendMessageW(hwndList, LVM_INSERTCOLUMNA, 0, (LPARAM) &lvc) == -1)
{
ERR("Could not create popup ListView column\n");
return FALSE;
}
info->popup.hwndCallback = hwndCallback;
info->popup.hwndPopup = hwndPopup;
info->popup.hwndList = hwndList;
SetWindowLongPtrW(hwndPopup, GWLP_USERDATA, (LONG_PTR)info);
SetWindowLongPtrW(hwndCallback, GWLP_USERDATA, (LONG_PTR)info);
ResizePopupChild(info);
ShowWindow(hwndList, SW_SHOW);
return TRUE;
}
/* Viewer Window */
static LRESULT Help_OnSize(HWND hWnd)
@ -918,7 +1374,17 @@ static BOOL CreateViewer(HHInfo *pHHInfo)
if (!AddContentTab(pHHInfo))
return FALSE;
if (!AddIndexTab(pHHInfo))
return FALSE;
if (!AddIndexPopup(pHHInfo))
return FALSE;
if (!AddSearchTab(pHHInfo))
return FALSE;
InitContent(pHHInfo);
InitIndex(pHHInfo);
return TRUE;
}
@ -947,6 +1413,8 @@ void ReleaseHelpViewer(HHInfo *info)
ReleaseWebBrowser(info);
ReleaseContent(info);
ReleaseIndex(info);
ReleaseSearch(info);
if(info->WinType.hwndHelp)
DestroyWindow(info->WinType.hwndHelp);
@ -958,6 +1426,13 @@ void ReleaseHelpViewer(HHInfo *info)
HHInfo *CreateHelpViewer(LPCWSTR filename)
{
HHInfo *info = heap_alloc_zero(sizeof(HHInfo));
int i;
/* Set the invalid tab ID (-1) as the default value for all
* of the tabs, this matches a failed TCM_INSERTITEM call.
*/
for(i=0;i<sizeof(info->tabs)/sizeof(HHTab);i++)
info->tabs[i].id = -1;
OleInitialize(NULL);

View file

@ -272,12 +272,41 @@ HWND WINAPI HtmlHelpA(HWND caller, LPCSTR filename, UINT command, DWORD_PTR data
int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine)
{
MSG msg;
int len, buflen;
int len, buflen, mapid = -1;
WCHAR *filename;
char *endq = NULL;
hh_process = TRUE;
/* Parse command line option of the HTML Help command.
*
* Note: The only currently handled action is "mapid",
* which corresponds to opening a specific page.
*/
while(*szCmdLine == '-')
{
LPSTR space, ptr;
ptr = szCmdLine + 1;
space = strchr(ptr, ' ');
if(!strncmp(ptr, "mapid", space-ptr))
{
char idtxt[10];
ptr += strlen("mapid")+1;
space = strchr(ptr, ' ');
memcpy(idtxt, ptr, space-ptr);
idtxt[space-ptr] = '\0';
mapid = atoi(idtxt);
szCmdLine = space+1;
}
else
{
FIXME("Unhandled HTML Help command line parameter! (%.*s)\n", space-szCmdLine, szCmdLine);
return 0;
}
}
/* FIXME: Check szCmdLine for bad arguments */
if (*szCmdLine == '\"')
endq = strchr(++szCmdLine, '\"');
@ -291,7 +320,11 @@ int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine)
MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, filename, buflen);
filename[buflen-1] = 0;
HtmlHelpW(GetDesktopWindow(), filename, HH_DISPLAY_TOPIC, 0);
/* Open a specific help topic */
if(mapid != -1)
HtmlHelpW(GetDesktopWindow(), filename, HH_HELP_CONTEXT, mapid);
else
HtmlHelpW(GetDesktopWindow(), filename, HH_DISPLAY_TOPIC, 0);
heap_free(filename);

View file

@ -65,11 +65,37 @@ typedef struct ContentItem {
ChmPath merge;
} ContentItem;
typedef struct IndexSubItem {
LPWSTR name;
LPWSTR local;
} IndexSubItem;
typedef struct IndexItem {
struct IndexItem *next;
HTREEITEM id;
LPWSTR keyword;
ChmPath merge;
int nItems;
int itemFlags;
int indentLevel;
IndexSubItem *items;
} IndexItem;
typedef struct SearchItem {
struct SearchItem *next;
HTREEITEM id;
LPWSTR title;
LPWSTR filename;
} SearchItem;
typedef struct CHMInfo
{
IITStorage *pITStorage;
IStorage *pStorage;
LPCWSTR szFile;
WCHAR *szFile;
IStream *strings_stream;
char **strings;
@ -90,6 +116,19 @@ typedef struct {
DWORD id;
} HHTab;
typedef struct {
HWND hwndList;
HWND hwndPopup;
HWND hwndCallback;
} IndexPopup;
typedef struct {
SearchItem *root;
HWND hwndEdit;
HWND hwndList;
HWND hwndContainer;
} SearchTab;
typedef struct {
IOleClientSite *client_site;
IWebBrowser2 *web_browser;
@ -111,6 +150,9 @@ typedef struct {
CHMInfo *pCHMInfo;
ContentItem *content;
IndexItem *index;
IndexPopup popup;
SearchTab search;
HWND hwndTabCtrl;
HWND hwndSizeBar;
HFONT hFont;
@ -127,6 +169,9 @@ void DoPageAction(HHInfo*,DWORD);
void InitContent(HHInfo*);
void ReleaseContent(HHInfo*);
void InitIndex(HHInfo*);
void ReleaseIndex(HHInfo*);
CHMInfo *OpenCHM(LPCWSTR szFile);
BOOL LoadWinTypeFromCHM(HHInfo *info);
CHMInfo *CloseCHM(CHMInfo *pCHMInfo);
@ -139,6 +184,9 @@ void ReleaseHelpViewer(HHInfo*);
BOOL NavigateToUrl(HHInfo*,LPCWSTR);
BOOL NavigateToChm(HHInfo*,LPCWSTR,LPCWSTR);
void InitSearch(HHInfo *info, const char *needle);
void ReleaseSearch(HHInfo *info);
/* memory allocation functions */
static inline void * __WINE_ALLOC_SIZE(1) heap_alloc(size_t len)

View file

@ -31,20 +31,25 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
#include "Cs.rc"
#include "Da.rc"
#include "De.rc"
#include "El.rc"
#include "En.rc"
#include "Fr.rc"
#include "Fi.rc"
#include "Hu.rc"
#include "Ko.rc"
#include "Lt.rc"
#include "Nl.rc"
#include "No.rc"
#include "Pl.rc"
#include "Pt.rc"
#include "Ru.rc"
#include "Si.rc"
#include "Sv.rc"
#include "Tr.rc"
/* UTF-8 */
#include "De.rc"
#include "Es.rc"
#include "Fr.rc"
#include "Lt.rc"
#include "Ru.rc"
#include "Si.rc"
#include "Uk.rc"
#include "Zh.rc"

View file

@ -0,0 +1,295 @@
/*
* Copyright 2007 Jacek Caban for CodeWeavers
* Copyright 2010 Erich Hoover
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "hhctrl.h"
#include "stream.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
/* Fill the TreeView object corresponding to the Index items */
static void fill_index_tree(HWND hwnd, IndexItem *item)
{
int index = 0;
LVITEMW lvi;
while(item) {
TRACE("tree debug: %s\n", debugstr_w(item->keyword));
if(!item->keyword)
{
FIXME("HTML Help index item has no keyword.\n");
item = item->next;
continue;
}
memset(&lvi, 0, sizeof(lvi));
lvi.iItem = index++;
lvi.mask = LVIF_TEXT|LVIF_PARAM|LVIF_INDENT;
lvi.iIndent = item->indentLevel;
lvi.cchTextMax = strlenW(item->keyword)+1;
lvi.pszText = item->keyword;
lvi.lParam = (LPARAM)item;
item->id = (HTREEITEM)SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
item = item->next;
}
}
/* Parse the attributes correspond to a list item, including sub-topics.
*
* Each list item has, at minimum, a param of type "keyword" and two
* parameters corresponding to a "sub-topic." For each sub-topic there
* must be a "name" param and a "local" param, if there is only one
* sub-topic then there isn't really a sub-topic, the index will jump
* directly to the requested item.
*/
static void parse_index_obj_node_param(IndexItem *item, const char *text)
{
const char *ptr;
LPWSTR *param;
int len, wlen;
ptr = get_attr(text, "name", &len);
if(!ptr) {
WARN("name attr not found\n");
return;
}
/* Allocate a new sub-item, either on the first run or whenever a
* sub-topic has filled out both the "name" and "local" params.
*/
if(item->itemFlags == 0x11 && (!strncasecmp("name", ptr, len) || !strncasecmp("local", ptr, len))) {
item->nItems++;
item->items = heap_realloc(item->items, sizeof(IndexSubItem)*item->nItems);
item->items[item->nItems-1].name = NULL;
item->items[item->nItems-1].local = NULL;
item->itemFlags = 0x00;
}
if(!strncasecmp("keyword", ptr, len)) {
param = &item->keyword;
}else if(!item->keyword && !strncasecmp("name", ptr, len)) {
/* Some HTML Help index files use an additional "name" parameter
* rather than the "keyword" parameter. In this case, the first
* occurance of the "name" parameter is the keyword.
*/
param = &item->keyword;
}else if(!strncasecmp("name", ptr, len)) {
item->itemFlags |= 0x01;
param = &item->items[item->nItems-1].name;
}else if(!strncasecmp("local", ptr, len)) {
item->itemFlags |= 0x10;
param = &item->items[item->nItems-1].local;
}else {
WARN("unhandled param %s\n", debugstr_an(ptr, len));
return;
}
ptr = get_attr(text, "value", &len);
if(!ptr) {
WARN("value attr not found\n");
return;
}
wlen = MultiByteToWideChar(CP_ACP, 0, ptr, len, NULL, 0);
*param = heap_alloc((wlen+1)*sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, ptr, len, *param, wlen);
(*param)[wlen] = 0;
}
/* Parse the object tag corresponding to a list item.
*
* At this step we look for all of the "param" child tags, using this information
* to build up the information about the list item. When we reach the </object>
* tag we know that we've finished parsing this list item.
*/
static IndexItem *parse_index_sitemap_object(HHInfo *info, stream_t *stream)
{
strbuf_t node, node_name;
IndexItem *item;
strbuf_init(&node);
strbuf_init(&node_name);
item = heap_alloc_zero(sizeof(IndexItem));
item->nItems = 0;
item->items = heap_alloc_zero(0);
item->itemFlags = 0x11;
while(next_node(stream, &node)) {
get_node_name(&node, &node_name);
TRACE("%s\n", node.buf);
if(!strcasecmp(node_name.buf, "param")) {
parse_index_obj_node_param(item, node.buf);
}else if(!strcasecmp(node_name.buf, "/object")) {
break;
}else {
WARN("Unhandled tag! %s\n", node_name.buf);
}
strbuf_zero(&node);
}
strbuf_free(&node);
strbuf_free(&node_name);
return item;
}
/* Parse the HTML list item node corresponding to a specific help entry.
*
* At this stage we look for the only child tag we expect to find under
* the list item: the <OBJECT> tag. We also only expect to find object
* tags with the "type" attribute set to "text/sitemap".
*/
static IndexItem *parse_li(HHInfo *info, stream_t *stream)
{
strbuf_t node, node_name;
IndexItem *ret = NULL;
strbuf_init(&node);
strbuf_init(&node_name);
while(next_node(stream, &node)) {
get_node_name(&node, &node_name);
TRACE("%s\n", node.buf);
if(!strcasecmp(node_name.buf, "object")) {
const char *ptr;
int len;
static const char sz_text_sitemap[] = "text/sitemap";
ptr = get_attr(node.buf, "type", &len);
if(ptr && len == sizeof(sz_text_sitemap)-1
&& !memcmp(ptr, sz_text_sitemap, len)) {
ret = parse_index_sitemap_object(info, stream);
break;
}
}else {
WARN("Unhandled tag! %s\n", node_name.buf);
}
strbuf_zero(&node);
}
strbuf_free(&node);
strbuf_free(&node_name);
return ret;
}
/* Parse the HTML Help page corresponding to all of the Index items.
*
* At this high-level stage we locate out each HTML list item tag.
* Since there is no end-tag for the <LI> item, we must hope that
* the <LI> entry is parsed correctly or tags might get lost.
*
* Within each entry it is also possible to encounter an additional
* <UL> tag. When this occurs the tag indicates that the topics
* contained within it are related to the parent <LI> topic and
* should be inset by an indent.
*/
static void parse_hhindex(HHInfo *info, IStream *str, IndexItem *item)
{
stream_t stream;
strbuf_t node, node_name;
int indent_level = -1;
strbuf_init(&node);
strbuf_init(&node_name);
stream_init(&stream, str);
while(next_node(&stream, &node)) {
get_node_name(&node, &node_name);
TRACE("%s\n", node.buf);
if(!strcasecmp(node_name.buf, "li")) {
item->next = parse_li(info, &stream);
item->next->merge = item->merge;
item = item->next;
item->indentLevel = indent_level;
}else if(!strcasecmp(node_name.buf, "ul")) {
indent_level++;
}else if(!strcasecmp(node_name.buf, "/ul")) {
indent_level--;
}else {
WARN("Unhandled tag! %s\n", node_name.buf);
}
strbuf_zero(&node);
}
strbuf_free(&node);
strbuf_free(&node_name);
}
/* Initialize the HTML Help Index tab */
void InitIndex(HHInfo *info)
{
IStream *stream;
info->index = heap_alloc_zero(sizeof(IndexItem));
info->index->nItems = 0;
SetChmPath(&info->index->merge, info->pCHMInfo->szFile, info->WinType.pszIndex);
stream = GetChmStream(info->pCHMInfo, info->pCHMInfo->szFile, &info->index->merge);
if(!stream) {
TRACE("Could not get index stream\n");
return;
}
parse_hhindex(info, stream, info->index);
IStream_Release(stream);
fill_index_tree(info->tabs[TAB_INDEX].hwnd, info->index->next);
}
/* Free all of the Index items, including all of the "sub-items" that
* correspond to different sub-topics.
*/
void ReleaseIndex(HHInfo *info)
{
IndexItem *item = info->index, *next;
int i;
/* Note: item->merge is identical for all items, only free once */
heap_free(item->merge.chm_file);
heap_free(item->merge.chm_index);
while(item) {
next = item->next;
heap_free(item->keyword);
for(i=0;i<item->nItems;i++) {
heap_free(item->items[i].name);
heap_free(item->items[i].local);
}
heap_free(item->items);
item = next;
}
}

View file

@ -0,0 +1,246 @@
/*
* Copyright 2010 Erich Hoover
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "hhctrl.h"
#include "stream.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
const WCHAR *folder, const char *needle);
/* Allocate a ListView entry for a search result. */
static SearchItem *alloc_search_item(WCHAR *title, const WCHAR *filename)
{
int filename_len = filename ? (strlenW(filename)+1)*sizeof(WCHAR) : 0;
SearchItem *item;
item = heap_alloc_zero(sizeof(SearchItem));
if(filename)
{
item->filename = heap_alloc(filename_len);
memcpy(item->filename, filename, filename_len);
}
item->title = title; /* Already allocated */
return item;
}
/* Fill the ListView object corresponding to the found Search tab items */
static void fill_search_tree(HWND hwndList, SearchItem *item)
{
int index = 0;
LVITEMW lvi;
SendMessageW(hwndList, LVM_DELETEALLITEMS, 0, 0);
while(item) {
TRACE("list debug: %s\n", debugstr_w(item->filename));
memset(&lvi, 0, sizeof(lvi));
lvi.iItem = index++;
lvi.mask = LVIF_TEXT|LVIF_PARAM;
lvi.cchTextMax = strlenW(item->title)+1;
lvi.pszText = item->title;
lvi.lParam = (LPARAM)item;
item->id = (HTREEITEM)SendMessageW(hwndList, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
item = item->next;
}
}
/* Search the CHM storage stream (an HTML file) for the requested text.
*
* Before searching the HTML file all HTML tags are removed so that only
* the content of the document is scanned. If the search string is found
* then the title of the document is returned.
*/
static WCHAR *SearchCHM_File(IStorage *pStorage, const WCHAR *file, const char *needle)
{
char *buffer = heap_alloc(BLOCK_SIZE);
strbuf_t content, node, node_name;
IStream *temp_stream = NULL;
DWORD i, buffer_size = 0;
WCHAR *title = NULL;
BOOL found = FALSE;
stream_t stream;
HRESULT hres;
hres = IStorage_OpenStream(pStorage, file, NULL, STGM_READ, 0, &temp_stream);
if(FAILED(hres)) {
FIXME("Could not open '%s' stream: %08x\n", debugstr_w(file), hres);
goto cleanup;
}
strbuf_init(&node);
strbuf_init(&content);
strbuf_init(&node_name);
stream_init(&stream, temp_stream);
/* Remove all HTML formatting and record the title */
while(next_node(&stream, &node)) {
get_node_name(&node, &node_name);
if(next_content(&stream, &content) && content.len > 1)
{
char *text = &content.buf[1];
int textlen = content.len-1;
if(!strcasecmp(node_name.buf, "title"))
{
int wlen = MultiByteToWideChar(CP_ACP, 0, text, textlen, NULL, 0);
title = heap_alloc((wlen+1)*sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, text, textlen, title, wlen);
title[wlen] = 0;
}
buffer = heap_realloc(buffer, buffer_size + textlen + 1);
memcpy(&buffer[buffer_size], text, textlen);
buffer[buffer_size + textlen] = '\0';
buffer_size += textlen;
}
strbuf_zero(&node);
strbuf_zero(&content);
}
/* Convert the buffer to lower case for comparison against the
* requested text (already in lower case).
*/
for(i=0;i<buffer_size;i++)
buffer[i] = tolower(buffer[i]);
/* Search the decoded buffer for the requested text */
if(strstr(buffer, needle))
found = TRUE;
strbuf_free(&node);
strbuf_free(&content);
strbuf_free(&node_name);
cleanup:
heap_free(buffer);
if(temp_stream)
IStream_Release(temp_stream);
if(!found)
{
heap_free(title);
return NULL;
}
return title;
}
/* Search all children of a CHM storage object for the requested text and
* return the last found search item.
*/
static SearchItem *SearchCHM_Storage(SearchItem *item, IStorage *pStorage,
const char *needle)
{
const WCHAR szHTMext[] = {'.','h','t','m',0};
IEnumSTATSTG *elem = NULL;
WCHAR *filename = NULL;
STATSTG entries;
HRESULT hres;
ULONG retr;
hres = IStorage_EnumElements(pStorage, 0, NULL, 0, &elem);
if(hres != S_OK)
{
FIXME("Could not enumerate '/' storage elements: %08x\n", hres);
return NULL;
}
while (IEnumSTATSTG_Next(elem, 1, &entries, &retr) == NOERROR)
{
switch(entries.type) {
case STGTY_STORAGE:
item = SearchCHM_Folder(item, pStorage, entries.pwcsName, needle);
break;
case STGTY_STREAM:
filename = entries.pwcsName;
while(strchrW(filename, '/'))
filename = strchrW(filename, '/')+1;
if(strstrW(filename, szHTMext))
{
WCHAR *title = SearchCHM_File(pStorage, filename, needle);
if(title)
{
item->next = alloc_search_item(title, entries.pwcsName);
item = item->next;
}
}
break;
default:
FIXME("Unhandled IStorage stream element.\n");
}
}
return item;
}
/* Open a CHM storage object (folder) by name and find all items with
* the requested text. The last found item is returned.
*/
static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
const WCHAR *folder, const char *needle)
{
IStorage *temp_storage = NULL;
HRESULT hres;
hres = IStorage_OpenStorage(pStorage, folder, NULL, STGM_READ, NULL, 0, &temp_storage);
if(FAILED(hres))
{
FIXME("Could not open '%s' storage object: %08x\n", debugstr_w(folder), hres);
return NULL;
}
item = SearchCHM_Storage(item, temp_storage, needle);
IStorage_Release(temp_storage);
return item;
}
/* Search the entire CHM file for the requested text and add all of
* the found items to a ListView for the user to choose the item
* they want.
*/
void InitSearch(HHInfo *info, const char *needle)
{
CHMInfo *chm = info->pCHMInfo;
SearchItem *root_item = alloc_search_item(NULL, NULL);
SearchCHM_Storage(root_item, chm->pStorage, needle);
fill_search_tree(info->search.hwndList, root_item->next);
if(info->search.root)
ReleaseSearch(info);
info->search.root = root_item;
}
/* Free all of the found Search items. */
void ReleaseSearch(HHInfo *info)
{
SearchItem *item = info->search.root;
info->search.root = NULL;
while(item) {
heap_free(item->filename);
item = item->next;
}
}

View file

@ -0,0 +1,179 @@
/*
* Copyright 2007 Jacek Caban for CodeWeavers
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "hhctrl.h"
#include "stream.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
void strbuf_init(strbuf_t *buf)
{
buf->size = 8;
buf->len = 0;
buf->buf = heap_alloc(buf->size);
}
void strbuf_zero(strbuf_t *buf)
{
buf->len = 0;
}
void strbuf_free(strbuf_t *buf)
{
heap_free(buf->buf);
}
void strbuf_append(strbuf_t *buf, const char *data, int len)
{
if(buf->len+len > buf->size) {
buf->size = buf->len+len;
buf->buf = heap_realloc(buf->buf, buf->size);
}
memcpy(buf->buf+buf->len, data, len);
buf->len += len;
}
void stream_init(stream_t *stream, IStream *str)
{
memset(stream, 0, sizeof(stream_t));
stream->str = str;
}
BOOL stream_chr(stream_t *stream, strbuf_t *buf, char c)
{
BOOL b = TRUE;
ULONG i;
while(b) {
for(i=stream->p; i<stream->size; i++) {
if(stream->buf[i] == c) {
b = FALSE;
break;
}
}
if(buf && i > stream->p)
strbuf_append(buf, stream->buf+stream->p, i-stream->p);
stream->p = i;
if(stream->p == stream->size) {
stream->p = 0;
IStream_Read(stream->str, stream->buf, sizeof(stream->buf), &stream->size);
if(!stream->size)
break;
}
}
return stream->size != 0;
}
void get_node_name(strbuf_t *node, strbuf_t *name)
{
const char *ptr = node->buf+1;
strbuf_zero(name);
while(*ptr != '>' && !isspace(*ptr))
ptr++;
strbuf_append(name, node->buf+1, ptr-node->buf-1);
strbuf_append(name, "", 1);
}
/* Return the stream content up to the next HTML tag.
*
* Note: the first returned character is the end of the last tag (>).
*/
BOOL next_content(stream_t *stream, strbuf_t *buf)
{
if(!stream_chr(stream, buf, '<'))
return FALSE;
return TRUE;
}
BOOL next_node(stream_t *stream, strbuf_t *buf)
{
if(!stream_chr(stream, NULL, '<'))
return FALSE;
if(!stream_chr(stream, buf, '>'))
return FALSE;
strbuf_append(buf, ">", 2);
return TRUE;
}
/*
* Find the value of a named HTML attribute.
*
* Note: Attribute names are case insensitive, so it is necessary to
* put both the node text and the attribute name in the same case
* before attempting a string search.
*/
const char *get_attr(const char *node, const char *name, int *len)
{
const char *ptr, *ptr2;
int name_len, node_len;
char name_buf[32];
char *node_buf;
int i;
/* Create a lower case copy of the node */
node_len = strlen(node)+1;
node_buf = heap_alloc(node_len*sizeof(char));
if(!node_buf)
return NULL;
memcpy(node_buf, node, node_len);
for(i=0;i<node_len;i++)
node_buf[i] = tolower(node_buf[i]);
/* Create a lower case copy of the attribute name (search string) */
name_len = strlen(name);
memcpy(name_buf, name, name_len);
for(i=0;i<name_len;i++)
name_buf[i] = tolower(name_buf[i]);
name_buf[name_len++] = '=';
name_buf[name_len++] = '\"';
name_buf[name_len] = 0;
ptr = strstr(node_buf, name_buf);
if(!ptr) {
WARN("name not found\n");
heap_free(node_buf);
return NULL;
}
ptr += name_len;
ptr2 = strchr(ptr, '\"');
if(!ptr2)
{
heap_free(node_buf);
return NULL;
}
*len = ptr2-ptr;
/* Return the pointer offset within the original string */
ptr = node+(ptr-node_buf);
heap_free(node_buf);
return ptr;
}

View file

@ -0,0 +1,48 @@
/*
* Copyright 2007 Jacek Caban for CodeWeavers
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef HHCTRL_STREAM_H
#define HHCTRL_STREAM_H
#define BLOCK_SIZE 0x1000
typedef struct {
char *buf;
int size;
int len;
} strbuf_t;
typedef struct {
IStream *str;
char buf[BLOCK_SIZE];
ULONG size;
ULONG p;
} stream_t;
void strbuf_init(strbuf_t *buf);
void strbuf_zero(strbuf_t *buf);
void strbuf_free(strbuf_t *buf);
void strbuf_append(strbuf_t *buf, const char *data, int len);
void stream_init(stream_t *stream, IStream *str);
BOOL stream_chr(stream_t *stream, strbuf_t *buf, char c);
void get_node_name(strbuf_t *node, strbuf_t *name);
BOOL next_content(stream_t *stream, strbuf_t *buf);
BOOL next_node(stream_t *stream, strbuf_t *buf);
const char *get_attr(const char *node, const char *name, int *len);
#endif