mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-01 06:06:13 +00:00
hhctrl.ocx: Make the Index tab functional.
This commit is contained in:
parent
5912b0c92f
commit
50f67cbab1
4 changed files with 337 additions and 13 deletions
|
@ -10,6 +10,7 @@ C_SRCS = \
|
|||
content.c \
|
||||
help.c \
|
||||
hhctrl.c \
|
||||
index.c \
|
||||
regsvr.c \
|
||||
stream.c \
|
||||
webbrowser.c
|
||||
|
|
|
@ -397,26 +397,53 @@ static LRESULT OnTabChange(HWND hwnd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static LRESULT OnTopicChange(HWND hwnd, ContentItem *item)
|
||||
static LRESULT OnTopicChange(HWND hwnd, 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;
|
||||
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;
|
||||
chmfile = citer->merge.chm_file;
|
||||
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) {
|
||||
FIXME("Support for sub-topics not implemented.\n");
|
||||
return 0;
|
||||
}
|
||||
name = iiter->items[0].name;
|
||||
local = iiter->items[0].local;
|
||||
chmfile = iiter->merge.chm_file;
|
||||
break;
|
||||
default:
|
||||
FIXME("Unhandled operation for this tab!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
NavigateToChm(info, chmfile, item->local);
|
||||
TRACE("name %s loal %s\n", debugstr_w(name), debugstr_w(local));
|
||||
|
||||
NavigateToChm(info, chmfile, local);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -434,7 +461,9 @@ static LRESULT CALLBACK Child_WndProc(HWND hWnd, UINT message, WPARAM wParam, LP
|
|||
case TCN_SELCHANGE:
|
||||
return OnTabChange(hWnd);
|
||||
case TVN_SELCHANGEDW:
|
||||
return OnTopicChange(hWnd, (ContentItem*)((NMTREEVIEWW *)lParam)->itemNew.lParam);
|
||||
return OnTopicChange(hWnd, (void*)((NMTREEVIEWW *)lParam)->itemNew.lParam);
|
||||
case NM_DBLCLK:
|
||||
return OnTopicChange(hWnd, (void*)((NMITEMACTIVATE *)lParam)->lParam);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -975,6 +1004,7 @@ static BOOL CreateViewer(HHInfo *pHHInfo)
|
|||
return FALSE;
|
||||
|
||||
InitContent(pHHInfo);
|
||||
InitIndex(pHHInfo);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -1003,6 +1033,7 @@ void ReleaseHelpViewer(HHInfo *info)
|
|||
|
||||
ReleaseWebBrowser(info);
|
||||
ReleaseContent(info);
|
||||
ReleaseIndex(info);
|
||||
|
||||
if(info->WinType.hwndHelp)
|
||||
DestroyWindow(info->WinType.hwndHelp);
|
||||
|
|
|
@ -65,6 +65,23 @@ 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;
|
||||
IndexSubItem *items;
|
||||
} IndexItem;
|
||||
|
||||
typedef struct CHMInfo
|
||||
{
|
||||
IITStorage *pITStorage;
|
||||
|
@ -111,6 +128,7 @@ typedef struct {
|
|||
|
||||
CHMInfo *pCHMInfo;
|
||||
ContentItem *content;
|
||||
IndexItem *index;
|
||||
HWND hwndTabCtrl;
|
||||
HWND hwndSizeBar;
|
||||
HFONT hFont;
|
||||
|
@ -127,6 +145,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);
|
||||
|
|
271
dlls/hhctrl.ocx/index.c
Normal file
271
dlls/hhctrl.ocx/index.c
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* 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));
|
||||
|
||||
memset(&lvi, 0, sizeof(lvi));
|
||||
lvi.iItem = index++;
|
||||
lvi.mask = LVIF_TEXT|LVIF_PARAM;
|
||||
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(!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.
|
||||
*/
|
||||
static void parse_hhindex(HHInfo *info, IStream *str, IndexItem *item)
|
||||
{
|
||||
stream_t stream;
|
||||
strbuf_t node, node_name;
|
||||
|
||||
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;
|
||||
}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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue