2 * Copyright 2007 Jacek Caban for CodeWeavers
3 * Copyright 2011 Owen Rudge for CodeWeavers
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
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp
);
33 static void free_content_item(ContentItem
*item
)
40 free_content_item(item
->child
);
44 free(item
->merge
.chm_file
);
45 free(item
->merge
.chm_index
);
51 static void parse_obj_node_param(ContentItem
*item
, ContentItem
*hhc_root
, const char *text
, UINT code_page
)
57 ptr
= get_attr(text
, "name", &len
);
59 WARN("name attr not found\n");
63 if(!_strnicmp("name", ptr
, len
)) {
65 }else if(!_strnicmp("merge", ptr
, len
)) {
67 }else if(!_strnicmp("local", ptr
, len
)) {
70 WARN("unhandled param %s\n", debugstr_an(ptr
, len
));
74 ptr
= get_attr(text
, "value", &len
);
76 WARN("value attr not found\n");
81 * "merge" parameter data (referencing another CHM file) can be incorporated into the "local" parameter
82 * by specifying the filename in the format:
83 * MS-ITS:file.chm::/local_path.htm
85 if(param
== &item
->local
&& strstr(ptr
, "::"))
87 const char *local
= strstr(ptr
, "::")+2;
88 int local_len
= len
-(local
-ptr
);
90 item
->local
= decode_html(local
, local_len
, code_page
);
94 *param
= decode_html(ptr
, len
, code_page
);
97 SetChmPath(&item
->merge
, hhc_root
->merge
.chm_file
, merge
);
102 static ContentItem
*parse_hhc(HHInfo
*,IStream
*,ContentItem
*,insert_type_t
*);
104 static ContentItem
*insert_item(ContentItem
*item
, ContentItem
*new_item
, insert_type_t insert_type
)
112 switch(insert_type
) {
114 item
->next
= new_item
;
118 ContentItem
*iter
= item
->child
;
121 iter
->next
= new_item
;
123 item
->child
= new_item
;
131 static ContentItem
*parse_sitemap_object(HHInfo
*info
, stream_t
*stream
, ContentItem
*hhc_root
,
132 insert_type_t
*insert_type
)
134 strbuf_t node
, node_name
;
137 *insert_type
= INSERT_NEXT
;
140 strbuf_init(&node_name
);
142 item
= calloc(1, sizeof(ContentItem
));
144 while(next_node(stream
, &node
)) {
145 get_node_name(&node
, &node_name
);
147 TRACE("%s\n", node
.buf
);
149 if(!stricmp(node_name
.buf
, "/object"))
151 if(!stricmp(node_name
.buf
, "param"))
152 parse_obj_node_param(item
, hhc_root
, node
.buf
, info
->pCHMInfo
->codePage
);
158 strbuf_free(&node_name
);
160 if(item
->merge
.chm_index
) {
161 IStream
*merge_stream
;
163 merge_stream
= GetChmStream(info
->pCHMInfo
, item
->merge
.chm_file
, &item
->merge
);
165 item
->child
= parse_hhc(info
, merge_stream
, hhc_root
, insert_type
);
166 IStream_Release(merge_stream
);
168 WARN("Could not get %s::%s stream\n", debugstr_w(item
->merge
.chm_file
),
169 debugstr_w(item
->merge
.chm_file
));
172 free_content_item(item
);
182 static ContentItem
*parse_ul(HHInfo
*info
, stream_t
*stream
, ContentItem
*hhc_root
)
184 strbuf_t node
, node_name
;
185 ContentItem
*ret
= NULL
, *prev
= NULL
, *new_item
= NULL
;
189 strbuf_init(&node_name
);
191 while(next_node(stream
, &node
)) {
192 get_node_name(&node
, &node_name
);
194 TRACE("%s\n", node
.buf
);
196 if(!stricmp(node_name
.buf
, "object")) {
200 static const char sz_text_sitemap
[] = "text/sitemap";
202 ptr
= get_attr(node
.buf
, "type", &len
);
204 if(ptr
&& len
== sizeof(sz_text_sitemap
)-1
205 && !memcmp(ptr
, sz_text_sitemap
, len
)) {
206 new_item
= parse_sitemap_object(info
, stream
, hhc_root
, &it
);
207 prev
= insert_item(prev
, new_item
, it
);
211 }else if(!stricmp(node_name
.buf
, "ul")) {
212 new_item
= parse_ul(info
, stream
, hhc_root
);
213 insert_item(prev
, new_item
, INSERT_CHILD
);
214 }else if(!stricmp(node_name
.buf
, "/ul")) {
222 strbuf_free(&node_name
);
227 static ContentItem
*parse_hhc(HHInfo
*info
, IStream
*str
, ContentItem
*hhc_root
,
228 insert_type_t
*insert_type
)
231 strbuf_t node
, node_name
;
232 ContentItem
*ret
= NULL
, *prev
= NULL
;
234 *insert_type
= INSERT_NEXT
;
237 strbuf_init(&node_name
);
239 stream_init(&stream
, str
);
241 while(next_node(&stream
, &node
)) {
242 get_node_name(&node
, &node_name
);
244 TRACE("%s\n", node
.buf
);
246 if(!stricmp(node_name
.buf
, "ul")) {
247 ContentItem
*item
= parse_ul(info
, &stream
, hhc_root
);
248 prev
= insert_item(prev
, item
, INSERT_CHILD
);
251 *insert_type
= INSERT_CHILD
;
258 strbuf_free(&node_name
);
263 static void insert_content_item(HWND hwnd
, ContentItem
*parent
, ContentItem
*item
)
265 TVINSERTSTRUCTW tvis
;
267 memset(&tvis
, 0, sizeof(tvis
));
268 tvis
.item
.mask
= TVIF_TEXT
|TVIF_PARAM
|TVIF_IMAGE
|TVIF_SELECTEDIMAGE
;
269 tvis
.item
.cchTextMax
= lstrlenW(item
->name
)+1;
270 tvis
.item
.pszText
= item
->name
;
271 tvis
.item
.lParam
= (LPARAM
)item
;
272 tvis
.item
.iImage
= item
->child
? HHTV_FOLDER
: HHTV_DOCUMENT
;
273 tvis
.item
.iSelectedImage
= item
->child
? HHTV_FOLDER
: HHTV_DOCUMENT
;
274 tvis
.hParent
= parent
? parent
->id
: 0;
275 tvis
.hInsertAfter
= TVI_LAST
;
277 item
->id
= (HTREEITEM
)SendMessageW(hwnd
, TVM_INSERTITEMW
, 0, (LPARAM
)&tvis
);
280 static void fill_content_tree(HWND hwnd
, ContentItem
*parent
, ContentItem
*item
)
284 insert_content_item(hwnd
, parent
, item
);
285 fill_content_tree(hwnd
, item
, item
->child
);
287 fill_content_tree(hwnd
, parent
, item
->child
);
293 static void set_item_parents(ContentItem
*parent
, ContentItem
*item
)
296 item
->parent
= parent
;
297 set_item_parents(item
, item
->child
);
302 void InitContent(HHInfo
*info
)
305 insert_type_t insert_type
;
307 info
->content
= calloc(1, sizeof(ContentItem
));
308 SetChmPath(&info
->content
->merge
, info
->pCHMInfo
->szFile
, info
->WinType
.pszToc
);
310 stream
= GetChmStream(info
->pCHMInfo
, info
->pCHMInfo
->szFile
, &info
->content
->merge
);
312 TRACE("Could not get content stream\n");
316 info
->content
->child
= parse_hhc(info
, stream
, info
->content
, &insert_type
);
317 IStream_Release(stream
);
319 set_item_parents(NULL
, info
->content
);
320 fill_content_tree(info
->tabs
[TAB_CONTENTS
].hwnd
, NULL
, info
->content
);
323 void ReleaseContent(HHInfo
*info
)
325 free_content_item(info
->content
);
328 void ActivateContentTopic(HWND hWnd
, LPCWSTR filename
, ContentItem
*item
)
330 if (lstrcmpiW(item
->local
, filename
) == 0)
332 SendMessageW(hWnd
, TVM_SELECTITEM
, TVGN_CARET
, (LPARAM
) item
->id
);
337 ActivateContentTopic(hWnd
, filename
, item
->next
);
340 ActivateContentTopic(hWnd
, filename
, item
->child
);