From e29b5f5dfb5fc797c0777df44618b68fefa6e33a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 23 Apr 2008 21:37:51 +0200 Subject: [PATCH] winhelp: Start feeding the richedit control with relevant RTF stream. --- programs/winhelp/hlpfile.c | 158 ++++++++++++++++++++++++++++++++++--- programs/winhelp/hlpfile.h | 12 ++- programs/winhelp/winhelp.c | 76 +++++++++++++++--- 3 files changed, 220 insertions(+), 26 deletions(-) diff --git a/programs/winhelp/hlpfile.c b/programs/winhelp/hlpfile.c index 498136dd5e0..ac270c74a32 100644 --- a/programs/winhelp/hlpfile.c +++ b/programs/winhelp/hlpfile.c @@ -618,6 +618,58 @@ static BYTE* HLPFILE_DecompressGfx(BYTE* src, unsigned csz, unsigned sz, BYTE return dst; } +static BOOL HLPFILE_RtfAddRawString(struct RtfData* rd, const char* str, size_t sz) +{ + if (!rd) return TRUE; /* FIXME: TEMP */ + if (rd->ptr + sz >= rd->data + rd->allocated) + { + char* new = HeapReAlloc(GetProcessHeap(), 0, rd->data, rd->allocated *= 2); + if (!new) return FALSE; + rd->ptr = new + (rd->ptr - rd->data); + rd->data = new; + } + memcpy(rd->ptr, str, sz); + rd->ptr += sz; + + return TRUE; +} + +static BOOL HLPFILE_RtfAddControl(struct RtfData* rd, const char* str) +{ + if (!rd) return TRUE; /* FIXME: TEMP */ + if (*str == '\\' || *str == '{') rd->in_text = FALSE; + else if (*str == '}') rd->in_text = TRUE; + return HLPFILE_RtfAddRawString(rd, str, strlen(str)); +} + +static BOOL HLPFILE_RtfAddText(struct RtfData* rd, const char* str) +{ + const char* p; + const char* last; + const char* replace; + + if (!rd) return TRUE; /* FIXME: TEMP */ + if (!rd->in_text) + { + if (!HLPFILE_RtfAddRawString(rd, " ", 1)) return FALSE; + rd->in_text = TRUE; + } + for (last = p = str; *p; p++) + { + switch (*p) + { + case '{': replace = "\\{"; break; + case '}': replace = "\\}"; break; + case '\\': replace = "\\\\"; break; + default: continue; + } + if ((p != last && !HLPFILE_RtfAddRawString(rd, last, p - last)) || + !HLPFILE_RtfAddRawString(rd, replace, 2)) return FALSE; + last = p + 1; + } + return HLPFILE_RtfAddRawString(rd, last, p - last); +} + /****************************************************************** * HLPFILE_LoadBitmap * @@ -875,7 +927,7 @@ static HLPFILE_LINK* HLPFILE_AllocLink(int cookie, const char* str, LONG h * * HLPFILE_BrowseParagraph */ -static BOOL HLPFILE_BrowseParagraph(HLPFILE_PAGE* page, BYTE *buf, BYTE* end) +static BOOL HLPFILE_BrowseParagraph(HLPFILE_PAGE* page, struct RtfData* rd, BYTE *buf, BYTE* end) { HLPFILE_PARAGRAPH *paragraph, **paragraphptr; UINT textsize; @@ -885,6 +937,8 @@ static BOOL HLPFILE_BrowseParagraph(HLPFILE_PAGE* page, BYTE *buf, BYTE* end) unsigned short bits; unsigned nc, ncol = 1; BOOL in_table = FALSE; + char tmp[256]; + BOOL ret = FALSE; for (paragraphptr = &page->first_paragraph; *paragraphptr; paragraphptr = &(*paragraphptr)->next) /* Nothing */; @@ -976,11 +1030,11 @@ static BOOL HLPFILE_BrowseParagraph(HLPFILE_PAGE* page, BYTE *buf, BYTE* end) while (text < text_end && format < format_end) { WINE_TRACE("Got text: %s (%p/%p - %p/%p)\n", wine_dbgstr_a(text), text, text_end, format, format_end); - textsize = strlen(text) + 1; - if (textsize > 1) + textsize = strlen(text); + if (textsize) { paragraph = HeapAlloc(GetProcessHeap(), 0, - sizeof(HLPFILE_PARAGRAPH) + textsize); + sizeof(HLPFILE_PARAGRAPH) + textsize + 1); if (!paragraph) return FALSE; *paragraphptr = paragraph; paragraphptr = ¶graph->next; @@ -998,9 +1052,15 @@ static BOOL HLPFILE_BrowseParagraph(HLPFILE_PAGE* page, BYTE *buf, BYTE* end) attributes.wVSpace = 0; attributes.wHSpace = 0; + if (rd) /* FIXME: TEMP */ { + if (rd->force_color && !HLPFILE_RtfAddControl(rd, "{\\ul\\cf1 ")) goto done; + if (!HLPFILE_RtfAddText(rd, text)) goto done; + if (rd->force_color && !HLPFILE_RtfAddControl(rd, "}")) goto done; + rd->char_pos += textsize; + } } /* else: null text, keep on storing attributes */ - text += textsize; + text += textsize + 1; if (*format == 0xff) { @@ -1022,25 +1082,43 @@ static BOOL HLPFILE_BrowseParagraph(HLPFILE_PAGE* page, BYTE *buf, BYTE* end) break; case 0x80: - attributes.wFont = GET_USHORT(format, 1); - WINE_TRACE("Changing font to %d\n", attributes.wFont); - format += 3; - break; + { + unsigned font = GET_USHORT(format, 1); + attributes.wFont = font; + WINE_TRACE("Changing font to %d\n", attributes.wFont); + format += 3; + /* FIXME: missing at least colors, also bold attribute looses information */ + sprintf(tmp, "\\f%d\\cf%d\\fs%d%s%s%s%s", + font, font + 2, + -2 * page->file->fonts[font].LogFont.lfHeight, + page->file->fonts[font].LogFont.lfWeight > 400 ? "\\b" : "\\b0", + page->file->fonts[font].LogFont.lfItalic ? "\\i" : "\\i0", + page->file->fonts[font].LogFont.lfUnderline ? "\\ul" : "\\ul0", + page->file->fonts[font].LogFont.lfStrikeOut ? "\\strike" : "\\strike0"); + if (!HLPFILE_RtfAddControl(rd, tmp)) goto done; + } + break; case 0x81: + if (!HLPFILE_RtfAddControl(rd, "\\line")) goto done; attributes.wVSpace++; format += 1; + if (rd) /* FIXME: TEMP */ rd->char_pos++; break; case 0x82: + if (!HLPFILE_RtfAddControl(rd, "\\par\\pard")) goto done; attributes.wVSpace++; attributes.wIndent = 0; format += 1; + if (rd) /* FIXME: TEMP */ rd->char_pos++; break; case 0x83: + if (!HLPFILE_RtfAddControl(rd, "\\tab")) goto done; attributes.wIndent++; format += 1; + if (rd) /* FIXME: TEMP */ rd->char_pos++; break; #if 0 @@ -1206,20 +1284,74 @@ static BOOL HLPFILE_BrowseParagraph(HLPFILE_PAGE* page, BYTE *buf, BYTE* end) } } } + ret = TRUE; +done: HeapFree(GetProcessHeap(), 0, text_base); - return TRUE; + return ret; } /****************************************************************** * HLPFILE_BrowsePage * */ -BOOL HLPFILE_BrowsePage(HLPFILE_PAGE* page) +BOOL HLPFILE_BrowsePage(HLPFILE_PAGE* page, struct RtfData* rd) { HLPFILE *hlpfile = page->file; BYTE *buf, *end; DWORD ref = page->reference; unsigned index, old_index = -1, offset, count = 0; + char tmp[1024]; + + if (rd) { /* FIXME: TEMP */ + rd->in_text = TRUE; + rd->data = rd->ptr = HeapAlloc(GetProcessHeap(), 0, rd->allocated = 32768); + rd->char_pos = 0; + rd->force_color = FALSE; + } + + if (!HLPFILE_RtfAddControl(rd, "{\\rtf1\\ansi\\ansicpg1252\\deff0")) return FALSE; + /* generate font table */ + if (!HLPFILE_RtfAddControl(rd, "{\\fonttbl")) return FALSE; + for (index = 0; index < hlpfile->numFonts; index++) + { + const char* family; + switch (hlpfile->fonts[index].LogFont.lfPitchAndFamily & 0xF0) + { + case FF_MODERN: family = "modern"; break; + case FF_ROMAN: family = "roman"; break; + case FF_SWISS: family = "swiss"; break; + case FF_SCRIPT: family = "script"; break; + case FF_DECORATIVE: family = "decor"; break; + default: family = "nil"; break; + } + sprintf(tmp, "{\\f%d\\f%s\\fprq%d\\fcharset0 %s;}", + index, family, + hlpfile->fonts[index].LogFont.lfPitchAndFamily & 0x0F, + hlpfile->fonts[index].LogFont.lfFaceName); + if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE; + } + if (!HLPFILE_RtfAddControl(rd, "}")) return FALSE; + /* generate color table */ + if (!HLPFILE_RtfAddControl(rd, "{\\colortbl ;\\red0\\green128\\blue0;")) return FALSE; + for (index = 0; index < hlpfile->numFonts; index++) + { + const char* family; + switch (hlpfile->fonts[index].LogFont.lfPitchAndFamily & 0xF0) + { + case FF_MODERN: family = "modern"; break; + case FF_ROMAN: family = "roman"; break; + case FF_SWISS: family = "swiss"; break; + case FF_SCRIPT: family = "script"; break; + case FF_DECORATIVE: family = "decor"; break; + default: family = "nil"; break; + } + sprintf(tmp, "\\red%d\\green%d\\blue%d;", + GetRValue(hlpfile->fonts[index].color), + GetGValue(hlpfile->fonts[index].color), + GetBValue(hlpfile->fonts[index].color)); + if (!HLPFILE_RtfAddControl(rd, tmp)) return FALSE; + } + if (!HLPFILE_RtfAddControl(rd, "}")) return FALSE; do { @@ -1255,7 +1387,7 @@ BOOL HLPFILE_BrowsePage(HLPFILE_PAGE* page) case 0x01: case 0x20: case 0x23: - if (!HLPFILE_BrowseParagraph(page, buf, end)) return FALSE; + if (!HLPFILE_BrowseParagraph(page, rd, buf, end)) return FALSE; break; default: WINE_ERR("buf[0x14] = %x\n", buf[0x14]); @@ -1270,7 +1402,7 @@ BOOL HLPFILE_BrowsePage(HLPFILE_PAGE* page) ref = GET_UINT(buf, 0xc); } while (ref != 0xffffffff); done: - return TRUE; + return HLPFILE_RtfAddControl(rd, "}"); } /****************************************************************** diff --git a/programs/winhelp/hlpfile.h b/programs/winhelp/hlpfile.h index 548a6e207bd..f4e72d783f2 100644 --- a/programs/winhelp/hlpfile.h +++ b/programs/winhelp/hlpfile.h @@ -199,4 +199,14 @@ void HLPFILE_FreeHlpFile(HLPFILE*); void* HLPFILE_BPTreeSearch(BYTE*, const void*, HLPFILE_BPTreeCompare); void HLPFILE_BPTreeEnum(BYTE*, HLPFILE_BPTreeCallback cb, void *cookie); -BOOL HLPFILE_BrowsePage(HLPFILE_PAGE*); +struct RtfData { + BOOL in_text; + char* data; /* RTF stream start */ + char* ptr; /* current position in stream */ + unsigned allocated; /* overall allocated size */ + unsigned char_pos; /* current char position (in richedit) */ + char* where; /* pointer to feed back richedit */ + BOOL force_color; +}; + +BOOL HLPFILE_BrowsePage(HLPFILE_PAGE*, struct RtfData* rd); diff --git a/programs/winhelp/winhelp.c b/programs/winhelp/winhelp.c index 12ab5160d5a..ce57b6aeb7b 100644 --- a/programs/winhelp/winhelp.c +++ b/programs/winhelp/winhelp.c @@ -54,7 +54,7 @@ static void WINHELP_InitFonts(HWND hWnd); static void WINHELP_DeleteLines(WINHELP_WINDOW*); static void WINHELP_DeleteWindow(WINHELP_WINDOW*); static void WINHELP_DeleteButtons(WINHELP_WINDOW*); -static void WINHELP_SetupText(HWND hWnd, ULONG relative); +static void WINHELP_SetupText(HWND hWnd, WINHELP_WINDOW *win, ULONG relative); static WINHELP_LINE_PART* WINHELP_IsOverLink(WINHELP_WINDOW*, WPARAM, LPARAM); WINHELP_GLOBALS Globals = {3, NULL, TRUE, NULL, NULL, NULL, NULL, NULL, {{{NULL,NULL}},0}}; @@ -539,11 +539,12 @@ BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remembe BOOL bPrimary, bPopup, bReUsed = FALSE; LPSTR name; HICON hIcon; + HWND hTextWnd = NULL; bPrimary = !lstrcmpi(wpage->wininfo->name, "main"); bPopup = !bPrimary && (wpage->wininfo->win_style & WS_POPUP); - if (wpage->page && !wpage->page->first_paragraph) HLPFILE_BrowsePage(wpage->page); + if (wpage->page && !wpage->page->first_paragraph) HLPFILE_BrowsePage(wpage->page, NULL); if (!bPopup) { @@ -578,7 +579,8 @@ BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remembe win->page = wpage->page; win->info = wpage->wininfo; - WINHELP_SetupText(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), wpage->relative); + hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT); + WINHELP_SetupText(hTextWnd, win, wpage->relative); InvalidateRect(win->hMainWnd, NULL, TRUE); if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE); @@ -652,12 +654,12 @@ BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remembe 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_BUTTON, Globals.hInstance, NULL); if (!use_richedit) - CreateWindow(TEXT_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, win); + hTextWnd = CreateWindow(TEXT_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE, + 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, win); else - CreateWindow(RICHEDIT_CLASS, NULL, - ES_MULTILINE | ES_READONLY | WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE, - 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, NULL); + hTextWnd = CreateWindow(RICHEDIT_CLASS, NULL, + ES_MULTILINE | ES_READONLY | WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE, + 0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_TEXT, Globals.hInstance, NULL); } hIcon = (wpage->page) ? wpage->page->file->hIcon : NULL; @@ -678,7 +680,7 @@ BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remembe WINHELP_LayoutMainWindow(win); ShowWindow(win->hMainWnd, nCmdShow); - UpdateWindow(win->hMainWnd); + WINHELP_SetupText(hTextWnd, win, wpage->relative); return TRUE; } @@ -827,6 +829,53 @@ static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, return DefWindowProc(hWnd, msg, wParam, lParam); } +static DWORD CALLBACK WINHELP_RtfStreamIn(DWORD_PTR cookie, BYTE* buff, + LONG cb, LONG* pcb) +{ + struct RtfData* rd = (struct RtfData*)cookie; + + if (rd->where >= rd->ptr) return 1; + if (rd->where + cb > rd->ptr) + cb = rd->ptr - rd->where; + memcpy(buff, rd->where, cb); + rd->where += cb; + *pcb = cb; + return 0; +} + +static void WINHELP_FillRichEdit(HWND hTextWnd, WINHELP_WINDOW *win, ULONG relative) +{ + SendMessage(hTextWnd, WM_SETREDRAW, FALSE, 0); + SendMessage(hTextWnd, EM_SETBKGNDCOLOR, 0, (LPARAM)win->info->sr_color); + /* set word-wrap to window size (undocumented) */ + SendMessage(hTextWnd, EM_SETTARGETDEVICE, 0, 0); + if (win->page) + { + struct RtfData rd; + EDITSTREAM es; + + if (HLPFILE_BrowsePage(win->page, &rd)) + { + rd.where = rd.data; + es.dwCookie = (DWORD_PTR)&rd; + es.dwError = 0; + es.pfnCallback = WINHELP_RtfStreamIn; + + SendMessageW(hTextWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es); + } + /* FIXME: else leaking potentially the rd.first_link chain */ + HeapFree(GetProcessHeap(), 0, rd.data); + } + else + { + SendMessage(hTextWnd, WM_SETTEXT, 0, (LPARAM)""); + } + SendMessage(hTextWnd, WM_SETREDRAW, TRUE, 0); + SendMessage(hTextWnd, EM_SETSEL, 0, 0); + SendMessage(hTextWnd, EM_SCROLLCARET, 0, 0); + InvalidateRect(hTextWnd, NULL, TRUE); +} + /*********************************************************************** * * WINHELP_ButtonBoxWndProc @@ -1014,7 +1063,7 @@ static LRESULT CALLBACK WINHELP_TextWndProc(HWND hWnd, UINT msg, WPARAM wParam, case WM_WINDOWPOSCHANGED: winpos = (WINDOWPOS*) lParam; - if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd, 0); + if (!(winpos->flags & SWP_NOSIZE)) WINHELP_SetupText(hWnd, NULL, 0); break; case WM_MOUSEWHEEL: @@ -1355,12 +1404,15 @@ static LRESULT CALLBACK WINHELP_ShadowWndProc(HWND hWnd, UINT msg, WPARAM wParam * * SetupText */ -static void WINHELP_SetupText(HWND hWnd, ULONG relative) +static void WINHELP_SetupText(HWND hWnd, WINHELP_WINDOW* win, ULONG relative) { - HDC hDc = GetDC(hWnd); + HDC hDc; RECT rect; SIZE newsize; + if (!win) win = (WINHELP_WINDOW*) GetWindowLong(hWnd, 0); + if (use_richedit) return WINHELP_FillRichEdit(hWnd, win, relative); + hDc = GetDC(hWnd); ShowScrollBar(hWnd, SB_VERT, FALSE); if (!WINHELP_SplitLines(hWnd, NULL)) {