1c2c66affSColin Finck /*
2c2c66affSColin Finck * Copyright 2007 Jacek Caban for CodeWeavers
3c2c66affSColin Finck * Copyright 2010 Erich Hoover
4c2c66affSColin Finck *
5c2c66affSColin Finck * This library is free software; you can redistribute it and/or
6c2c66affSColin Finck * modify it under the terms of the GNU Lesser General Public
7c2c66affSColin Finck * License as published by the Free Software Foundation; either
8c2c66affSColin Finck * version 2.1 of the License, or (at your option) any later version.
9c2c66affSColin Finck *
10c2c66affSColin Finck * This library is distributed in the hope that it will be useful,
11c2c66affSColin Finck * but WITHOUT ANY WARRANTY; without even the implied warranty of
12c2c66affSColin Finck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13c2c66affSColin Finck * Lesser General Public License for more details.
14c2c66affSColin Finck *
15c2c66affSColin Finck * You should have received a copy of the GNU Lesser General Public
16c2c66affSColin Finck * License along with this library; if not, write to the Free Software
17c2c66affSColin Finck * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18c2c66affSColin Finck */
19c2c66affSColin Finck
20c2c66affSColin Finck #include "hhctrl.h"
2196f371dcSAmine Khaldi #include "stream.h"
2296f371dcSAmine Khaldi
2396f371dcSAmine Khaldi #include "wine/debug.h"
2496f371dcSAmine Khaldi
2596f371dcSAmine Khaldi WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
26c2c66affSColin Finck
27c2c66affSColin Finck /* Fill the TreeView object corresponding to the Index items */
fill_index_tree(HWND hwnd,IndexItem * item)28c2c66affSColin Finck static void fill_index_tree(HWND hwnd, IndexItem *item)
29c2c66affSColin Finck {
30c2c66affSColin Finck int index = 0;
31c2c66affSColin Finck LVITEMW lvi;
32c2c66affSColin Finck
33c2c66affSColin Finck while(item) {
34c2c66affSColin Finck TRACE("tree debug: %s\n", debugstr_w(item->keyword));
35c2c66affSColin Finck
36c2c66affSColin Finck if(!item->keyword)
37c2c66affSColin Finck {
38c2c66affSColin Finck FIXME("HTML Help index item has no keyword.\n");
39c2c66affSColin Finck item = item->next;
40c2c66affSColin Finck continue;
41c2c66affSColin Finck }
42c2c66affSColin Finck memset(&lvi, 0, sizeof(lvi));
43c2c66affSColin Finck lvi.iItem = index++;
44c2c66affSColin Finck lvi.mask = LVIF_TEXT|LVIF_PARAM|LVIF_INDENT;
45c2c66affSColin Finck lvi.iIndent = item->indentLevel;
46*6cbb6445SAmine Khaldi lvi.cchTextMax = lstrlenW(item->keyword)+1;
47c2c66affSColin Finck lvi.pszText = item->keyword;
48c2c66affSColin Finck lvi.lParam = (LPARAM)item;
49c2c66affSColin Finck item->id = (HTREEITEM)SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
50c2c66affSColin Finck item = item->next;
51c2c66affSColin Finck }
52c2c66affSColin Finck }
53c2c66affSColin Finck
item_realloc(IndexItem * item,int num_items)54c2c66affSColin Finck static void item_realloc(IndexItem *item, int num_items)
55c2c66affSColin Finck {
56c2c66affSColin Finck item->nItems = num_items;
57c2c66affSColin Finck item->items = heap_realloc(item->items, sizeof(IndexSubItem)*item->nItems);
58c2c66affSColin Finck item->items[item->nItems-1].name = NULL;
59c2c66affSColin Finck item->items[item->nItems-1].local = NULL;
60c2c66affSColin Finck item->itemFlags = 0x00;
61c2c66affSColin Finck }
62c2c66affSColin Finck
63c2c66affSColin Finck /* Parse the attributes correspond to a list item, including sub-topics.
64c2c66affSColin Finck *
65c2c66affSColin Finck * Each list item has, at minimum, a param of type "keyword" and two
66c2c66affSColin Finck * parameters corresponding to a "sub-topic." For each sub-topic there
67c2c66affSColin Finck * must be a "name" param and a "local" param, if there is only one
68c2c66affSColin Finck * sub-topic then there isn't really a sub-topic, the index will jump
69c2c66affSColin Finck * directly to the requested item.
70c2c66affSColin Finck */
parse_index_obj_node_param(IndexItem * item,const char * text,UINT code_page)71c2c66affSColin Finck static void parse_index_obj_node_param(IndexItem *item, const char *text, UINT code_page)
72c2c66affSColin Finck {
73c2c66affSColin Finck const char *ptr;
74c2c66affSColin Finck LPWSTR *param;
75c2c66affSColin Finck int len;
76c2c66affSColin Finck
77c2c66affSColin Finck ptr = get_attr(text, "name", &len);
78c2c66affSColin Finck if(!ptr) {
79c2c66affSColin Finck WARN("name attr not found\n");
80c2c66affSColin Finck return;
81c2c66affSColin Finck }
82c2c66affSColin Finck
83c2c66affSColin Finck /* Allocate a new sub-item, either on the first run or whenever a
84c2c66affSColin Finck * sub-topic has filled out both the "name" and "local" params.
85c2c66affSColin Finck */
86*6cbb6445SAmine Khaldi if(item->itemFlags == 0x11 && (!_strnicmp("name", ptr, len) || !_strnicmp("local", ptr, len)))
87c2c66affSColin Finck item_realloc(item, item->nItems+1);
88*6cbb6445SAmine Khaldi if(!_strnicmp("keyword", ptr, len)) {
89c2c66affSColin Finck param = &item->keyword;
90*6cbb6445SAmine Khaldi }else if(!item->keyword && !_strnicmp("name", ptr, len)) {
91c2c66affSColin Finck /* Some HTML Help index files use an additional "name" parameter
92c2c66affSColin Finck * rather than the "keyword" parameter. In this case, the first
93c2c66affSColin Finck * occurrence of the "name" parameter is the keyword.
94c2c66affSColin Finck */
95c2c66affSColin Finck param = &item->keyword;
96*6cbb6445SAmine Khaldi }else if(!_strnicmp("name", ptr, len)) {
97c2c66affSColin Finck item->itemFlags |= 0x01;
98c2c66affSColin Finck param = &item->items[item->nItems-1].name;
99*6cbb6445SAmine Khaldi }else if(!_strnicmp("local", ptr, len)) {
100c2c66affSColin Finck item->itemFlags |= 0x10;
101c2c66affSColin Finck param = &item->items[item->nItems-1].local;
102c2c66affSColin Finck }else {
103c2c66affSColin Finck WARN("unhandled param %s\n", debugstr_an(ptr, len));
104c2c66affSColin Finck return;
105c2c66affSColin Finck }
106c2c66affSColin Finck
107c2c66affSColin Finck ptr = get_attr(text, "value", &len);
108c2c66affSColin Finck if(!ptr) {
109c2c66affSColin Finck WARN("value attr not found\n");
110c2c66affSColin Finck return;
111c2c66affSColin Finck }
112c2c66affSColin Finck
113c2c66affSColin Finck *param = decode_html(ptr, len, code_page);
114c2c66affSColin Finck }
115c2c66affSColin Finck
116c2c66affSColin Finck /* Parse the object tag corresponding to a list item.
117c2c66affSColin Finck *
118c2c66affSColin Finck * At this step we look for all of the "param" child tags, using this information
119c2c66affSColin Finck * to build up the information about the list item. When we reach the </object>
120c2c66affSColin Finck * tag we know that we've finished parsing this list item.
121c2c66affSColin Finck */
parse_index_sitemap_object(HHInfo * info,stream_t * stream)122c2c66affSColin Finck static IndexItem *parse_index_sitemap_object(HHInfo *info, stream_t *stream)
123c2c66affSColin Finck {
124c2c66affSColin Finck strbuf_t node, node_name;
125c2c66affSColin Finck IndexItem *item;
126c2c66affSColin Finck
127c2c66affSColin Finck strbuf_init(&node);
128c2c66affSColin Finck strbuf_init(&node_name);
129c2c66affSColin Finck
130c2c66affSColin Finck item = heap_alloc_zero(sizeof(IndexItem));
131c2c66affSColin Finck item->nItems = 0;
132c2c66affSColin Finck item->items = heap_alloc_zero(0);
133c2c66affSColin Finck item->itemFlags = 0x11;
134c2c66affSColin Finck
135c2c66affSColin Finck while(next_node(stream, &node)) {
136c2c66affSColin Finck get_node_name(&node, &node_name);
137c2c66affSColin Finck
138c2c66affSColin Finck TRACE("%s\n", node.buf);
139c2c66affSColin Finck
140*6cbb6445SAmine Khaldi if(!_strnicmp(node_name.buf, "param", -1)) {
141c2c66affSColin Finck parse_index_obj_node_param(item, node.buf, info->pCHMInfo->codePage);
142*6cbb6445SAmine Khaldi }else if(!_strnicmp(node_name.buf, "/object", -1)) {
143c2c66affSColin Finck break;
144c2c66affSColin Finck }else {
145c2c66affSColin Finck WARN("Unhandled tag! %s\n", node_name.buf);
146c2c66affSColin Finck }
147c2c66affSColin Finck
148c2c66affSColin Finck strbuf_zero(&node);
149c2c66affSColin Finck }
150c2c66affSColin Finck
151c2c66affSColin Finck strbuf_free(&node);
152c2c66affSColin Finck strbuf_free(&node_name);
153c2c66affSColin Finck
154c2c66affSColin Finck return item;
155c2c66affSColin Finck }
156c2c66affSColin Finck
157c2c66affSColin Finck /* Parse the HTML list item node corresponding to a specific help entry.
158c2c66affSColin Finck *
159c2c66affSColin Finck * At this stage we look for the only child tag we expect to find under
160c2c66affSColin Finck * the list item: the <OBJECT> tag. We also only expect to find object
161c2c66affSColin Finck * tags with the "type" attribute set to "text/sitemap".
162c2c66affSColin Finck */
parse_li(HHInfo * info,stream_t * stream)163c2c66affSColin Finck static IndexItem *parse_li(HHInfo *info, stream_t *stream)
164c2c66affSColin Finck {
165c2c66affSColin Finck strbuf_t node, node_name;
166c2c66affSColin Finck IndexItem *ret = NULL;
167c2c66affSColin Finck
168c2c66affSColin Finck strbuf_init(&node);
169c2c66affSColin Finck strbuf_init(&node_name);
170c2c66affSColin Finck
171c2c66affSColin Finck while(next_node(stream, &node)) {
172c2c66affSColin Finck get_node_name(&node, &node_name);
173c2c66affSColin Finck
174c2c66affSColin Finck TRACE("%s\n", node.buf);
175c2c66affSColin Finck
176*6cbb6445SAmine Khaldi if(!_strnicmp(node_name.buf, "object", -1)) {
177c2c66affSColin Finck const char *ptr;
178c2c66affSColin Finck int len;
179c2c66affSColin Finck
180c2c66affSColin Finck static const char sz_text_sitemap[] = "text/sitemap";
181c2c66affSColin Finck
182c2c66affSColin Finck ptr = get_attr(node.buf, "type", &len);
183c2c66affSColin Finck
184c2c66affSColin Finck if(ptr && len == sizeof(sz_text_sitemap)-1
185c2c66affSColin Finck && !memcmp(ptr, sz_text_sitemap, len)) {
186c2c66affSColin Finck ret = parse_index_sitemap_object(info, stream);
187c2c66affSColin Finck break;
188c2c66affSColin Finck }
189c2c66affSColin Finck }else {
190c2c66affSColin Finck WARN("Unhandled tag! %s\n", node_name.buf);
191c2c66affSColin Finck }
192c2c66affSColin Finck
193c2c66affSColin Finck strbuf_zero(&node);
194c2c66affSColin Finck }
195c2c66affSColin Finck if(!ret)
196c2c66affSColin Finck FIXME("Failed to parse <li> tag!\n");
197c2c66affSColin Finck
198c2c66affSColin Finck strbuf_free(&node);
199c2c66affSColin Finck strbuf_free(&node_name);
200c2c66affSColin Finck
201c2c66affSColin Finck return ret;
202c2c66affSColin Finck }
203c2c66affSColin Finck
204c2c66affSColin Finck /* Parse the HTML Help page corresponding to all of the Index items.
205c2c66affSColin Finck *
206c2c66affSColin Finck * At this high-level stage we locate out each HTML list item tag.
207c2c66affSColin Finck * Since there is no end-tag for the <LI> item, we must hope that
208c2c66affSColin Finck * the <LI> entry is parsed correctly or tags might get lost.
209c2c66affSColin Finck *
210c2c66affSColin Finck * Within each entry it is also possible to encounter an additional
211c2c66affSColin Finck * <UL> tag. When this occurs the tag indicates that the topics
212c2c66affSColin Finck * contained within it are related to the parent <LI> topic and
213c2c66affSColin Finck * should be inset by an indent.
214c2c66affSColin Finck */
parse_hhindex(HHInfo * info,IStream * str,IndexItem * item)215c2c66affSColin Finck static void parse_hhindex(HHInfo *info, IStream *str, IndexItem *item)
216c2c66affSColin Finck {
217c2c66affSColin Finck stream_t stream;
218c2c66affSColin Finck strbuf_t node, node_name;
219c2c66affSColin Finck int indent_level = -1;
220c2c66affSColin Finck
221c2c66affSColin Finck strbuf_init(&node);
222c2c66affSColin Finck strbuf_init(&node_name);
223c2c66affSColin Finck
224c2c66affSColin Finck stream_init(&stream, str);
225c2c66affSColin Finck
226c2c66affSColin Finck while(next_node(&stream, &node)) {
227c2c66affSColin Finck get_node_name(&node, &node_name);
228c2c66affSColin Finck
229c2c66affSColin Finck TRACE("%s\n", node.buf);
230c2c66affSColin Finck
231*6cbb6445SAmine Khaldi if(!_strnicmp(node_name.buf, "li", -1)) {
232c2c66affSColin Finck IndexItem *new_item;
233c2c66affSColin Finck
234c2c66affSColin Finck new_item = parse_li(info, &stream);
235*6cbb6445SAmine Khaldi if(new_item && item->keyword && lstrcmpW(new_item->keyword, item->keyword) == 0) {
236c2c66affSColin Finck int num_items = item->nItems;
237c2c66affSColin Finck
238c2c66affSColin Finck item_realloc(item, num_items+1);
239c2c66affSColin Finck memcpy(&item->items[num_items], &new_item->items[0], sizeof(IndexSubItem));
240c2c66affSColin Finck heap_free(new_item->keyword);
241c2c66affSColin Finck heap_free(new_item->items);
242c2c66affSColin Finck heap_free(new_item);
243c2c66affSColin Finck } else if(new_item) {
244c2c66affSColin Finck item->next = new_item;
245c2c66affSColin Finck item->next->merge = item->merge;
246c2c66affSColin Finck item = item->next;
247c2c66affSColin Finck item->indentLevel = indent_level;
248c2c66affSColin Finck }
249*6cbb6445SAmine Khaldi }else if(!_strnicmp(node_name.buf, "ul", -1)) {
250c2c66affSColin Finck indent_level++;
251*6cbb6445SAmine Khaldi }else if(!_strnicmp(node_name.buf, "/ul", -1)) {
252c2c66affSColin Finck indent_level--;
253c2c66affSColin Finck }else {
254c2c66affSColin Finck WARN("Unhandled tag! %s\n", node_name.buf);
255c2c66affSColin Finck }
256c2c66affSColin Finck
257c2c66affSColin Finck strbuf_zero(&node);
258c2c66affSColin Finck }
259c2c66affSColin Finck
260c2c66affSColin Finck strbuf_free(&node);
261c2c66affSColin Finck strbuf_free(&node_name);
262c2c66affSColin Finck }
263c2c66affSColin Finck
264c2c66affSColin Finck /* Initialize the HTML Help Index tab */
InitIndex(HHInfo * info)265c2c66affSColin Finck void InitIndex(HHInfo *info)
266c2c66affSColin Finck {
267c2c66affSColin Finck IStream *stream;
268c2c66affSColin Finck
269c2c66affSColin Finck info->index = heap_alloc_zero(sizeof(IndexItem));
270c2c66affSColin Finck info->index->nItems = 0;
271c2c66affSColin Finck SetChmPath(&info->index->merge, info->pCHMInfo->szFile, info->WinType.pszIndex);
272c2c66affSColin Finck
273c2c66affSColin Finck stream = GetChmStream(info->pCHMInfo, info->pCHMInfo->szFile, &info->index->merge);
274c2c66affSColin Finck if(!stream) {
275c2c66affSColin Finck TRACE("Could not get index stream\n");
276c2c66affSColin Finck return;
277c2c66affSColin Finck }
278c2c66affSColin Finck
279c2c66affSColin Finck parse_hhindex(info, stream, info->index);
280c2c66affSColin Finck IStream_Release(stream);
281c2c66affSColin Finck
282c2c66affSColin Finck fill_index_tree(info->tabs[TAB_INDEX].hwnd, info->index->next);
283c2c66affSColin Finck }
284c2c66affSColin Finck
285c2c66affSColin Finck /* Free all of the Index items, including all of the "sub-items" that
286c2c66affSColin Finck * correspond to different sub-topics.
287c2c66affSColin Finck */
ReleaseIndex(HHInfo * info)288c2c66affSColin Finck void ReleaseIndex(HHInfo *info)
289c2c66affSColin Finck {
290c2c66affSColin Finck IndexItem *item = info->index, *next;
291c2c66affSColin Finck int i;
292c2c66affSColin Finck
293c2c66affSColin Finck if(!item) return;
294c2c66affSColin Finck /* Note: item->merge is identical for all items, only free once */
295c2c66affSColin Finck heap_free(item->merge.chm_file);
296c2c66affSColin Finck heap_free(item->merge.chm_index);
297c2c66affSColin Finck while(item) {
298c2c66affSColin Finck next = item->next;
299c2c66affSColin Finck
300c2c66affSColin Finck heap_free(item->keyword);
301c2c66affSColin Finck for(i=0;i<item->nItems;i++) {
302c2c66affSColin Finck heap_free(item->items[i].name);
303c2c66affSColin Finck heap_free(item->items[i].local);
304c2c66affSColin Finck }
305c2c66affSColin Finck heap_free(item->items);
306c2c66affSColin Finck
307c2c66affSColin Finck item = next;
308c2c66affSColin Finck }
309c2c66affSColin Finck }
310