xref: /reactos/dll/win32/hhctrl.ocx/index.c (revision 6cbb6445)
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