[HHCTRL.OCX]
authorChristoph von Wittich <christoph_vw@reactos.org>
Sun, 7 Mar 2010 12:48:05 +0000 (12:48 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Sun, 7 Mar 2010 12:48:05 +0000 (12:48 +0000)
sync hhctrl.ocx to wine 1.1.40

svn path=/trunk/; revision=45990

31 files changed:
reactos/dll/win32/hhctrl.ocx/Cs.rc
reactos/dll/win32/hhctrl.ocx/Da.rc
reactos/dll/win32/hhctrl.ocx/De.rc
reactos/dll/win32/hhctrl.ocx/El.rc
reactos/dll/win32/hhctrl.ocx/En.rc
reactos/dll/win32/hhctrl.ocx/Es.rc [new file with mode: 0644]
reactos/dll/win32/hhctrl.ocx/Fi.rc
reactos/dll/win32/hhctrl.ocx/Fr.rc
reactos/dll/win32/hhctrl.ocx/Hu.rc
reactos/dll/win32/hhctrl.ocx/Ko.rc
reactos/dll/win32/hhctrl.ocx/Lt.rc
reactos/dll/win32/hhctrl.ocx/Nl.rc
reactos/dll/win32/hhctrl.ocx/No.rc
reactos/dll/win32/hhctrl.ocx/Pl.rc
reactos/dll/win32/hhctrl.ocx/Pt.rc
reactos/dll/win32/hhctrl.ocx/Ru.rc
reactos/dll/win32/hhctrl.ocx/Si.rc
reactos/dll/win32/hhctrl.ocx/Sv.rc
reactos/dll/win32/hhctrl.ocx/Tr.rc
reactos/dll/win32/hhctrl.ocx/Uk.rc [new file with mode: 0644]
reactos/dll/win32/hhctrl.ocx/Zh.rc
reactos/dll/win32/hhctrl.ocx/chm.c
reactos/dll/win32/hhctrl.ocx/content.c
reactos/dll/win32/hhctrl.ocx/help.c
reactos/dll/win32/hhctrl.ocx/hhctrl.c
reactos/dll/win32/hhctrl.ocx/hhctrl.h
reactos/dll/win32/hhctrl.ocx/hhctrl.rc
reactos/dll/win32/hhctrl.ocx/index.c [new file with mode: 0644]
reactos/dll/win32/hhctrl.ocx/search.c [new file with mode: 0644]
reactos/dll/win32/hhctrl.ocx/stream.c [new file with mode: 0644]
reactos/dll/win32/hhctrl.ocx/stream.h [new file with mode: 0644]

index ec564d8..b914762 100644 (file)
@@ -21,6 +21,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_CZECH, SUBLANG_DEFAULT
 
 /* Czech strings in CP1250 */
index e425ef0..1832d71 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_DANISH, SUBLANG_DEFAULT
 
 STRINGTABLE
index ef6e699..20b20f8 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
+#pragma code_page(65001)
+
 LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
 
 STRINGTABLE
@@ -33,14 +37,14 @@ STRINGTABLE
 BEGIN
     IDTB_EXPAND      "Anzeigen"
     IDTB_CONTRACT    "Verstecken"
-    IDTB_STOP        "Stop"
+    IDTB_STOP        "Stopp"
     IDTB_REFRESH     "Aktualisieren"
-    IDTB_BACK        "Zurück"
+    IDTB_BACK        "Zurück"
     IDTB_HOME        "Startseite"
     IDTB_SYNC        "Synchronisieren"
     IDTB_PRINT       "Drucken"
     IDTB_OPTIONS     "Einstellungen"
-    IDTB_FORWARD     "Vorwärts"
+    IDTB_FORWARD     "Vorwärts"
     IDTB_NOTES       "IDTB_NOTES"
     IDTB_BROWSE_FWD  "IDTB_BROWSE_FWD"
     IDTB_BROWSE_BACK "IDT_BROWSE_BACK"
@@ -52,7 +56,7 @@ BEGIN
     IDTB_JUMP1       "Sprung1"
     IDTB_JUMP2       "Sprung2"
     IDTB_CUSTOMIZE   "Anpassen"
-    IDTB_ZOOM        "Vergrößern"
+    IDTB_ZOOM        "Vergrößern"
     IDTB_TOC_NEXT    "IDTB_TOC_NEXT"
     IDTB_TOC_PREV    "IDTB_TOC_PREV"
 END
index dd81eb6..4fa79a2 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_GREEK, SUBLANG_DEFAULT
 
 STRINGTABLE
index 83917f6..b69383e 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
 
 STRINGTABLE
@@ -56,3 +58,13 @@ BEGIN
     IDTB_TOC_NEXT    "IDTB_TOC_NEXT"
     IDTB_TOC_PREV    "IDTB_TOC_PREV"
 END
+
+LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
+
+STRINGTABLE
+BEGIN
+    IDS_CONTENTS     "&Contents"
+    IDS_INDEX        "I&ndex"
+    IDS_SEARCH       "&Search"
+    IDS_FAVORITES    "Favour&ites"
+END
diff --git a/reactos/dll/win32/hhctrl.ocx/Es.rc b/reactos/dll/win32/hhctrl.ocx/Es.rc
new file mode 100644 (file)
index 0000000..d7681aa
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * HTML Help resources
+ * Spanish Language Support
+ *
+ * Copyright 2010 José Manuel Ferrer Ortiz
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "resource.h"
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
+
+STRINGTABLE
+BEGIN
+    IDS_CONTENTS     "&Contenido"
+    IDS_INDEX        "Í&ndice"
+    IDS_SEARCH       "&Buscar"
+    IDS_FAVORITES    "Favor&itos"
+END
+
+STRINGTABLE
+BEGIN
+    IDTB_EXPAND      "Mostrar"
+    IDTB_CONTRACT    "Ocultar"
+    IDTB_STOP        "Parar"
+    IDTB_REFRESH     "Recargar"
+    IDTB_BACK        "Atrás"
+    IDTB_HOME        "Inicio"
+    IDTB_SYNC        "Sincronizar"
+    IDTB_PRINT       "Imprimir"
+    IDTB_OPTIONS     "Opciones"
+    IDTB_FORWARD     "Adelante"
+    IDTB_NOTES       "IDTB_NOTES"
+    IDTB_BROWSE_FWD  "IDTB_BROWSE_FWD"
+    IDTB_BROWSE_BACK "IDT_BROWSE_BACK"
+    IDTB_CONTENTS    "IDTB_CONTENTS"
+    IDTB_INDEX       "IDTB_INDEX"
+    IDTB_SEARCH      "IDTB_SEARCH"
+    IDTB_HISTORY     "IDTB_HISTORY"
+    IDTB_FAVORITES   "IDTB_FAVORITES"
+    IDTB_JUMP1       "Jump1"
+    IDTB_JUMP2       "Jump2"
+    IDTB_CUSTOMIZE   "Personalizar"
+    IDTB_ZOOM        "Zoom"
+    IDTB_TOC_NEXT    "IDTB_TOC_NEXT"
+    IDTB_TOC_PREV    "IDTB_TOC_PREV"
+END
index 975ce1f..825422e 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_FINNISH, SUBLANG_DEFAULT
 
 STRINGTABLE
index 0e5dd4f..2146a3c 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
+/* UTF-8 */
+#pragma code_page(65001)
+
 LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
 
 STRINGTABLE
@@ -33,9 +38,9 @@ STRINGTABLE
 BEGIN
     IDTB_EXPAND      "Afficher"
     IDTB_CONTRACT    "Cacher"
-    IDTB_STOP        "Arrêter"
+    IDTB_STOP        "Arrêter"
     IDTB_REFRESH     "A&ctualiser"
-    IDTB_BACK        "Précédent"
+    IDTB_BACK        "Précédent"
     IDTB_HOME        "Sommaire"
     IDTB_SYNC        "Synchroniser"
     IDTB_PRINT       "Imprimer"
@@ -49,8 +54,8 @@ BEGIN
     IDTB_SEARCH      "IDTB_SEARCH"
     IDTB_HISTORY     "IDTB_HISTORY"
     IDTB_FAVORITES   "IDTB_FAVORITES"
-    IDTB_JUMP1       "Jump1"
-    IDTB_JUMP2       "Jump2"
+    IDTB_JUMP1       "Saut1"
+    IDTB_JUMP2       "Saut2"
     IDTB_CUSTOMIZE   "Personnaliser"
     IDTB_ZOOM        "Zoom"
     IDTB_TOC_NEXT    "IDTB_TOC_NEXT"
index 225c04a..0cab8cd 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_HUNGARIAN, SUBLANG_DEFAULT
 
 STRINGTABLE
index 169eb70..37831df 100644 (file)
@@ -20,6 +20,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT
 
 STRINGTABLE
index fed6b40..8c813ba 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 /* UTF-8 */
 #pragma code_page(65001)
 
@@ -59,5 +61,3 @@ BEGIN
     IDTB_TOC_NEXT    "IDTB_TOC_NEXT"
     IDTB_TOC_PREV    "IDTB_TOC_PREV"
 END
-
-#pragma code_page(default)
index 929ac39..99c12fa 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_DUTCH, SUBLANG_NEUTRAL
 
 STRINGTABLE
index 5ff634d..a76ad31 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL
 
 STRINGTABLE
index 40d3089..f3ce6f2 100644 (file)
@@ -20,6 +20,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_POLISH, SUBLANG_DEFAULT
 
 STRINGTABLE
index d196ff4..13476ce 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL
 
 STRINGTABLE
index a530f80..0634c39 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
+/* UTF-8 */
+#pragma code_page(65001)
+
 LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
 
 STRINGTABLE
 BEGIN
-    IDS_CONTENTS     "&Ñîäåðæàíèå"
-    IDS_INDEX        "&Îãëàâëåíèå"
-    IDS_SEARCH       "&Ïîèñê"
-    IDS_FAVORITES    "&Èçáðàííîå"
+    IDS_CONTENTS     "&Содержание"
+    IDS_INDEX        "&Оглавление"
+    IDS_SEARCH       "&Поиск"
+    IDS_FAVORITES    "&Избранное"
 END
 
 STRINGTABLE
 BEGIN
-    IDTB_EXPAND      "Ïîêàçàòü"
-    IDTB_CONTRACT    "Ñïðÿòàòü"
-    IDTB_STOP        "Îñòàíîâèòü"
-    IDTB_REFRESH     "Îáíîâèòü"
-    IDTB_BACK        "Íàçàä"
-    IDTB_HOME        " íà÷àëî"
-    IDTB_SYNC        "Ñèíõðîíèçèðîâàòü"
-    IDTB_PRINT       "Ïå÷àòü"
-    IDTB_OPTIONS     "Íàñòðîéêè"
-    IDTB_FORWARD     "Âïåð¸ä"
-    IDTB_NOTES       "Çàïèñêè"
-    IDTB_BROWSE_FWD  "Ïðîñìîòð âïåð¸ä"
-    IDTB_BROWSE_BACK "Ïðîñìîòð íàçàä"
-    IDTB_CONTENTS    "Ñîäåðæàíèå"
-    IDTB_INDEX       "Îãëàâëåíèå"
-    IDTB_SEARCH      "Ïîèñê"
-    IDTB_HISTORY     "Èñòîðèÿ"
-    IDTB_FAVORITES   "Èçáðàííîå"
-    IDTB_JUMP1       "Ïåðåõîä 1"
-    IDTB_JUMP2       "Ïåðåõîä 2"
-    IDTB_CUSTOMIZE   "Ïåðñîíàëèçîâàòü"
-    IDTB_ZOOM        "Ìàñøòàá"
-    IDTB_TOC_NEXT    "Ñëåäóþùàÿ ãëàâà"
-    IDTB_TOC_PREV    "Ïðåäûäóùàÿ ãëàâà"
+    IDTB_EXPAND      "Показать"
+    IDTB_CONTRACT    "Спрятать"
+    IDTB_STOP        "Остановить"
+    IDTB_REFRESH     "Обновить"
+    IDTB_BACK        "Назад"
+    IDTB_HOME        "В начало"
+    IDTB_SYNC        "Синхронизировать"
+    IDTB_PRINT       "Печать"
+    IDTB_OPTIONS     "Настройки"
+    IDTB_FORWARD     "Вперёд"
+    IDTB_NOTES       "Записки"
+    IDTB_BROWSE_FWD  "Просмотр вперёд"
+    IDTB_BROWSE_BACK "Просмотр назад"
+    IDTB_CONTENTS    "Содержание"
+    IDTB_INDEX       "Оглавление"
+    IDTB_SEARCH      "Поиск"
+    IDTB_HISTORY     "История"
+    IDTB_FAVORITES   "Избранное"
+    IDTB_JUMP1       "Переход 1"
+    IDTB_JUMP2       "Переход 2"
+    IDTB_CUSTOMIZE   "Персонализовать"
+    IDTB_ZOOM        "Масштаб"
+    IDTB_TOC_NEXT    "Следующая глава"
+    IDTB_TOC_PREV    "Предыдущая глава"
 END
index c8ea447..bc8ca1d 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 #pragma code_page(65001)
 
 LANGUAGE LANG_SLOVENIAN, SUBLANG_DEFAULT
@@ -58,5 +60,3 @@ BEGIN
     IDTB_TOC_NEXT    "IDTB_TOC_NEXT"
     IDTB_TOC_PREV    "IDTB_TOC_PREV"
 END
-
-#pragma code_page(default)
index cb3e4e2..b92c63c 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL
 
 STRINGTABLE
index 7488999..f1011b1 100644 (file)
@@ -19,6 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 LANGUAGE LANG_TURKISH, SUBLANG_DEFAULT
 
 STRINGTABLE
diff --git a/reactos/dll/win32/hhctrl.ocx/Uk.rc b/reactos/dll/win32/hhctrl.ocx/Uk.rc
new file mode 100644 (file)
index 0000000..40d96b8
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * HTML Help resources
+ * Ukrainian Language Support
+ *
+ * Copyright 2005 James Hawkins
+ * Copyright 2007 Artem Reznikov
+ * Copyright 2010 Igor Paliychuk
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "resource.h"
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT
+
+STRINGTABLE
+BEGIN
+    IDS_CONTENTS     "&Зміст"
+    IDS_INDEX        "&Вказівник"
+    IDS_SEARCH       "&Пошук"
+    IDS_FAVORITES    "&Обране"
+END
+
+STRINGTABLE
+BEGIN
+    IDTB_EXPAND      "Показувати"
+    IDTB_CONTRACT    "Приховати"
+    IDTB_STOP        "Зупинити"
+    IDTB_REFRESH     "Оновити"
+    IDTB_BACK        "Назад"
+    IDTB_HOME        "Додому"
+    IDTB_SYNC        "Синхронізувати"
+    IDTB_PRINT       "Друк"
+    IDTB_OPTIONS     "Параметри"
+    IDTB_FORWARD     "Вперед"
+    IDTB_NOTES       "IDTB_NOTES"
+    IDTB_BROWSE_FWD  "IDTB_BROWSE_FWD"
+    IDTB_BROWSE_BACK "IDT_BROWSE_BACK"
+    IDTB_CONTENTS    "IDTB_CONTENTS"
+    IDTB_INDEX       "IDTB_INDEX"
+    IDTB_SEARCH      "IDTB_SEARCH"
+    IDTB_HISTORY     "IDTB_HISTORY"
+    IDTB_FAVORITES   "IDTB_FAVORITES"
+    IDTB_JUMP1       "Jump1"
+    IDTB_JUMP2       "Jump2"
+    IDTB_CUSTOMIZE   "Налаштування"
+    IDTB_ZOOM        "Збільшення"
+    IDTB_TOC_NEXT    "IDTB_TOC_NEXT"
+    IDTB_TOC_PREV    "IDTB_TOC_PREV"
+END
+
+LANGUAGE LANG_UKRAINIAN, SUBLANG_NEUTRAL
+
+STRINGTABLE
+BEGIN
+    IDS_CONTENTS     "&Зміст"
+    IDS_INDEX        "&Вказівник"
+    IDS_SEARCH       "&Пошук"
+    IDS_FAVORITES    "&Обране"
+END
index a7ccab0..d6295a9 100644 (file)
@@ -18,6 +18,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "resource.h"
+
 /* Chinese text is encoded in UTF-8 */
 #pragma code_page(65001)
 
@@ -96,5 +98,3 @@ BEGIN
     IDTB_TOC_NEXT    "後一項"
     IDTB_TOC_PREV    "前一項"
 END
-
-#pragma code_page(default)
index b7df8e7..cf5b8bf 100644 (file)
@@ -38,12 +38,13 @@ static LPCSTR GetChmString(CHMInfo *chm, DWORD offset)
         return NULL;
 
     if(chm->strings_size <= (offset >> BLOCK_BITS)) {
+        chm->strings_size = (offset >> BLOCK_BITS)+1;
         if(chm->strings)
             chm->strings = heap_realloc_zero(chm->strings,
-                    chm->strings_size = ((offset >> BLOCK_BITS)+1)*sizeof(char*));
+                    chm->strings_size*sizeof(char*));
         else
             chm->strings = heap_alloc_zero(
-                    chm->strings_size = ((offset >> BLOCK_BITS)+1)*sizeof(char*));
+                    chm->strings_size*sizeof(char*));
 
     }
 
@@ -388,7 +389,6 @@ CHMInfo *OpenCHM(LPCWSTR szFile)
         WARN("Could not open storage: %08x\n", hres);
         return CloseCHM(ret);
     }
-
     hres = IStorage_OpenStream(ret->pStorage, wszSTRINGS, NULL, STGM_READ, 0,
             &ret->strings_stream);
     if(FAILED(hres)) {
index 4fc0176..bfe25cf 100644 (file)
 #define NONAMELESSSTRUCT
 
 #include "hhctrl.h"
+#include "stream.h"
 
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
 
-#define BLOCK_SIZE 0x1000
-
 typedef enum {
     INSERT_NEXT,
     INSERT_CHILD
@@ -50,134 +49,6 @@ static void free_content_item(ContentItem *item)
     }
 }
 
-typedef struct {
-    char *buf;
-    int size;
-    int len;
-} strbuf_t;
-
-static void strbuf_init(strbuf_t *buf)
-{
-    buf->size = 8;
-    buf->len = 0;
-    buf->buf = heap_alloc(buf->size);
-}
-
-static void strbuf_zero(strbuf_t *buf)
-{
-    buf->len = 0;
-}
-
-static void strbuf_free(strbuf_t *buf)
-{
-    heap_free(buf->buf);
-}
-
-static void strbuf_append(strbuf_t *buf, const char *data, int len)
-{
-    if(buf->len+len > buf->size) {
-        buf->size = buf->len+len;
-        buf->buf = heap_realloc(buf->buf, buf->size);
-    }
-
-    memcpy(buf->buf+buf->len, data, len);
-    buf->len += len;
-}
-
-typedef struct {
-    IStream *str;
-    char buf[BLOCK_SIZE];
-    ULONG size;
-    ULONG p;
-} stream_t;
-
-static void stream_init(stream_t *stream, IStream *str)
-{
-    memset(stream, 0, sizeof(stream_t));
-    stream->str = str;
-}
-
-static BOOL stream_chr(stream_t *stream, strbuf_t *buf, char c)
-{
-    BOOL b = TRUE;
-    ULONG i;
-
-    while(b) {
-        for(i=stream->p; i<stream->size; i++) {
-            if(stream->buf[i] == c) {
-                b = FALSE;
-                break;
-            }
-        }
-
-        if(buf && i > stream->p)
-            strbuf_append(buf, stream->buf+stream->p, i-stream->p);
-        stream->p = i;
-
-        if(stream->p == stream->size) {
-            stream->p = 0;
-            IStream_Read(stream->str, stream->buf, sizeof(stream->buf), &stream->size);
-            if(!stream->size)
-                break;
-        }
-    }
-
-    return stream->size != 0;
-}
-
-static void get_node_name(strbuf_t *node, strbuf_t *name)
-{
-    const char *ptr = node->buf+1;
-
-    strbuf_zero(name);
-
-    while(*ptr != '>' && !isspace(*ptr))
-        ptr++;
-
-    strbuf_append(name, node->buf+1, ptr-node->buf-1);
-    strbuf_append(name, "", 1);
-}
-
-static BOOL next_node(stream_t *stream, strbuf_t *buf)
-{
-    if(!stream_chr(stream, NULL, '<'))
-        return FALSE;
-
-    if(!stream_chr(stream, buf, '>'))
-        return FALSE;
-
-    strbuf_append(buf, ">", 2);
-
-    return TRUE;
-}
-
-static const char *get_attr(const char *node, const char *name, int *len)
-{
-    const char *ptr, *ptr2;
-    char name_buf[32];
-    int nlen;
-
-    nlen = strlen(name);
-    memcpy(name_buf, name, nlen);
-    name_buf[nlen++] = '=';
-    name_buf[nlen++] = '\"';
-    name_buf[nlen] = 0;
-
-    ptr = strstr(node, name_buf);
-    if(!ptr) {
-        WARN("name not found\n");
-        return NULL;
-    }
-
-    ptr += nlen;
-    ptr2 = strchr(ptr, '\"');
-    if(!ptr2)
-        return NULL;
-
-    *len = ptr2-ptr;
-    return ptr;
-}
-
 static void parse_obj_node_param(ContentItem *item, ContentItem *hhc_root, const char *text)
 {
     const char *ptr;
index 092e97e..30cb008 100644 (file)
@@ -44,6 +44,7 @@ static LRESULT Help_OnSize(HWND hWnd);
 #define TAB_TOP_PADDING     8
 #define TAB_RIGHT_PADDING   4
 #define TAB_MARGIN  8
+#define EDIT_HEIGHT         20
 
 static const WCHAR szEmpty[] = {0};
 
@@ -320,8 +321,10 @@ static LRESULT Child_OnPaint(HWND hWnd)
     return 0;
 }
 
-static void ResizeTabChild(HHInfo *info, HWND hwnd)
+static void ResizeTabChild(HHInfo *info, int tab)
 {
+    HWND hwnd = info->tabs[tab].hwnd;
+    INT width, height;
     RECT rect, tabrc;
     DWORD cnt;
 
@@ -333,9 +336,47 @@ static void ResizeTabChild(HHInfo *info, HWND hwnd)
     rect.top = TAB_TOP_PADDING + cnt*(tabrc.bottom-tabrc.top) + TAB_MARGIN;
     rect.right -= TAB_RIGHT_PADDING + TAB_MARGIN;
     rect.bottom -= TAB_MARGIN;
+    width = rect.right-rect.left;
+    height = rect.bottom-rect.top;
 
-    SetWindowPos(hwnd, NULL, rect.left, rect.top, rect.right-rect.left,
-                 rect.bottom-rect.top, SWP_NOZORDER | SWP_NOACTIVATE);
+    SetWindowPos(hwnd, NULL, rect.left, rect.top, width, height,
+                 SWP_NOZORDER | SWP_NOACTIVATE);
+
+    switch (tab)
+    {
+    case TAB_INDEX: {
+        int scroll_width = GetSystemMetrics(SM_CXVSCROLL);
+        int border_width = GetSystemMetrics(SM_CXBORDER);
+        int edge_width = GetSystemMetrics(SM_CXEDGE);
+
+        /* Resize the tab widget column to perfectly fit the tab window and
+         * leave sufficient space for the scroll widget.
+         */
+        SendMessageW(info->tabs[TAB_INDEX].hwnd, LVM_SETCOLUMNWIDTH, 0,
+                     width-scroll_width-2*border_width-2*edge_width);
+
+        break;
+    }
+    case TAB_SEARCH: {
+        int scroll_width = GetSystemMetrics(SM_CXVSCROLL);
+        int border_width = GetSystemMetrics(SM_CXBORDER);
+        int edge_width = GetSystemMetrics(SM_CXEDGE);
+        int top_pos = 0;
+
+        SetWindowPos(info->search.hwndEdit, NULL, 0, top_pos, width,
+                      EDIT_HEIGHT, SWP_NOZORDER | SWP_NOACTIVATE);
+        top_pos += EDIT_HEIGHT + TAB_MARGIN;
+        SetWindowPos(info->search.hwndList, NULL, 0, top_pos, width,
+                      height-top_pos, SWP_NOZORDER | SWP_NOACTIVATE);
+        /* Resize the tab widget column to perfectly fit the tab window and
+         * leave sufficient space for the scroll widget.
+         */
+        SendMessageW(info->search.hwndList, LVM_SETCOLUMNWIDTH, 0,
+                     width-scroll_width-2*border_width-2*edge_width);
+
+        break;
+    }
+    }
 }
 
 static LRESULT Child_OnSize(HWND hwnd)
@@ -351,7 +392,8 @@ static LRESULT Child_OnSize(HWND hwnd)
                  rect.right - TAB_RIGHT_PADDING,
                  rect.bottom - TAB_TOP_PADDING, SWP_NOMOVE);
 
-    ResizeTabChild(info, info->tabs[TAB_CONTENTS].hwnd);
+    ResizeTabChild(info, TAB_CONTENTS);
+    ResizeTabChild(info, TAB_INDEX);
     return 0;
 }
 
@@ -375,29 +417,101 @@ static LRESULT OnTabChange(HWND hwnd)
     return 0;
 }
 
-static LRESULT OnTopicChange(HWND hwnd, ContentItem *item)
+static LRESULT OnTopicChange(HHInfo *info, void *user_data)
 {
-    HHInfo *info = (HHInfo*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
-    LPCWSTR chmfile = NULL;
-    ContentItem *iter = item;
+    LPCWSTR chmfile = NULL, name = NULL, local = NULL;
+    ContentItem *citer;
+    SearchItem *siter;
+    IndexItem *iiter;
 
-    if(!item || !info)
+    if(!user_data || !info)
         return 0;
 
-    TRACE("name %s loal %s\n", debugstr_w(item->name), debugstr_w(item->local));
-
-    while(iter) {
-        if(iter->merge.chm_file) {
-            chmfile = iter->merge.chm_file;
-            break;
+    switch (info->current_tab)
+    {
+    case TAB_CONTENTS:
+        citer = (ContentItem *) user_data;
+        name = citer->name;
+        local = citer->local;
+        while(citer) {
+            if(citer->merge.chm_file) {
+                chmfile = citer->merge.chm_file;
+                break;
+            }
+            citer = citer->parent;
+        }
+        break;
+    case TAB_INDEX:
+        iiter = (IndexItem *) user_data;
+        if(iiter->nItems == 0) {
+            FIXME("No entries for this item!\n");
+            return 0;
+        }
+        if(iiter->nItems > 1) {
+            int i = 0;
+            LVITEMW lvi;
+
+            SendMessageW(info->popup.hwndList, LVM_DELETEALLITEMS, 0, 0);
+            for(i=0;i<iiter->nItems;i++) {
+                IndexSubItem *item = &iiter->items[i];
+                WCHAR *name = iiter->keyword;
+
+                if(item->name)
+                    name = item->name;
+                memset(&lvi, 0, sizeof(lvi));
+                lvi.iItem = i;
+                lvi.mask = LVIF_TEXT|LVIF_PARAM;
+                lvi.cchTextMax = strlenW(name)+1;
+                lvi.pszText = name;
+                lvi.lParam = (LPARAM) item;
+                SendMessageW(info->popup.hwndList, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
+            }
+            ShowWindow(info->popup.hwndPopup, SW_SHOW);
+            return 0;
         }
-        iter = iter->parent;
+        name = iiter->items[0].name;
+        local = iiter->items[0].local;
+        chmfile = iiter->merge.chm_file;
+        break;
+    case TAB_SEARCH:
+        siter = (SearchItem *) user_data;
+        name = siter->filename;
+        local = siter->filename;
+        chmfile = info->pCHMInfo->szFile;
+        break;
+    default:
+        FIXME("Unhandled operation for this tab!\n");
+        return 0;
+    }
+
+    if(!chmfile)
+    {
+        FIXME("No help file found for this item!\n");
+        return 0;
     }
 
-    NavigateToChm(info, chmfile, item->local);
+    TRACE("name %s loal %s\n", debugstr_w(name), debugstr_w(local));
+
+    NavigateToChm(info, chmfile, local);
     return 0;
 }
 
+/* Capture the Enter/Return key and send it up to Child_WndProc as an NM_RETURN message */
+static LRESULT CALLBACK EditChild_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    WNDPROC editWndProc = (WNDPROC)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
+
+    if(message == WM_KEYUP && wParam == VK_RETURN)
+    {
+        NMHDR nmhdr;
+
+        nmhdr.hwndFrom = hWnd;
+        nmhdr.code = NM_RETURN;
+        SendMessageW(GetParent(GetParent(hWnd)), WM_NOTIFY, wParam, (LPARAM)&nmhdr);
+    }
+    return editWndProc(hWnd, message, wParam, lParam);
+}
+
 static LRESULT CALLBACK Child_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
     switch (message)
@@ -407,12 +521,71 @@ static LRESULT CALLBACK Child_WndProc(HWND hWnd, UINT message, WPARAM wParam, LP
     case WM_SIZE:
         return Child_OnSize(hWnd);
     case WM_NOTIFY: {
+        HHInfo *info = (HHInfo*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
         NMHDR *nmhdr = (NMHDR*)lParam;
+
         switch(nmhdr->code) {
         case TCN_SELCHANGE:
             return OnTabChange(hWnd);
         case TVN_SELCHANGEDW:
-            return OnTopicChange(hWnd, (ContentItem*)((NMTREEVIEWW *)lParam)->itemNew.lParam);
+            return OnTopicChange(info, (void*)((NMTREEVIEWW *)lParam)->itemNew.lParam);
+        case NM_DBLCLK:
+            if(!info)
+                return 0;
+            switch(info->current_tab)
+            {
+            case TAB_INDEX:
+                return OnTopicChange(info, (void*)((NMITEMACTIVATE *)lParam)->lParam);
+            case TAB_SEARCH:
+                return OnTopicChange(info, (void*)((NMITEMACTIVATE *)lParam)->lParam);
+            }
+            break;
+        case NM_RETURN:
+            if(!info)
+                return 0;
+            switch(info->current_tab) {
+            case TAB_INDEX: {
+                HWND hwndList = info->tabs[TAB_INDEX].hwnd;
+                LVITEMW lvItem;
+
+                lvItem.iItem = (int) SendMessageW(hwndList, LVM_GETSELECTIONMARK, 0, 0);
+                lvItem.mask = TVIF_PARAM;
+                SendMessageW(hwndList, LVM_GETITEMW, 0, (LPARAM)&lvItem);
+                OnTopicChange(info, (void*) lvItem.lParam);
+                return 0;
+            }
+            case TAB_SEARCH: {
+                if(nmhdr->hwndFrom == info->search.hwndEdit) {
+                    char needle[100];
+                    DWORD i, len;
+
+                    len = GetWindowTextA(info->search.hwndEdit, needle, sizeof(needle));
+                    if(!len)
+                    {
+                        FIXME("Unable to get search text.\n");
+                        return 0;
+                    }
+                    /* Convert the requested text for comparison later against the
+                     * lower case version of HTML file contents.
+                     */
+                    for(i=0;i<len;i++)
+                        needle[i] = tolower(needle[i]);
+                    InitSearch(info, needle);
+                    return 0;
+                }else if(nmhdr->hwndFrom == info->search.hwndList) {
+                    HWND hwndList = info->search.hwndList;
+                    LVITEMW lvItem;
+
+                    lvItem.iItem = (int) SendMessageW(hwndList, LVM_GETSELECTIONMARK, 0, 0);
+                    lvItem.mask = TVIF_PARAM;
+                    SendMessageW(hwndList, LVM_GETITEMW, 0, (LPARAM)&lvItem);
+                    OnTopicChange(info, (void*) lvItem.lParam);
+                    return 0;
+                }
+                break;
+            }
+            }
+            break;
         }
         break;
     }
@@ -729,6 +902,8 @@ static BOOL HH_AddHTMLPane(HHInfo *pHHInfo)
 
 static BOOL AddContentTab(HHInfo *info)
 {
+    if(info->tabs[TAB_CONTENTS].id == -1)
+        return TRUE; /* No "Contents" tab */
     info->tabs[TAB_CONTENTS].hwnd = CreateWindowExW(WS_EX_CLIENTEDGE, WC_TREEVIEWW,
            szEmpty, WS_CHILD | WS_BORDER | 0x25, 50, 50, 100, 100,
            info->WinType.hwndNavigation, NULL, hhctrl_hinstance, NULL);
@@ -737,12 +912,293 @@ static BOOL AddContentTab(HHInfo *info)
         return FALSE;
     }
 
-    ResizeTabChild(info, info->tabs[TAB_CONTENTS].hwnd);
+    ResizeTabChild(info, TAB_CONTENTS);
     ShowWindow(info->tabs[TAB_CONTENTS].hwnd, SW_SHOW);
 
     return TRUE;
 }
 
+static BOOL AddIndexTab(HHInfo *info)
+{
+    char hidden_column[] = "Column";
+    LVCOLUMNA lvc;
+
+    if(info->tabs[TAB_INDEX].id == -1)
+        return TRUE; /* No "Index" tab */
+    info->tabs[TAB_INDEX].hwnd = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW,
+           szEmpty, WS_CHILD | WS_BORDER | LVS_SINGLESEL | LVS_REPORT | LVS_NOCOLUMNHEADER, 50, 50, 100, 100,
+           info->WinType.hwndNavigation, NULL, hhctrl_hinstance, NULL);
+    if(!info->tabs[TAB_INDEX].hwnd) {
+        ERR("Could not create ListView control\n");
+        return FALSE;
+    }
+    memset(&lvc, 0, sizeof(lvc));
+    lvc.mask = LVCF_TEXT;
+    lvc.pszText = hidden_column;
+    if(SendMessageW(info->tabs[TAB_INDEX].hwnd, LVM_INSERTCOLUMNA, 0, (LPARAM) &lvc) == -1)
+    {
+        ERR("Could not create ListView column\n");
+        return FALSE;
+    }
+
+    ResizeTabChild(info, TAB_INDEX);
+    ShowWindow(info->tabs[TAB_INDEX].hwnd, SW_HIDE);
+
+    return TRUE;
+}
+
+static BOOL AddSearchTab(HHInfo *info)
+{
+    HWND hwndList, hwndEdit, hwndContainer;
+    char hidden_column[] = "Column";
+    WNDPROC editWndProc;
+    LVCOLUMNA lvc;
+
+    if(info->tabs[TAB_SEARCH].id == -1)
+        return TRUE; /* No "Search" tab */
+    hwndContainer = CreateWindowExW(WS_EX_CONTROLPARENT, szChildClass, szEmpty,
+                                    WS_CHILD, 0, 0, 0, 0, info->WinType.hwndNavigation,
+                                    NULL, hhctrl_hinstance, NULL);
+    if(!hwndContainer) {
+        ERR("Could not create search window container control.\n");
+        return FALSE;
+    }
+    hwndEdit = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, szEmpty, WS_CHILD
+                                | WS_VISIBLE | ES_LEFT | SS_NOTIFY, 0, 0, 0, 0,
+                               hwndContainer, NULL, hhctrl_hinstance, NULL);
+    if(!hwndEdit) {
+        ERR("Could not create search ListView control.\n");
+        return FALSE;
+    }
+    if(SendMessageW(hwndEdit, WM_SETFONT, (WPARAM) info->hFont, (LPARAM) FALSE) == -1)
+    {
+        ERR("Could not set font for edit control.\n");
+        return FALSE;
+    }
+    editWndProc = (WNDPROC) SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC, (LONG_PTR)EditChild_WndProc);
+    if(!editWndProc) {
+        ERR("Could not redirect messages for edit control.\n");
+        return FALSE;
+    }
+    SetWindowLongPtrW(hwndEdit, GWLP_USERDATA, (LONG_PTR)editWndProc);
+    hwndList = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW, szEmpty,
+                               WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_SINGLESEL
+                                | LVS_REPORT | LVS_NOCOLUMNHEADER, 0, 0, 0, 0,
+                               hwndContainer, NULL, hhctrl_hinstance, NULL);
+    if(!hwndList) {
+        ERR("Could not create search ListView control.\n");
+        return FALSE;
+    }
+    memset(&lvc, 0, sizeof(lvc));
+    lvc.mask = LVCF_TEXT;
+    lvc.pszText = hidden_column;
+    if(SendMessageW(hwndList, LVM_INSERTCOLUMNA, 0, (LPARAM) &lvc) == -1)
+    {
+        ERR("Could not create ListView column\n");
+        return FALSE;
+    }
+
+    info->search.hwndEdit = hwndEdit;
+    info->search.hwndList = hwndList;
+    info->search.hwndContainer = hwndContainer;
+    info->tabs[TAB_SEARCH].hwnd = hwndContainer;
+
+    SetWindowLongPtrW(hwndContainer, GWLP_USERDATA, (LONG_PTR)info);
+
+    ResizeTabChild(info, TAB_SEARCH);
+
+    return TRUE;
+}
+
+/* The Index tab's sub-topic popup */
+
+static void ResizePopupChild(HHInfo *info)
+{
+    int scroll_width = GetSystemMetrics(SM_CXVSCROLL);
+    int border_width = GetSystemMetrics(SM_CXBORDER);
+    int edge_width = GetSystemMetrics(SM_CXEDGE);
+    INT width, height;
+    RECT rect;
+
+    if(!info)
+        return;
+
+    GetClientRect(info->popup.hwndPopup, &rect);
+    SetWindowPos(info->popup.hwndCallback, HWND_TOP, 0, 0,
+                 rect.right, rect.bottom, SWP_NOMOVE);
+
+    rect.left = TAB_MARGIN;
+    rect.top = TAB_TOP_PADDING + TAB_MARGIN;
+    rect.right -= TAB_RIGHT_PADDING + TAB_MARGIN;
+    rect.bottom -= TAB_MARGIN;
+    width = rect.right-rect.left;
+    height = rect.bottom-rect.top;
+
+    SetWindowPos(info->popup.hwndList, NULL, rect.left, rect.top, width, height,
+                 SWP_NOZORDER | SWP_NOACTIVATE);
+
+    SendMessageW(info->popup.hwndList, LVM_SETCOLUMNWIDTH, 0,
+                 width-scroll_width-2*border_width-2*edge_width);
+}
+
+static LRESULT CALLBACK HelpPopup_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    HHInfo *info = (HHInfo *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
+
+    switch (message)
+    {
+    case WM_SIZE:
+        ResizePopupChild(info);
+        return 0;
+    case WM_DESTROY:
+        DestroyWindow(hWnd);
+        return 0;
+    case WM_CLOSE:
+        ShowWindow(hWnd, SW_HIDE);
+        return 0;
+
+    default:
+        return DefWindowProcW(hWnd, message, wParam, lParam);
+    }
+
+    return 0;
+}
+
+static LRESULT CALLBACK PopupChild_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    switch (message)
+    {
+    case WM_NOTIFY: {
+        NMHDR *nmhdr = (NMHDR*)lParam;
+        switch(nmhdr->code)
+        {
+        case NM_DBLCLK: {
+            HHInfo *info = (HHInfo*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
+            IndexSubItem *iter;
+
+            if(info == 0 || lParam == 0)
+                return 0;
+            iter = (IndexSubItem*) ((NMITEMACTIVATE *)lParam)->lParam;
+            if(iter == 0)
+                return 0;
+            NavigateToChm(info, info->index->merge.chm_file, iter->local);
+            ShowWindow(info->popup.hwndPopup, SW_HIDE);
+            return 0;
+        }
+        case NM_RETURN: {
+            HHInfo *info = (HHInfo*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
+            IndexSubItem *iter;
+            LVITEMW lvItem;
+
+            if(info == 0)
+                return 0;
+
+            lvItem.iItem = (int) SendMessageW(info->popup.hwndList, LVM_GETSELECTIONMARK, 0, 0);
+            lvItem.mask = TVIF_PARAM;
+            SendMessageW(info->popup.hwndList, LVM_GETITEMW, 0, (LPARAM)&lvItem);
+            iter = (IndexSubItem*) lvItem.lParam;
+            NavigateToChm(info, info->index->merge.chm_file, iter->local);
+            ShowWindow(info->popup.hwndPopup, SW_HIDE);
+            return 0;
+        }
+        }
+        break;
+    }
+    default:
+        return DefWindowProcW(hWnd, message, wParam, lParam);
+    }
+
+    return 0;
+}
+
+static BOOL AddIndexPopup(HHInfo *info)
+{
+    static const WCHAR szPopupChildClass[] = {'H','H',' ','P','o','p','u','p',' ','C','h','i','l','d',0};
+    static const WCHAR windowCaptionW[] = {'S','e','l','e','c','t',' ','T','o','p','i','c',':',0};
+    static const WCHAR windowClassW[] = {'H','H',' ','P','o','p','u','p',0};
+    HWND hwndList, hwndPopup, hwndCallback;
+    char hidden_column[] = "Column";
+    WNDCLASSEXW wcex;
+    LVCOLUMNA lvc;
+
+    if(info->tabs[TAB_INDEX].id == -1)
+        return TRUE; /* No "Index" tab */
+
+    wcex.cbSize         = sizeof(WNDCLASSEXW);
+    wcex.style          = CS_HREDRAW | CS_VREDRAW;
+    wcex.lpfnWndProc    = HelpPopup_WndProc;
+    wcex.cbClsExtra     = 0;
+    wcex.cbWndExtra     = 0;
+    wcex.hInstance      = hhctrl_hinstance;
+    wcex.hIcon          = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
+    wcex.hCursor        = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
+    wcex.hbrBackground  = (HBRUSH)(COLOR_MENU + 1);
+    wcex.lpszMenuName   = NULL;
+    wcex.lpszClassName  = windowClassW;
+    wcex.hIconSm        = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
+    RegisterClassExW(&wcex);
+
+    wcex.cbSize         = sizeof(WNDCLASSEXW);
+    wcex.style          = 0;
+    wcex.lpfnWndProc    = PopupChild_WndProc;
+    wcex.cbClsExtra     = 0;
+    wcex.cbWndExtra     = 0;
+    wcex.hInstance      = hhctrl_hinstance;
+    wcex.hIcon          = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
+    wcex.hCursor        = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
+    wcex.hbrBackground  = (HBRUSH)(COLOR_BTNFACE + 1);
+    wcex.lpszMenuName   = NULL;
+    wcex.lpszClassName  = szPopupChildClass;
+    wcex.hIconSm        = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION);
+    RegisterClassExW(&wcex);
+
+    hwndPopup = CreateWindowExW(WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_APPWINDOW
+                                 | WS_EX_WINDOWEDGE | WS_EX_RIGHTSCROLLBAR,
+                                windowClassW, windowCaptionW, WS_POPUPWINDOW
+                                 | WS_OVERLAPPEDWINDOW | WS_VISIBLE
+                                 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, CW_USEDEFAULT,
+                                CW_USEDEFAULT, 300, 200, info->WinType.hwndHelp,
+                                NULL, hhctrl_hinstance, NULL);
+    if (!hwndPopup)
+        return FALSE;
+
+    hwndCallback = CreateWindowExW(WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
+                                   szPopupChildClass, szEmpty, WS_CHILDWINDOW | WS_VISIBLE,
+                                   0, 0, 0, 0,
+                                   hwndPopup, NULL, hhctrl_hinstance, NULL);
+    if (!hwndCallback)
+        return FALSE;
+
+    ShowWindow(hwndPopup, SW_HIDE);
+    hwndList = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW, szEmpty,
+                               WS_CHILD | WS_BORDER | LVS_SINGLESEL | LVS_REPORT
+                                | LVS_NOCOLUMNHEADER, 50, 50, 100, 100,
+                               hwndCallback, NULL, hhctrl_hinstance, NULL);
+    if(!hwndList) {
+        ERR("Could not create popup ListView control\n");
+        return FALSE;
+    }
+    memset(&lvc, 0, sizeof(lvc));
+    lvc.mask = LVCF_TEXT;
+    lvc.pszText = hidden_column;
+    if(SendMessageW(hwndList, LVM_INSERTCOLUMNA, 0, (LPARAM) &lvc) == -1)
+    {
+        ERR("Could not create popup ListView column\n");
+        return FALSE;
+    }
+
+    info->popup.hwndCallback = hwndCallback;
+    info->popup.hwndPopup = hwndPopup;
+    info->popup.hwndList = hwndList;
+    SetWindowLongPtrW(hwndPopup, GWLP_USERDATA, (LONG_PTR)info);
+    SetWindowLongPtrW(hwndCallback, GWLP_USERDATA, (LONG_PTR)info);
+
+    ResizePopupChild(info);
+    ShowWindow(hwndList, SW_SHOW);
+
+    return TRUE;
+}
+
 /* Viewer Window */
 
 static LRESULT Help_OnSize(HWND hWnd)
@@ -918,7 +1374,17 @@ static BOOL CreateViewer(HHInfo *pHHInfo)
     if (!AddContentTab(pHHInfo))
         return FALSE;
 
+    if (!AddIndexTab(pHHInfo))
+        return FALSE;
+
+    if (!AddIndexPopup(pHHInfo))
+        return FALSE;
+
+    if (!AddSearchTab(pHHInfo))
+        return FALSE;
+
     InitContent(pHHInfo);
+    InitIndex(pHHInfo);
 
     return TRUE;
 }
@@ -947,6 +1413,8 @@ void ReleaseHelpViewer(HHInfo *info)
 
     ReleaseWebBrowser(info);
     ReleaseContent(info);
+    ReleaseIndex(info);
+    ReleaseSearch(info);
 
     if(info->WinType.hwndHelp)
         DestroyWindow(info->WinType.hwndHelp);
@@ -958,6 +1426,13 @@ void ReleaseHelpViewer(HHInfo *info)
 HHInfo *CreateHelpViewer(LPCWSTR filename)
 {
     HHInfo *info = heap_alloc_zero(sizeof(HHInfo));
+    int i;
+
+    /* Set the invalid tab ID (-1) as the default value for all
+     * of the tabs, this matches a failed TCM_INSERTITEM call.
+     */
+    for(i=0;i<sizeof(info->tabs)/sizeof(HHTab);i++)
+        info->tabs[i].id = -1;
 
     OleInitialize(NULL);
 
index 8268a21..99f7130 100644 (file)
@@ -272,12 +272,41 @@ HWND WINAPI HtmlHelpA(HWND caller, LPCSTR filename, UINT command, DWORD_PTR data
 int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine)
 {
     MSG msg;
-    int len, buflen;
+    int len, buflen, mapid = -1;
     WCHAR *filename;
     char *endq = NULL;
 
     hh_process = TRUE;
 
+    /* Parse command line option of the HTML Help command.
+     *
+     * Note: The only currently handled action is "mapid",
+     *  which corresponds to opening a specific page.
+     */
+    while(*szCmdLine == '-')
+    {
+        LPSTR space, ptr;
+
+        ptr = szCmdLine + 1;
+        space = strchr(ptr, ' ');
+        if(!strncmp(ptr, "mapid", space-ptr))
+        {
+            char idtxt[10];
+
+            ptr += strlen("mapid")+1;
+            space = strchr(ptr, ' ');
+            memcpy(idtxt, ptr, space-ptr);
+            idtxt[space-ptr] = '\0';
+            mapid = atoi(idtxt);
+            szCmdLine = space+1;
+        }
+        else
+        {
+            FIXME("Unhandled HTML Help command line parameter! (%.*s)\n", space-szCmdLine, szCmdLine);
+            return 0;
+        }
+    }
+
     /* FIXME: Check szCmdLine for bad arguments */
     if (*szCmdLine == '\"')
         endq = strchr(++szCmdLine, '\"');
@@ -291,7 +320,11 @@ int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine)
     MultiByteToWideChar(CP_ACP, 0, szCmdLine, len, filename, buflen);
     filename[buflen-1] = 0;
 
-    HtmlHelpW(GetDesktopWindow(), filename, HH_DISPLAY_TOPIC, 0);
+    /* Open a specific help topic */
+    if(mapid != -1)
+        HtmlHelpW(GetDesktopWindow(), filename, HH_HELP_CONTEXT, mapid);
+    else
+        HtmlHelpW(GetDesktopWindow(), filename, HH_DISPLAY_TOPIC, 0);
 
     heap_free(filename);
 
index 795bea4..91d3a77 100644 (file)
@@ -65,11 +65,37 @@ typedef struct ContentItem {
     ChmPath merge;
 } ContentItem;
 
+typedef struct IndexSubItem {
+    LPWSTR name;
+    LPWSTR local;
+} IndexSubItem;
+
+typedef struct IndexItem {
+    struct IndexItem *next;
+
+    HTREEITEM id;
+    LPWSTR keyword;
+    ChmPath merge;
+
+    int nItems;
+    int itemFlags;
+    int indentLevel;
+    IndexSubItem *items;
+} IndexItem;
+
+typedef struct SearchItem {
+    struct SearchItem *next;
+
+    HTREEITEM id;
+    LPWSTR title;
+    LPWSTR filename;
+} SearchItem;
+
 typedef struct CHMInfo
 {
     IITStorage *pITStorage;
     IStorage *pStorage;
-    LPCWSTR szFile;
+    WCHAR *szFile;
 
     IStream *strings_stream;
     char **strings;
@@ -90,6 +116,19 @@ typedef struct {
     DWORD id;
 } HHTab;
 
+typedef struct {
+    HWND hwndList;
+    HWND hwndPopup;
+    HWND hwndCallback;
+} IndexPopup;
+
+typedef struct {
+    SearchItem *root;
+    HWND hwndEdit;
+    HWND hwndList;
+    HWND hwndContainer;
+} SearchTab;
+
 typedef struct {
     IOleClientSite *client_site;
     IWebBrowser2 *web_browser;
@@ -111,6 +150,9 @@ typedef struct {
 
     CHMInfo *pCHMInfo;
     ContentItem *content;
+    IndexItem *index;
+    IndexPopup popup;
+    SearchTab search;
     HWND hwndTabCtrl;
     HWND hwndSizeBar;
     HFONT hFont;
@@ -127,6 +169,9 @@ void DoPageAction(HHInfo*,DWORD);
 void InitContent(HHInfo*);
 void ReleaseContent(HHInfo*);
 
+void InitIndex(HHInfo*);
+void ReleaseIndex(HHInfo*);
+
 CHMInfo *OpenCHM(LPCWSTR szFile);
 BOOL LoadWinTypeFromCHM(HHInfo *info);
 CHMInfo *CloseCHM(CHMInfo *pCHMInfo);
@@ -139,6 +184,9 @@ void ReleaseHelpViewer(HHInfo*);
 BOOL NavigateToUrl(HHInfo*,LPCWSTR);
 BOOL NavigateToChm(HHInfo*,LPCWSTR,LPCWSTR);
 
+void InitSearch(HHInfo *info, const char *needle);
+void ReleaseSearch(HHInfo *info);
+
 /* memory allocation functions */
 
 static inline void * __WINE_ALLOC_SIZE(1) heap_alloc(size_t len)
index 8618819..8e76454 100644 (file)
@@ -31,20 +31,25 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
 
 #include "Cs.rc"
 #include "Da.rc"
-#include "De.rc"
 #include "El.rc"
 #include "En.rc"
-#include "Fr.rc"
 #include "Fi.rc"
 #include "Hu.rc"
 #include "Ko.rc"
-#include "Lt.rc"
 #include "Nl.rc"
 #include "No.rc"
 #include "Pl.rc"
 #include "Pt.rc"
-#include "Ru.rc"
-#include "Si.rc"
 #include "Sv.rc"
 #include "Tr.rc"
+
+/* UTF-8 */
+#include "De.rc"
+#include "Es.rc"
+#include "Fr.rc"
+#include "Lt.rc"
+#include "Ru.rc"
+#include "Si.rc"
+#include "Uk.rc"
 #include "Zh.rc"
+
diff --git a/reactos/dll/win32/hhctrl.ocx/index.c b/reactos/dll/win32/hhctrl.ocx/index.c
new file mode 100644 (file)
index 0000000..e9385c3
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2007 Jacek Caban for CodeWeavers
+ * Copyright 2010 Erich Hoover
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include "hhctrl.h"
+#include "stream.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
+
+/* Fill the TreeView object corresponding to the Index items */
+static void fill_index_tree(HWND hwnd, IndexItem *item)
+{
+    int index = 0;
+    LVITEMW lvi;
+
+    while(item) {
+        TRACE("tree debug: %s\n", debugstr_w(item->keyword));
+
+        if(!item->keyword)
+        {
+            FIXME("HTML Help index item has no keyword.\n");
+            item = item->next;
+            continue;
+        }
+        memset(&lvi, 0, sizeof(lvi));
+        lvi.iItem = index++;
+        lvi.mask = LVIF_TEXT|LVIF_PARAM|LVIF_INDENT;
+        lvi.iIndent = item->indentLevel;
+        lvi.cchTextMax = strlenW(item->keyword)+1;
+        lvi.pszText = item->keyword;
+        lvi.lParam = (LPARAM)item;
+        item->id = (HTREEITEM)SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
+        item = item->next;
+    }
+}
+
+/* Parse the attributes correspond to a list item, including sub-topics.
+ *
+ * Each list item has, at minimum, a param of type "keyword" and two
+ * parameters corresponding to a "sub-topic."  For each sub-topic there
+ * must be a "name" param and a "local" param, if there is only one
+ * sub-topic then there isn't really a sub-topic, the index will jump
+ * directly to the requested item.
+ */
+static void parse_index_obj_node_param(IndexItem *item, const char *text)
+{
+    const char *ptr;
+    LPWSTR *param;
+    int len, wlen;
+
+    ptr = get_attr(text, "name", &len);
+    if(!ptr) {
+        WARN("name attr not found\n");
+        return;
+    }
+
+    /* Allocate a new sub-item, either on the first run or whenever a
+     * sub-topic has filled out both the "name" and "local" params.
+     */
+    if(item->itemFlags == 0x11 && (!strncasecmp("name", ptr, len) || !strncasecmp("local", ptr, len))) {
+        item->nItems++;
+        item->items = heap_realloc(item->items, sizeof(IndexSubItem)*item->nItems);
+        item->items[item->nItems-1].name = NULL;
+        item->items[item->nItems-1].local = NULL;
+        item->itemFlags = 0x00;
+    }
+    if(!strncasecmp("keyword", ptr, len)) {
+        param = &item->keyword;
+    }else if(!item->keyword && !strncasecmp("name", ptr, len)) {
+        /* Some HTML Help index files use an additional "name" parameter
+         * rather than the "keyword" parameter.  In this case, the first
+         * occurance of the "name" parameter is the keyword.
+         */
+        param = &item->keyword;
+    }else if(!strncasecmp("name", ptr, len)) {
+        item->itemFlags |= 0x01;
+        param = &item->items[item->nItems-1].name;
+    }else if(!strncasecmp("local", ptr, len)) {
+        item->itemFlags |= 0x10;
+        param = &item->items[item->nItems-1].local;
+    }else {
+        WARN("unhandled param %s\n", debugstr_an(ptr, len));
+        return;
+    }
+
+    ptr = get_attr(text, "value", &len);
+    if(!ptr) {
+        WARN("value attr not found\n");
+        return;
+    }
+
+    wlen = MultiByteToWideChar(CP_ACP, 0, ptr, len, NULL, 0);
+    *param = heap_alloc((wlen+1)*sizeof(WCHAR));
+    MultiByteToWideChar(CP_ACP, 0, ptr, len, *param, wlen);
+    (*param)[wlen] = 0;
+}
+
+/* Parse the object tag corresponding to a list item.
+ *
+ * At this step we look for all of the "param" child tags, using this information
+ * to build up the information about the list item.  When we reach the </object>
+ * tag we know that we've finished parsing this list item.
+ */
+static IndexItem *parse_index_sitemap_object(HHInfo *info, stream_t *stream)
+{
+    strbuf_t node, node_name;
+    IndexItem *item;
+
+    strbuf_init(&node);
+    strbuf_init(&node_name);
+
+    item = heap_alloc_zero(sizeof(IndexItem));
+    item->nItems = 0;
+    item->items = heap_alloc_zero(0);
+    item->itemFlags = 0x11;
+
+    while(next_node(stream, &node)) {
+        get_node_name(&node, &node_name);
+
+        TRACE("%s\n", node.buf);
+
+        if(!strcasecmp(node_name.buf, "param")) {
+            parse_index_obj_node_param(item, node.buf);
+        }else if(!strcasecmp(node_name.buf, "/object")) {
+            break;
+        }else {
+            WARN("Unhandled tag! %s\n", node_name.buf);
+        }
+
+        strbuf_zero(&node);
+    }
+
+    strbuf_free(&node);
+    strbuf_free(&node_name);
+
+    return item;
+}
+
+/* Parse the HTML list item node corresponding to a specific help entry.
+ *
+ * At this stage we look for the only child tag we expect to find under
+ * the list item: the <OBJECT> tag.  We also only expect to find object
+ * tags with the "type" attribute set to "text/sitemap".
+ */
+static IndexItem *parse_li(HHInfo *info, stream_t *stream)
+{
+    strbuf_t node, node_name;
+    IndexItem *ret = NULL;
+
+    strbuf_init(&node);
+    strbuf_init(&node_name);
+
+    while(next_node(stream, &node)) {
+        get_node_name(&node, &node_name);
+
+        TRACE("%s\n", node.buf);
+
+        if(!strcasecmp(node_name.buf, "object")) {
+            const char *ptr;
+            int len;
+
+            static const char sz_text_sitemap[] = "text/sitemap";
+
+            ptr = get_attr(node.buf, "type", &len);
+
+            if(ptr && len == sizeof(sz_text_sitemap)-1
+               && !memcmp(ptr, sz_text_sitemap, len)) {
+                ret = parse_index_sitemap_object(info, stream);
+                break;
+            }
+        }else {
+            WARN("Unhandled tag! %s\n", node_name.buf);
+        }
+
+        strbuf_zero(&node);
+    }
+
+    strbuf_free(&node);
+    strbuf_free(&node_name);
+
+    return ret;
+}
+
+/* Parse the HTML Help page corresponding to all of the Index items.
+ *
+ * At this high-level stage we locate out each HTML list item tag.
+ * Since there is no end-tag for the <LI> item, we must hope that
+ * the <LI> entry is parsed correctly or tags might get lost.
+ *
+ * Within each entry it is also possible to encounter an additional
+ * <UL> tag.  When this occurs the tag indicates that the topics
+ * contained within it are related to the parent <LI> topic and
+ * should be inset by an indent.
+ */
+static void parse_hhindex(HHInfo *info, IStream *str, IndexItem *item)
+{
+    stream_t stream;
+    strbuf_t node, node_name;
+    int indent_level = -1;
+
+    strbuf_init(&node);
+    strbuf_init(&node_name);
+
+    stream_init(&stream, str);
+
+    while(next_node(&stream, &node)) {
+        get_node_name(&node, &node_name);
+
+        TRACE("%s\n", node.buf);
+
+        if(!strcasecmp(node_name.buf, "li")) {
+            item->next = parse_li(info, &stream);
+            item->next->merge = item->merge;
+            item = item->next;
+            item->indentLevel = indent_level;
+        }else if(!strcasecmp(node_name.buf, "ul")) {
+            indent_level++;
+        }else if(!strcasecmp(node_name.buf, "/ul")) {
+            indent_level--;
+        }else {
+            WARN("Unhandled tag! %s\n", node_name.buf);
+        }
+
+        strbuf_zero(&node);
+    }
+
+    strbuf_free(&node);
+    strbuf_free(&node_name);
+}
+
+/* Initialize the HTML Help Index tab */
+void InitIndex(HHInfo *info)
+{
+    IStream *stream;
+
+    info->index = heap_alloc_zero(sizeof(IndexItem));
+    info->index->nItems = 0;
+    SetChmPath(&info->index->merge, info->pCHMInfo->szFile, info->WinType.pszIndex);
+
+    stream = GetChmStream(info->pCHMInfo, info->pCHMInfo->szFile, &info->index->merge);
+    if(!stream) {
+        TRACE("Could not get index stream\n");
+        return;
+    }
+
+    parse_hhindex(info, stream, info->index);
+    IStream_Release(stream);
+
+    fill_index_tree(info->tabs[TAB_INDEX].hwnd, info->index->next);
+}
+
+/* Free all of the Index items, including all of the "sub-items" that
+ * correspond to different sub-topics.
+ */
+void ReleaseIndex(HHInfo *info)
+{
+    IndexItem *item = info->index, *next;
+    int i;
+
+    /* Note: item->merge is identical for all items, only free once */
+    heap_free(item->merge.chm_file);
+    heap_free(item->merge.chm_index);
+    while(item) {
+        next = item->next;
+
+        heap_free(item->keyword);
+        for(i=0;i<item->nItems;i++) {
+            heap_free(item->items[i].name);
+            heap_free(item->items[i].local);
+        }
+        heap_free(item->items);
+
+        item = next;
+    }
+}
diff --git a/reactos/dll/win32/hhctrl.ocx/search.c b/reactos/dll/win32/hhctrl.ocx/search.c
new file mode 100644 (file)
index 0000000..f81e46c
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2010 Erich Hoover
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include "hhctrl.h"
+#include "stream.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
+
+static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
+                                    const WCHAR *folder, const char *needle);
+
+/* Allocate a ListView entry for a search result. */
+static SearchItem *alloc_search_item(WCHAR *title, const WCHAR *filename)
+{
+    int filename_len = filename ? (strlenW(filename)+1)*sizeof(WCHAR) : 0;
+    SearchItem *item;
+
+    item = heap_alloc_zero(sizeof(SearchItem));
+    if(filename)
+    {
+        item->filename = heap_alloc(filename_len);
+        memcpy(item->filename, filename, filename_len);
+    }
+    item->title = title; /* Already allocated */
+
+    return item;
+}
+
+/* Fill the ListView object corresponding to the found Search tab items */
+static void fill_search_tree(HWND hwndList, SearchItem *item)
+{
+    int index = 0;
+    LVITEMW lvi;
+
+    SendMessageW(hwndList, LVM_DELETEALLITEMS, 0, 0);
+    while(item) {
+        TRACE("list debug: %s\n", debugstr_w(item->filename));
+
+        memset(&lvi, 0, sizeof(lvi));
+        lvi.iItem = index++;
+        lvi.mask = LVIF_TEXT|LVIF_PARAM;
+        lvi.cchTextMax = strlenW(item->title)+1;
+        lvi.pszText = item->title;
+        lvi.lParam = (LPARAM)item;
+        item->id = (HTREEITEM)SendMessageW(hwndList, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
+        item = item->next;
+    }
+}
+
+/* Search the CHM storage stream (an HTML file) for the requested text.
+ *
+ * Before searching the HTML file all HTML tags are removed so that only
+ * the content of the document is scanned.  If the search string is found
+ * then the title of the document is returned.
+ */
+static WCHAR *SearchCHM_File(IStorage *pStorage, const WCHAR *file, const char *needle)
+{
+    char *buffer = heap_alloc(BLOCK_SIZE);
+    strbuf_t content, node, node_name;
+    IStream *temp_stream = NULL;
+    DWORD i, buffer_size = 0;
+    WCHAR *title = NULL;
+    BOOL found = FALSE;
+    stream_t stream;
+    HRESULT hres;
+
+    hres = IStorage_OpenStream(pStorage, file, NULL, STGM_READ, 0, &temp_stream);
+    if(FAILED(hres)) {
+        FIXME("Could not open '%s' stream: %08x\n", debugstr_w(file), hres);
+        goto cleanup;
+    }
+
+    strbuf_init(&node);
+    strbuf_init(&content);
+    strbuf_init(&node_name);
+
+    stream_init(&stream, temp_stream);
+
+    /* Remove all HTML formatting and record the title */
+    while(next_node(&stream, &node)) {
+        get_node_name(&node, &node_name);
+
+        if(next_content(&stream, &content) && content.len > 1)
+        {
+            char *text = &content.buf[1];
+            int textlen = content.len-1;
+
+            if(!strcasecmp(node_name.buf, "title"))
+            {
+                int wlen = MultiByteToWideChar(CP_ACP, 0, text, textlen, NULL, 0);
+                title = heap_alloc((wlen+1)*sizeof(WCHAR));
+                MultiByteToWideChar(CP_ACP, 0, text, textlen, title, wlen);
+                title[wlen] = 0;
+            }
+
+            buffer = heap_realloc(buffer, buffer_size + textlen + 1);
+            memcpy(&buffer[buffer_size], text, textlen);
+            buffer[buffer_size + textlen] = '\0';
+            buffer_size += textlen;
+        }
+
+        strbuf_zero(&node);
+        strbuf_zero(&content);
+    }
+
+    /* Convert the buffer to lower case for comparison against the
+     * requested text (already in lower case).
+     */
+    for(i=0;i<buffer_size;i++)
+        buffer[i] = tolower(buffer[i]);
+
+    /* Search the decoded buffer for the requested text */
+    if(strstr(buffer, needle))
+        found = TRUE;
+
+    strbuf_free(&node);
+    strbuf_free(&content);
+    strbuf_free(&node_name);
+
+cleanup:
+    heap_free(buffer);
+    if(temp_stream)
+        IStream_Release(temp_stream);
+    if(!found)
+    {
+        heap_free(title);
+        return NULL;
+    }
+    return title;
+}
+
+/* Search all children of a CHM storage object for the requested text and
+ * return the last found search item.
+ */
+static SearchItem *SearchCHM_Storage(SearchItem *item, IStorage *pStorage,
+                                     const char *needle)
+{
+    const WCHAR szHTMext[] = {'.','h','t','m',0};
+    IEnumSTATSTG *elem = NULL;
+    WCHAR *filename = NULL;
+    STATSTG entries;
+    HRESULT hres;
+    ULONG retr;
+
+    hres = IStorage_EnumElements(pStorage, 0, NULL, 0, &elem);
+    if(hres != S_OK)
+    {
+        FIXME("Could not enumerate '/' storage elements: %08x\n", hres);
+        return NULL;
+    }
+    while (IEnumSTATSTG_Next(elem, 1, &entries, &retr) == NOERROR)
+    {
+        switch(entries.type) {
+        case STGTY_STORAGE:
+            item = SearchCHM_Folder(item, pStorage, entries.pwcsName, needle);
+            break;
+        case STGTY_STREAM:
+            filename = entries.pwcsName;
+            while(strchrW(filename, '/'))
+                filename = strchrW(filename, '/')+1;
+            if(strstrW(filename, szHTMext))
+            {
+                WCHAR *title = SearchCHM_File(pStorage, filename, needle);
+
+                if(title)
+                {
+                    item->next = alloc_search_item(title, entries.pwcsName);
+                    item = item->next;
+                }
+            }
+            break;
+        default:
+            FIXME("Unhandled IStorage stream element.\n");
+        }
+    }
+    return item;
+}
+
+/* Open a CHM storage object (folder) by name and find all items with
+ * the requested text.  The last found item is returned.
+ */
+static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
+                                    const WCHAR *folder, const char *needle)
+{
+    IStorage *temp_storage = NULL;
+    HRESULT hres;
+
+    hres = IStorage_OpenStorage(pStorage, folder, NULL, STGM_READ, NULL, 0, &temp_storage);
+    if(FAILED(hres))
+    {
+        FIXME("Could not open '%s' storage object: %08x\n", debugstr_w(folder), hres);
+        return NULL;
+    }
+    item = SearchCHM_Storage(item, temp_storage, needle);
+
+    IStorage_Release(temp_storage);
+    return item;
+}
+
+/* Search the entire CHM file for the requested text and add all of
+ * the found items to a ListView for the user to choose the item
+ * they want.
+ */
+void InitSearch(HHInfo *info, const char *needle)
+{
+    CHMInfo *chm = info->pCHMInfo;
+    SearchItem *root_item = alloc_search_item(NULL, NULL);
+
+    SearchCHM_Storage(root_item, chm->pStorage, needle);
+    fill_search_tree(info->search.hwndList, root_item->next);
+    if(info->search.root)
+        ReleaseSearch(info);
+    info->search.root = root_item;
+}
+
+/* Free all of the found Search items. */
+void ReleaseSearch(HHInfo *info)
+{
+    SearchItem *item = info->search.root;
+
+    info->search.root = NULL;
+    while(item) {
+        heap_free(item->filename);
+        item = item->next;
+    }
+}
diff --git a/reactos/dll/win32/hhctrl.ocx/stream.c b/reactos/dll/win32/hhctrl.ocx/stream.c
new file mode 100644 (file)
index 0000000..317eeeb
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2007 Jacek Caban for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "hhctrl.h"
+#include "stream.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
+
+void strbuf_init(strbuf_t *buf)
+{
+    buf->size = 8;
+    buf->len = 0;
+    buf->buf = heap_alloc(buf->size);
+}
+
+void strbuf_zero(strbuf_t *buf)
+{
+    buf->len = 0;
+}
+
+void strbuf_free(strbuf_t *buf)
+{
+    heap_free(buf->buf);
+}
+
+void strbuf_append(strbuf_t *buf, const char *data, int len)
+{
+    if(buf->len+len > buf->size) {
+        buf->size = buf->len+len;
+        buf->buf = heap_realloc(buf->buf, buf->size);
+    }
+
+    memcpy(buf->buf+buf->len, data, len);
+    buf->len += len;
+}
+
+void stream_init(stream_t *stream, IStream *str)
+{
+    memset(stream, 0, sizeof(stream_t));
+    stream->str = str;
+}
+
+BOOL stream_chr(stream_t *stream, strbuf_t *buf, char c)
+{
+    BOOL b = TRUE;
+    ULONG i;
+
+    while(b) {
+        for(i=stream->p; i<stream->size; i++) {
+            if(stream->buf[i] == c) {
+                b = FALSE;
+                break;
+            }
+        }
+
+        if(buf && i > stream->p)
+            strbuf_append(buf, stream->buf+stream->p, i-stream->p);
+        stream->p = i;
+
+        if(stream->p == stream->size) {
+            stream->p = 0;
+            IStream_Read(stream->str, stream->buf, sizeof(stream->buf), &stream->size);
+            if(!stream->size)
+                break;
+        }
+    }
+
+    return stream->size != 0;
+}
+
+void get_node_name(strbuf_t *node, strbuf_t *name)
+{
+    const char *ptr = node->buf+1;
+
+    strbuf_zero(name);
+
+    while(*ptr != '>' && !isspace(*ptr))
+        ptr++;
+
+    strbuf_append(name, node->buf+1, ptr-node->buf-1);
+    strbuf_append(name, "", 1);
+}
+
+/* Return the stream content up to the next HTML tag.
+ *
+ * Note: the first returned character is the end of the last tag (>).
+ */
+BOOL next_content(stream_t *stream, strbuf_t *buf)
+{
+    if(!stream_chr(stream, buf, '<'))
+        return FALSE;
+
+    return TRUE;
+}
+
+BOOL next_node(stream_t *stream, strbuf_t *buf)
+{
+    if(!stream_chr(stream, NULL, '<'))
+        return FALSE;
+
+    if(!stream_chr(stream, buf, '>'))
+        return FALSE;
+
+    strbuf_append(buf, ">", 2);
+
+    return TRUE;
+}
+
+/*
+ * Find the value of a named HTML attribute.
+ *
+ * Note: Attribute names are case insensitive, so it is necessary to
+ * put both the node text and the attribute name in the same case
+ * before attempting a string search.
+ */
+const char *get_attr(const char *node, const char *name, int *len)
+{
+    const char *ptr, *ptr2;
+    int name_len, node_len;
+    char name_buf[32];
+    char *node_buf;
+    int i;
+
+    /* Create a lower case copy of the node */
+    node_len = strlen(node)+1;
+    node_buf = heap_alloc(node_len*sizeof(char));
+    if(!node_buf)
+        return NULL;
+    memcpy(node_buf, node, node_len);
+    for(i=0;i<node_len;i++)
+        node_buf[i] = tolower(node_buf[i]);
+    /* Create a lower case copy of the attribute name (search string) */
+    name_len = strlen(name);
+    memcpy(name_buf, name, name_len);
+    for(i=0;i<name_len;i++)
+        name_buf[i] = tolower(name_buf[i]);
+    name_buf[name_len++] = '=';
+    name_buf[name_len++] = '\"';
+    name_buf[name_len] = 0;
+
+    ptr = strstr(node_buf, name_buf);
+    if(!ptr) {
+        WARN("name not found\n");
+        heap_free(node_buf);
+        return NULL;
+    }
+
+    ptr += name_len;
+    ptr2 = strchr(ptr, '\"');
+    if(!ptr2)
+    {
+        heap_free(node_buf);
+        return NULL;
+    }
+
+    *len = ptr2-ptr;
+    /* Return the pointer offset within the original string */
+    ptr = node+(ptr-node_buf);
+
+    heap_free(node_buf);
+    return ptr;
+}
diff --git a/reactos/dll/win32/hhctrl.ocx/stream.h b/reactos/dll/win32/hhctrl.ocx/stream.h
new file mode 100644 (file)
index 0000000..a03fd46
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2007 Jacek Caban for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef HHCTRL_STREAM_H
+#define HHCTRL_STREAM_H
+
+#define BLOCK_SIZE 0x1000
+
+typedef struct {
+    char *buf;
+    int size;
+    int len;
+} strbuf_t;
+
+typedef struct {
+    IStream *str;
+    char buf[BLOCK_SIZE];
+    ULONG size;
+    ULONG p;
+} stream_t;
+
+void strbuf_init(strbuf_t *buf);
+void strbuf_zero(strbuf_t *buf);
+void strbuf_free(strbuf_t *buf);
+void strbuf_append(strbuf_t *buf, const char *data, int len);
+void stream_init(stream_t *stream, IStream *str);
+BOOL stream_chr(stream_t *stream, strbuf_t *buf, char c);
+void get_node_name(strbuf_t *node, strbuf_t *name);
+BOOL next_content(stream_t *stream, strbuf_t *buf);
+BOOL next_node(stream_t *stream, strbuf_t *buf);
+const char *get_attr(const char *node, const char *name, int *len);
+
+#endif