2 * Copyright 2007 Jacek Caban for CodeWeavers
3 * Copyright 2010 Erich Hoover
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "wine/debug.h"
25 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp
);
27 /* Fill the TreeView object corresponding to the Index items */
28 static void fill_index_tree(HWND hwnd
, IndexItem
*item
)
34 TRACE("tree debug: %s\n", debugstr_w(item
->keyword
));
38 FIXME("HTML Help index item has no keyword.\n");
42 memset(&lvi
, 0, sizeof(lvi
));
44 lvi
.mask
= LVIF_TEXT
|LVIF_PARAM
|LVIF_INDENT
;
45 lvi
.iIndent
= item
->indentLevel
;
46 lvi
.cchTextMax
= strlenW(item
->keyword
)+1;
47 lvi
.pszText
= item
->keyword
;
48 lvi
.lParam
= (LPARAM
)item
;
49 item
->id
= (HTREEITEM
)SendMessageW(hwnd
, LVM_INSERTITEMW
, 0, (LPARAM
)&lvi
);
54 static void item_realloc(IndexItem
*item
, int num_items
)
56 item
->nItems
= num_items
;
57 item
->items
= heap_realloc(item
->items
, sizeof(IndexSubItem
)*item
->nItems
);
58 item
->items
[item
->nItems
-1].name
= NULL
;
59 item
->items
[item
->nItems
-1].local
= NULL
;
60 item
->itemFlags
= 0x00;
63 /* Parse the attributes correspond to a list item, including sub-topics.
65 * Each list item has, at minimum, a param of type "keyword" and two
66 * parameters corresponding to a "sub-topic." For each sub-topic there
67 * must be a "name" param and a "local" param, if there is only one
68 * sub-topic then there isn't really a sub-topic, the index will jump
69 * directly to the requested item.
71 static void parse_index_obj_node_param(IndexItem
*item
, const char *text
, UINT code_page
)
77 ptr
= get_attr(text
, "name", &len
);
79 WARN("name attr not found\n");
83 /* Allocate a new sub-item, either on the first run or whenever a
84 * sub-topic has filled out both the "name" and "local" params.
86 if(item
->itemFlags
== 0x11 && (!strncasecmp("name", ptr
, len
) || !strncasecmp("local", ptr
, len
)))
87 item_realloc(item
, item
->nItems
+1);
88 if(!strncasecmp("keyword", ptr
, len
)) {
89 param
= &item
->keyword
;
90 }else if(!item
->keyword
&& !strncasecmp("name", ptr
, len
)) {
91 /* Some HTML Help index files use an additional "name" parameter
92 * rather than the "keyword" parameter. In this case, the first
93 * occurrence of the "name" parameter is the keyword.
95 param
= &item
->keyword
;
96 }else if(!strncasecmp("name", ptr
, len
)) {
97 item
->itemFlags
|= 0x01;
98 param
= &item
->items
[item
->nItems
-1].name
;
99 }else if(!strncasecmp("local", ptr
, len
)) {
100 item
->itemFlags
|= 0x10;
101 param
= &item
->items
[item
->nItems
-1].local
;
103 WARN("unhandled param %s\n", debugstr_an(ptr
, len
));
107 ptr
= get_attr(text
, "value", &len
);
109 WARN("value attr not found\n");
113 *param
= decode_html(ptr
, len
, code_page
);
116 /* Parse the object tag corresponding to a list item.
118 * At this step we look for all of the "param" child tags, using this information
119 * to build up the information about the list item. When we reach the </object>
120 * tag we know that we've finished parsing this list item.
122 static IndexItem
*parse_index_sitemap_object(HHInfo
*info
, stream_t
*stream
)
124 strbuf_t node
, node_name
;
128 strbuf_init(&node_name
);
130 item
= heap_alloc_zero(sizeof(IndexItem
));
132 item
->items
= heap_alloc_zero(0);
133 item
->itemFlags
= 0x11;
135 while(next_node(stream
, &node
)) {
136 get_node_name(&node
, &node_name
);
138 TRACE("%s\n", node
.buf
);
140 if(!strcasecmp(node_name
.buf
, "param")) {
141 parse_index_obj_node_param(item
, node
.buf
, info
->pCHMInfo
->codePage
);
142 }else if(!strcasecmp(node_name
.buf
, "/object")) {
145 WARN("Unhandled tag! %s\n", node_name
.buf
);
152 strbuf_free(&node_name
);
157 /* Parse the HTML list item node corresponding to a specific help entry.
159 * At this stage we look for the only child tag we expect to find under
160 * the list item: the <OBJECT> tag. We also only expect to find object
161 * tags with the "type" attribute set to "text/sitemap".
163 static IndexItem
*parse_li(HHInfo
*info
, stream_t
*stream
)
165 strbuf_t node
, node_name
;
166 IndexItem
*ret
= NULL
;
169 strbuf_init(&node_name
);
171 while(next_node(stream
, &node
)) {
172 get_node_name(&node
, &node_name
);
174 TRACE("%s\n", node
.buf
);
176 if(!strcasecmp(node_name
.buf
, "object")) {
180 static const char sz_text_sitemap
[] = "text/sitemap";
182 ptr
= get_attr(node
.buf
, "type", &len
);
184 if(ptr
&& len
== sizeof(sz_text_sitemap
)-1
185 && !memcmp(ptr
, sz_text_sitemap
, len
)) {
186 ret
= parse_index_sitemap_object(info
, stream
);
190 WARN("Unhandled tag! %s\n", node_name
.buf
);
196 FIXME("Failed to parse <li> tag!\n");
199 strbuf_free(&node_name
);
204 /* Parse the HTML Help page corresponding to all of the Index items.
206 * At this high-level stage we locate out each HTML list item tag.
207 * Since there is no end-tag for the <LI> item, we must hope that
208 * the <LI> entry is parsed correctly or tags might get lost.
210 * Within each entry it is also possible to encounter an additional
211 * <UL> tag. When this occurs the tag indicates that the topics
212 * contained within it are related to the parent <LI> topic and
213 * should be inset by an indent.
215 static void parse_hhindex(HHInfo
*info
, IStream
*str
, IndexItem
*item
)
218 strbuf_t node
, node_name
;
219 int indent_level
= -1;
222 strbuf_init(&node_name
);
224 stream_init(&stream
, str
);
226 while(next_node(&stream
, &node
)) {
227 get_node_name(&node
, &node_name
);
229 TRACE("%s\n", node
.buf
);
231 if(!strcasecmp(node_name
.buf
, "li")) {
234 new_item
= parse_li(info
, &stream
);
235 if(new_item
&& item
->keyword
&& strcmpW(new_item
->keyword
, item
->keyword
) == 0) {
236 int num_items
= item
->nItems
;
238 item_realloc(item
, num_items
+1);
239 memcpy(&item
->items
[num_items
], &new_item
->items
[0], sizeof(IndexSubItem
));
240 heap_free(new_item
->keyword
);
241 heap_free(new_item
->items
);
243 } else if(new_item
) {
244 item
->next
= new_item
;
245 item
->next
->merge
= item
->merge
;
247 item
->indentLevel
= indent_level
;
249 }else if(!strcasecmp(node_name
.buf
, "ul")) {
251 }else if(!strcasecmp(node_name
.buf
, "/ul")) {
254 WARN("Unhandled tag! %s\n", node_name
.buf
);
261 strbuf_free(&node_name
);
264 /* Initialize the HTML Help Index tab */
265 void InitIndex(HHInfo
*info
)
269 info
->index
= heap_alloc_zero(sizeof(IndexItem
));
270 info
->index
->nItems
= 0;
271 SetChmPath(&info
->index
->merge
, info
->pCHMInfo
->szFile
, info
->WinType
.pszIndex
);
273 stream
= GetChmStream(info
->pCHMInfo
, info
->pCHMInfo
->szFile
, &info
->index
->merge
);
275 TRACE("Could not get index stream\n");
279 parse_hhindex(info
, stream
, info
->index
);
280 IStream_Release(stream
);
282 fill_index_tree(info
->tabs
[TAB_INDEX
].hwnd
, info
->index
->next
);
285 /* Free all of the Index items, including all of the "sub-items" that
286 * correspond to different sub-topics.
288 void ReleaseIndex(HHInfo
*info
)
290 IndexItem
*item
= info
->index
, *next
;
294 /* Note: item->merge is identical for all items, only free once */
295 heap_free(item
->merge
.chm_file
);
296 heap_free(item
->merge
.chm_index
);
300 heap_free(item
->keyword
);
301 for(i
=0;i
<item
->nItems
;i
++) {
302 heap_free(item
->items
[i
].name
);
303 heap_free(item
->items
[i
].local
);
305 heap_free(item
->items
);