windowscodecs: Define a custom png error handler.
[wine] / dlls / hhctrl.ocx / index.c
1 /*
2  * Copyright 2007 Jacek Caban for CodeWeavers
3  * Copyright 2010 Erich Hoover
4  *
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.
9  *
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.
14  *
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
18  */
19
20 #define NONAMELESSUNION
21 #define NONAMELESSSTRUCT
22
23 #include "hhctrl.h"
24 #include "stream.h"
25
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
29
30 /* Fill the TreeView object corresponding to the Index items */
31 static void fill_index_tree(HWND hwnd, IndexItem *item)
32 {
33     int index = 0;
34     LVITEMW lvi;
35
36     while(item) {
37         TRACE("tree debug: %s\n", debugstr_w(item->keyword));
38
39         memset(&lvi, 0, sizeof(lvi));
40         lvi.iItem = index++;
41         lvi.mask = LVIF_TEXT|LVIF_PARAM;
42         lvi.cchTextMax = strlenW(item->keyword)+1;
43         lvi.pszText = item->keyword;
44         lvi.lParam = (LPARAM)item;
45         item->id = (HTREEITEM)SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
46         item = item->next;
47     }
48 }
49
50 /* Parse the attributes correspond to a list item, including sub-topics.
51  *
52  * Each list item has, at minimum, a param of type "keyword" and two
53  * parameters corresponding to a "sub-topic."  For each sub-topic there
54  * must be a "name" param and a "local" param, if there is only one
55  * sub-topic then there isn't really a sub-topic, the index will jump
56  * directly to the requested item.
57  */
58 static void parse_index_obj_node_param(IndexItem *item, const char *text)
59 {
60     const char *ptr;
61     LPWSTR *param;
62     int len, wlen;
63
64     ptr = get_attr(text, "name", &len);
65     if(!ptr) {
66         WARN("name attr not found\n");
67         return;
68     }
69
70     /* Allocate a new sub-item, either on the first run or whenever a
71      * sub-topic has filled out both the "name" and "local" params.
72      */
73     if(item->itemFlags == 0x11 && (!strncasecmp("name", ptr, len) || !strncasecmp("local", ptr, len))) {
74         item->nItems++;
75         item->items = heap_realloc(item->items, sizeof(IndexSubItem)*item->nItems);
76         item->items[item->nItems-1].name = NULL;
77         item->items[item->nItems-1].local = NULL;
78         item->itemFlags = 0x00;
79     }
80     if(!strncasecmp("keyword", ptr, len)) {
81         param = &item->keyword;
82     }else if(!strncasecmp("name", ptr, len)) {
83         item->itemFlags |= 0x01;
84         param = &item->items[item->nItems-1].name;
85     }else if(!strncasecmp("local", ptr, len)) {
86         item->itemFlags |= 0x10;
87         param = &item->items[item->nItems-1].local;
88     }else {
89         WARN("unhandled param %s\n", debugstr_an(ptr, len));
90         return;
91     }
92
93     ptr = get_attr(text, "value", &len);
94     if(!ptr) {
95         WARN("value attr not found\n");
96         return;
97     }
98
99     wlen = MultiByteToWideChar(CP_ACP, 0, ptr, len, NULL, 0);
100     *param = heap_alloc((wlen+1)*sizeof(WCHAR));
101     MultiByteToWideChar(CP_ACP, 0, ptr, len, *param, wlen);
102     (*param)[wlen] = 0;
103 }
104
105 /* Parse the object tag corresponding to a list item.
106  *
107  * At this step we look for all of the "param" child tags, using this information
108  * to build up the information about the list item.  When we reach the </object>
109  * tag we know that we've finished parsing this list item.
110  */
111 static IndexItem *parse_index_sitemap_object(HHInfo *info, stream_t *stream)
112 {
113     strbuf_t node, node_name;
114     IndexItem *item;
115
116     strbuf_init(&node);
117     strbuf_init(&node_name);
118
119     item = heap_alloc_zero(sizeof(IndexItem));
120     item->nItems = 0;
121     item->items = heap_alloc_zero(0);
122     item->itemFlags = 0x11;
123
124     while(next_node(stream, &node)) {
125         get_node_name(&node, &node_name);
126
127         TRACE("%s\n", node.buf);
128
129         if(!strcasecmp(node_name.buf, "param")) {
130             parse_index_obj_node_param(item, node.buf);
131         }else if(!strcasecmp(node_name.buf, "/object")) {
132             break;
133         }else {
134             WARN("Unhandled tag! %s\n", node_name.buf);
135         }
136
137         strbuf_zero(&node);
138     }
139
140     strbuf_free(&node);
141     strbuf_free(&node_name);
142
143     return item;
144 }
145
146 /* Parse the HTML list item node corresponding to a specific help entry.
147  *
148  * At this stage we look for the only child tag we expect to find under
149  * the list item: the <OBJECT> tag.  We also only expect to find object
150  * tags with the "type" attribute set to "text/sitemap".
151  */
152 static IndexItem *parse_li(HHInfo *info, stream_t *stream)
153 {
154     strbuf_t node, node_name;
155     IndexItem *ret = NULL;
156
157     strbuf_init(&node);
158     strbuf_init(&node_name);
159
160     while(next_node(stream, &node)) {
161         get_node_name(&node, &node_name);
162
163         TRACE("%s\n", node.buf);
164
165         if(!strcasecmp(node_name.buf, "object")) {
166             const char *ptr;
167             int len;
168
169             static const char sz_text_sitemap[] = "text/sitemap";
170
171             ptr = get_attr(node.buf, "type", &len);
172
173             if(ptr && len == sizeof(sz_text_sitemap)-1
174                && !memcmp(ptr, sz_text_sitemap, len)) {
175                 ret = parse_index_sitemap_object(info, stream);
176                 break;
177             }
178         }else {
179             WARN("Unhandled tag! %s\n", node_name.buf);
180         }
181
182         strbuf_zero(&node);
183     }
184
185     strbuf_free(&node);
186     strbuf_free(&node_name);
187
188     return ret;
189 }
190
191 /* Parse the HTML Help page corresponding to all of the Index items.
192  *
193  * At this high-level stage we locate out each HTML list item tag.
194  * Since there is no end-tag for the <LI> item, we must hope that
195  * the <LI> entry is parsed correctly or tags might get lost.
196  */
197 static void parse_hhindex(HHInfo *info, IStream *str, IndexItem *item)
198 {
199     stream_t stream;
200     strbuf_t node, node_name;
201
202     strbuf_init(&node);
203     strbuf_init(&node_name);
204
205     stream_init(&stream, str);
206
207     while(next_node(&stream, &node)) {
208         get_node_name(&node, &node_name);
209
210         TRACE("%s\n", node.buf);
211
212         if(!strcasecmp(node_name.buf, "li")) {
213             item->next = parse_li(info, &stream);
214             item->next->merge = item->merge;
215             item = item->next;
216         }else {
217             WARN("Unhandled tag! %s\n", node_name.buf);
218         }
219
220         strbuf_zero(&node);
221     }
222
223     strbuf_free(&node);
224     strbuf_free(&node_name);
225 }
226
227 /* Initialize the HTML Help Index tab */
228 void InitIndex(HHInfo *info)
229 {
230     IStream *stream;
231
232     info->index = heap_alloc_zero(sizeof(IndexItem));
233     info->index->nItems = 0;
234     SetChmPath(&info->index->merge, info->pCHMInfo->szFile, info->WinType.pszIndex);
235
236     stream = GetChmStream(info->pCHMInfo, info->pCHMInfo->szFile, &info->index->merge);
237     if(!stream) {
238         TRACE("Could not get index stream\n");
239         return;
240     }
241
242     parse_hhindex(info, stream, info->index);
243     IStream_Release(stream);
244
245     fill_index_tree(info->tabs[TAB_INDEX].hwnd, info->index->next);
246 }
247
248 /* Free all of the Index items, including all of the "sub-items" that
249  * correspond to different sub-topics.
250  */
251 void ReleaseIndex(HHInfo *info)
252 {
253     IndexItem *item = info->index, *next;
254     int i;
255
256     /* Note: item->merge is identical for all items, only free once */
257     heap_free(item->merge.chm_file);
258     heap_free(item->merge.chm_index);
259     while(item) {
260         next = item->next;
261
262         heap_free(item->keyword);
263         for(i=0;i<item->nItems;i++) {
264             heap_free(item->items[i].name);
265             heap_free(item->items[i].local);
266         }
267         heap_free(item->items);
268
269         item = next;
270     }
271 }