winhelp.c 56.3 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3
/*
 * Help Viewer
 *
4 5
 * Copyright    1996 Ulrich Schmid <uschmid@mail.hh.provi.de>
 *              2002 Sylvain Petreolle <spetreolle@yahoo.fr>
6
 *              2002, 2008 Eric Pouech <eric.pouech@wanadoo.fr>
7
 *              2004 Ken Belleau <jamez@ivic.qc.ca>
8
 *              2008 Kirill K. Smirnov <lich@math.spbu.ru>
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
22
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
23 24
 */

25
#include <assert.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
26
#include <stdio.h>
27
#include <string.h>
28
#include <stdarg.h>
29
#include <stdlib.h>
30

31 32
#define NONAMELESSUNION

33
#include "windef.h"
34
#include "winbase.h"
35
#include "wingdi.h"
Eric Pouech's avatar
Eric Pouech committed
36
#include "winuser.h"
37
#include "commdlg.h"
38
#include "winhelp.h"
39
#include "winhelp_res.h"
Eric Pouech's avatar
Eric Pouech committed
40
#include "shellapi.h"
41
#include "richedit.h"
42
#include "commctrl.h"
43

44 45 46 47
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(winhelp);

48
WINHELP_GLOBALS Globals = {3, NULL, TRUE, NULL, NULL, NULL, NULL, NULL, {{{NULL,NULL}},0}, NULL};
Alexandre Julliard's avatar
Alexandre Julliard committed
49

50 51
#define CTL_ID_BUTTON   0x700
#define CTL_ID_TEXT     0x701
52

53 54 55 56 57 58 59

/***********************************************************************
 *
 *           WINHELP_InitFonts
 */
static void WINHELP_InitFonts(HWND hWnd)
{
60 61 62 63 64 65 66 67 68
    WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
    LOGFONTW logfontlist[] = {
        {-10, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}},
        {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}},
        {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}},
        {-12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}},
        {-12, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}},
        {-10, 0, 0, 0, 700, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}},
        { -8, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 32, {'H','e','l','v',0}}};
69

70
    static HFONT fonts[ARRAY_SIZE(logfontlist)];
71
    static BOOL init = FALSE;
72

73
    win->fonts_len = ARRAY_SIZE(logfontlist);
74 75 76 77 78 79
    win->fonts = fonts;

    if (!init)
    {
        UINT i;

80
        for (i = 0; i < ARRAY_SIZE(logfontlist); i++)
81
	{
82
            fonts[i] = CreateFontIndirectW(&logfontlist[i]);
83 84
	}

85
        init = TRUE;
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
    }
}

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_SetupText(HWND hTextWnd, WINHELP_WINDOW* win, ULONG relative)
{
105
    static const WCHAR emptyW[1];
106
    /* At first clear area - needed by EM_POSFROMCHAR/EM_SETSCROLLPOS */
107 108 109
    SendMessageW(hTextWnd, WM_SETTEXT, 0, (LPARAM)emptyW);
    SendMessageW(hTextWnd, WM_SETREDRAW, FALSE, 0);
    SendMessageW(hTextWnd, EM_SETBKGNDCOLOR, 0, (LPARAM)win->info->sr_color);
110
    /* set word-wrap to window size (undocumented) */
111
    SendMessageW(hTextWnd, EM_SETTARGETDEVICE, 0, 0);
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
    if (win->page)
    {
        struct RtfData  rd;
        EDITSTREAM      es;
        unsigned        cp = 0;
        POINTL          ptl;
        POINT           pt;


        if (HLPFILE_BrowsePage(win->page, &rd, win->font_scale, relative))
        {
            rd.where = rd.data;
            es.dwCookie = (DWORD_PTR)&rd;
            es.dwError = 0;
            es.pfnCallback = WINHELP_RtfStreamIn;

            SendMessageW(hTextWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es);
            cp = rd.char_pos_rel;
        }
        /* FIXME: else leaking potentially the rd.first_link chain */
        HeapFree(GetProcessHeap(), 0, rd.data);
133
        SendMessageW(hTextWnd, EM_POSFROMCHAR, (WPARAM)&ptl, cp ? cp - 1 : 0);
134
        pt.x = 0; pt.y = ptl.y;
135
        SendMessageW(hTextWnd, EM_SETSCROLLPOS, 0, (LPARAM)&pt);
136
    }
137
    SendMessageW(hTextWnd, WM_SETREDRAW, TRUE, 0);
138
    RedrawWindow(hTextWnd, NULL, NULL, RDW_FRAME|RDW_INVALIDATE);
139 140
}

141 142 143 144 145 146
/***********************************************************************
 *
 *           WINHELP_GetOpenFileName
 */
BOOL WINHELP_GetOpenFileName(LPSTR lpszFile, int len)
{
147
    OPENFILENAMEA openfilename;
148 149 150 151 152 153
    CHAR szDir[MAX_PATH];
    CHAR szzFilter[2 * MAX_STRING_LEN + 100];
    LPSTR p = szzFilter;

    WINE_TRACE("()\n");

154
    LoadStringA(Globals.hInstance, STID_HELP_FILES_HLP, p, MAX_STRING_LEN);
155
    p += strlen(p) + 1;
156
    strcpy(p, "*.hlp");
157
    p += strlen(p) + 1;
158
    LoadStringA(Globals.hInstance, STID_ALL_FILES, p, MAX_STRING_LEN);
159
    p += strlen(p) + 1;
160
    strcpy(p, "*.*");
161 162 163
    p += strlen(p) + 1;
    *p = '\0';

164
    GetCurrentDirectoryA(sizeof(szDir), szDir);
165 166 167

    lpszFile[0]='\0';

168
    openfilename.lStructSize       = sizeof(openfilename);
169
    openfilename.hwndOwner         = (Globals.active_win ? Globals.active_win->hMainWnd : 0);
170 171 172 173 174 175 176 177 178 179 180
    openfilename.hInstance         = Globals.hInstance;
    openfilename.lpstrFilter       = szzFilter;
    openfilename.lpstrCustomFilter = 0;
    openfilename.nMaxCustFilter    = 0;
    openfilename.nFilterIndex      = 1;
    openfilename.lpstrFile         = lpszFile;
    openfilename.nMaxFile          = len;
    openfilename.lpstrFileTitle    = 0;
    openfilename.nMaxFileTitle     = 0;
    openfilename.lpstrInitialDir   = szDir;
    openfilename.lpstrTitle        = 0;
181
    openfilename.Flags             = OFN_ENABLESIZING | OFN_HIDEREADONLY | OFN_READONLY;
182 183 184 185 186 187 188
    openfilename.nFileOffset       = 0;
    openfilename.nFileExtension    = 0;
    openfilename.lpstrDefExt       = 0;
    openfilename.lCustData         = 0;
    openfilename.lpfnHook          = 0;
    openfilename.lpTemplateName    = 0;

189
    return GetOpenFileNameA(&openfilename);
190 191
}

192 193 194 195
/***********************************************************************
 *
 *           WINHELP_MessageBoxIDS_s
 */
196
static INT WINHELP_MessageBoxIDS_s(UINT ids_text, LPCSTR str, UINT ids_title, WORD type)
197
{
198 199 200
    CHAR text[MAX_STRING_LEN];
    CHAR newtext[MAX_STRING_LEN + MAX_PATH];

201 202
    LoadStringA(Globals.hInstance, ids_text, text, sizeof(text));
    wsprintfA(newtext, text, str);
203

204
    return MessageBoxA(0, newtext, MAKEINTRESOURCEA(ids_title), type);
205 206
}

207 208 209 210 211 212 213
/***********************************************************************
 *
 *           WINHELP_LookupHelpFile
 */
HLPFILE* WINHELP_LookupHelpFile(LPCSTR lpszFile)
{
    HLPFILE*        hlpfile;
214
    char szFullName[MAX_PATH];
215 216 217 218 219 220 221 222 223 224 225 226 227
    char szAddPath[MAX_PATH];
    char *p;

    /*
     * NOTE: This is needed by popup windows only.
     * In other cases it's not needed but does not hurt though.
     */
    if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
    {
        strcpy(szAddPath, Globals.active_win->page->file->lpszPath);
        p = strrchr(szAddPath, '\\');
        if (p) *p = 0;
    }
228

229 230 231
    /*
     * FIXME: Should we swap conditions?
     */
232 233
    if (!SearchPathA(NULL, lpszFile, ".hlp", MAX_PATH, szFullName, NULL) &&
        !SearchPathA(szAddPath, lpszFile, ".hlp", MAX_PATH, szFullName, NULL))
234
    {
235 236 237 238 239
        if (WINHELP_MessageBoxIDS_s(STID_FILE_NOT_FOUND_s, lpszFile, STID_WHERROR,
                                    MB_YESNO|MB_ICONQUESTION) != IDYES)
            return NULL;
        if (!WINHELP_GetOpenFileName(szFullName, MAX_PATH))
            return NULL;
240
    }
241
    hlpfile = HLPFILE_ReadHlpFile(szFullName);
242
    if (!hlpfile)
243 244
        WINHELP_MessageBoxIDS_s(STID_HLPFILE_ERROR_s, lpszFile,
                                STID_WHERROR, MB_OK|MB_ICONSTOP);
245 246 247 248 249 250 251 252 253 254 255
    return hlpfile;
}

/******************************************************************
 *		WINHELP_GetWindowInfo
 *
 *
 */
HLPFILE_WINDOWINFO*     WINHELP_GetWindowInfo(HLPFILE* hlpfile, LPCSTR name)
{
    static      HLPFILE_WINDOWINFO      mwi;
256
    unsigned int     i;
257 258

    if (!name || !name[0])
259
        name = Globals.active_win->info->name;
260

261 262
    if (hlpfile)
        for (i = 0; i < hlpfile->numWindows; i++)
263
            if (!lstrcmpiA(hlpfile->windows[i].name, name))
264
                return &hlpfile->windows[i];
265 266 267

    if (strcmp(name, "main") != 0)
    {
268
        WINE_FIXME("Couldn't find window info for %s\n", debugstr_a(name));
269 270 271 272 273 274 275
        assert(0);
        return NULL;
    }
    if (!mwi.name[0])
    {
        strcpy(mwi.type, "primary");
        strcpy(mwi.name, "main");
276
        if (hlpfile && hlpfile->lpszTitle[0])
277
        {
278
            char tmp[40];
279
            LoadStringA(Globals.hInstance, STID_WINE_HELP, tmp, sizeof(tmp));
280 281
            _snprintf(mwi.caption, sizeof(mwi.caption), "%s %s - %s",
                      hlpfile->lpszTitle, tmp, hlpfile->lpszPath);
282 283
        }
        else
284
            LoadStringA(Globals.hInstance, STID_WINE_HELP, mwi.caption, sizeof(mwi.caption));
285 286
        mwi.origin.x = mwi.origin.y = mwi.size.cx = mwi.size.cy = CW_USEDEFAULT;
        mwi.style = SW_SHOW;
287
        mwi.win_style = WS_OVERLAPPEDWINDOW;
288
        mwi.sr_color = mwi.nsr_color = 0xFFFFFF;
289 290 291 292 293 294 295 296 297
    }
    return &mwi;
}

/******************************************************************
 *		HLPFILE_GetPopupWindowInfo
 *
 *
 */
298
static HLPFILE_WINDOWINFO*     WINHELP_GetPopupWindowInfo(HLPFILE* hlpfile,
299
                                                          WINHELP_WINDOW* parent, LPARAM mouse)
300 301 302 303 304 305 306 307
{
    static      HLPFILE_WINDOWINFO      wi;

    RECT parent_rect;
    
    wi.type[0] = wi.name[0] = wi.caption[0] = '\0';

    /* Calculate horizontal size and position of a popup window */
308
    GetWindowRect(parent->hMainWnd, &parent_rect);
309
    wi.size.cx = (parent_rect.right  - parent_rect.left) / 2;
310
    wi.size.cy = 10; /* need a non null value, so that borders are taken into account while computing */
311

312 313
    wi.origin.x = (short)LOWORD(mouse);
    wi.origin.y = (short)HIWORD(mouse);
314
    ClientToScreen(parent->hMainWnd, &wi.origin);
315 316 317 318 319
    wi.origin.x -= wi.size.cx / 2;
    wi.origin.x  = min(wi.origin.x, GetSystemMetrics(SM_CXSCREEN) - wi.size.cx);
    wi.origin.x  = max(wi.origin.x, 0);

    wi.style = SW_SHOW;
320
    wi.win_style = WS_POPUP | WS_BORDER;
321 322 323 324
    if (parent->page->file->has_popup_color)
        wi.sr_color = parent->page->file->popup_color;
    else
        wi.sr_color = parent->info->sr_color;
325
    wi.nsr_color = 0xFFFFFF;
326 327 328

    return &wi;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
329

330 331 332 333 334 335 336 337 338 339
typedef struct
{
    WORD size;
    WORD command;
    LONG data;
    LONG reserved;
    WORD ofsFilename;
    WORD ofsData;
} WINHELP,*LPWINHELP;

340 341 342 343 344 345 346
static BOOL WINHELP_HasWorkingWindow(void)
{
    if (!Globals.active_win) return FALSE;
    if (Globals.active_win->next || Globals.win_list != Globals.active_win) return TRUE;
    return Globals.active_win->page != NULL && Globals.active_win->page->file != NULL;
}

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
/******************************************************************
 *		WINHELP_HandleCommand
 *
 *
 */
static LRESULT  WINHELP_HandleCommand(HWND hSrcWnd, LPARAM lParam)
{
    COPYDATASTRUCT*     cds = (COPYDATASTRUCT*)lParam;
    WINHELP*            wh;

    if (cds->dwData != 0xA1DE505)
    {
        WINE_FIXME("Wrong magic number (%08lx)\n", cds->dwData);
        return 0;
    }

363
    wh = cds->lpData;
364 365 366

    if (wh)
    {
367 368
        char*   ptr = (wh->ofsFilename) ? (LPSTR)wh + wh->ofsFilename : NULL;

369
        WINE_TRACE("Got[%u]: cmd=%u data=%08x fn=%s\n",
370
                   wh->size, wh->command, wh->data, debugstr_a(ptr));
371 372
        switch (wh->command)
        {
373 374 375 376 377
        case HELP_CONTEXT:
            if (ptr)
            {
                MACRO_JumpContext(ptr, "main", wh->data);
            }
378
            if (!WINHELP_HasWorkingWindow()) MACRO_Exit();
379
            break;
380 381 382
        case HELP_QUIT:
            MACRO_Exit();
            break;
383 384
        case HELP_CONTENTS:
            if (ptr)
385
            {
386
                MACRO_JumpContents(ptr, "main");
387
            }
388
            if (!WINHELP_HasWorkingWindow()) MACRO_Exit();
389
            break;
390 391
        case HELP_HELPONHELP:
            MACRO_HelpOn();
392
            if (!WINHELP_HasWorkingWindow()) MACRO_Exit();
393 394
            break;
        /* case HELP_SETINDEX: */
395
        case HELP_SETCONTENTS:
396 397 398 399 400
            if (ptr)
            {
                MACRO_SetContents(ptr, wh->data);
            }
            break;
401
        case HELP_CONTEXTPOPUP:
402 403 404 405 406 407 408 409 410
            if (ptr)
            {
                MACRO_PopupContext(ptr, wh->data);
            }
            break;
        /* case HELP_FORCEFILE:*/
        /* case HELP_CONTEXTMENU: */
        case HELP_FINDER:
            /* in fact, should be the topic dialog box */
411
            WINE_FIXME("HELP_FINDER: stub\n");
412 413 414 415 416 417 418 419 420 421 422 423
            if (ptr)
            {
                MACRO_JumpHash(ptr, "main", 0);
            }
            break;
        /* case HELP_WM_HELP: */
        /* case HELP_SETPOPUP_POS: */
        /* case HELP_KEY: */
        /* case HELP_COMMAND: */
        /* case HELP_PARTIALKEY: */
        /* case HELP_MULTIKEY: */
        /* case HELP_SETWINPOS: */
424 425
        default:
            WINE_FIXME("Unhandled command (%x) for remote winhelp control\n", wh->command);
426 427 428
            break;
        }
    }
429 430
    /* Always return success for now */
    return 1;
431 432
}

433 434 435
void            WINHELP_LayoutMainWindow(WINHELP_WINDOW* win)
{
    RECT        rect, button_box_rect;
436
    INT         text_top = 0;
437 438
    HWND        hButtonBoxWnd = GetDlgItem(win->hMainWnd, CTL_ID_BUTTON);
    HWND        hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
439 440 441 442

    GetClientRect(win->hMainWnd, &rect);

    /* Update button box and text Window */
443
    SetWindowPos(hButtonBoxWnd, HWND_TOP,
444 445 446 447
                 rect.left, rect.top,
                 rect.right - rect.left,
                 rect.bottom - rect.top, 0);

448
    if (GetWindowRect(hButtonBoxWnd, &button_box_rect))
449
        text_top = rect.top + button_box_rect.bottom - button_box_rect.top;
450

451
    SetWindowPos(hTextWnd, HWND_TOP,
452 453 454 455 456 457
                 rect.left, text_top,
                 rect.right - rect.left,
                 rect.bottom - text_top, 0);

}

458 459 460 461 462
/******************************************************************
 *		WINHELP_DeleteButtons
 *
 */
static void WINHELP_DeleteButtons(WINHELP_WINDOW* win)
463
{
464 465
    WINHELP_BUTTON*     b;
    WINHELP_BUTTON*     bp;
466

467
    for (b = win->first_button; b; b = bp)
468
    {
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
        DestroyWindow(b->hWnd);
        bp = b->next;
        HeapFree(GetProcessHeap(), 0, b);
    }
    win->first_button = NULL;
}

/******************************************************************
 *		WINHELP_DeleteBackSet
 *
 */
void WINHELP_DeleteBackSet(WINHELP_WINDOW* win)
{
    unsigned int i;

    for (i = 0; i < win->back.index; i++)
    {
        HLPFILE_FreeHlpFile(win->back.set[i].page->file);
        win->back.set[i].page = NULL;
    }
    win->back.index = 0;
}

/******************************************************************
 *             WINHELP_DeletePageLinks
 *
 */
static void WINHELP_DeletePageLinks(HLPFILE_PAGE* page)
{
    HLPFILE_LINK*       curr;
    HLPFILE_LINK*       next;

    for (curr = page->first_link; curr; curr = next)
    {
        next = curr->next;
        HeapFree(GetProcessHeap(), 0, curr);
    }
}

508 509 510 511 512 513 514 515 516 517 518 519 520
/***********************************************************************
 *
 *           WINHELP_GrabWindow
 */
WINHELP_WINDOW* WINHELP_GrabWindow(WINHELP_WINDOW* win)
{
    WINE_TRACE("Grab %p#%d++\n", win, win->ref_count);
    win->ref_count++;
    return win;
}

/***********************************************************************
 *
521
 *           WINHELP_ReleaseWindow
522 523 524 525 526 527 528 529 530 531 532 533 534
 */
BOOL WINHELP_ReleaseWindow(WINHELP_WINDOW* win)
{
    WINE_TRACE("Release %p#%d--\n", win, win->ref_count);

    if (!--win->ref_count)
    {
        DestroyWindow(win->hMainWnd);
        return FALSE;
    }
    return TRUE;
}

535 536 537 538 539 540 541
/***********************************************************************
 *
 *           WINHELP_DeleteWindow
 */
static void WINHELP_DeleteWindow(WINHELP_WINDOW* win)
{
    WINHELP_WINDOW**    w;
542
    BOOL bExit;
543
    HWND hTextWnd;
544 545 546 547 548 549 550 551 552

    for (w = &Globals.win_list; *w; w = &(*w)->next)
    {
        if (*w == win)
        {
            *w = win->next;
            break;
        }
    }
553
    bExit = (Globals.wVersion >= 4 && !lstrcmpiA(win->info->name, "main"));
554 555 556 557 558 559 560 561 562 563 564

    if (Globals.active_win == win)
    {
        Globals.active_win = Globals.win_list;
        if (Globals.win_list)
            SetActiveWindow(Globals.win_list->hMainWnd);
    }

    if (win == Globals.active_popup)
        Globals.active_popup = NULL;

565
    hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
566
    SetWindowLongPtrA(hTextWnd, GWLP_WNDPROC, (LONG_PTR)win->origRicheditWndProc);
567

568 569 570 571 572 573 574 575 576 577 578
    WINHELP_DeleteButtons(win);

    if (win->page) WINHELP_DeletePageLinks(win->page);
    if (win->hHistoryWnd) DestroyWindow(win->hHistoryWnd);

    DeleteObject(win->hBrush);

    WINHELP_DeleteBackSet(win);

    if (win->page) HLPFILE_FreeHlpFile(win->page->file);
    HeapFree(GetProcessHeap(), 0, win);
579 580 581 582

    if (bExit) MACRO_Exit();
    if (!Globals.win_list)
        PostQuitMessage(0);
583 584 585 586 587 588 589 590 591 592 593 594 595 596
}

static char* WINHELP_GetCaption(WINHELP_WNDPAGE* wpage)
{
    if (wpage->wininfo->caption[0]) return wpage->wininfo->caption;
    return wpage->page->file->lpszTitle;
}

static void WINHELP_RememberPage(WINHELP_WINDOW* win, WINHELP_WNDPAGE* wpage)
{
    unsigned        num;

    if (!Globals.history.index || Globals.history.set[0].page != wpage->page)
    {
597
        num = ARRAY_SIZE(Globals.history.set);
598 599 600 601 602 603
        /* we're full, remove latest entry */
        if (Globals.history.index == num)
        {
            HLPFILE_FreeHlpFile(Globals.history.set[num - 1].page->file);
            Globals.history.index--;
        }
604 605
        memmove(&Globals.history.set[1], &Globals.history.set[0],
                Globals.history.index * sizeof(Globals.history.set[0]));
606
        Globals.history.set[0] = *wpage;
607
        Globals.history.index++;
608
        wpage->page->file->wRefCount++;
609
    }
610
    if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
611

612
    num = ARRAY_SIZE(win->back.set);
613
    if (win->back.index == num)
614 615
    {
        /* we're full, remove latest entry */
616 617 618 619
        HLPFILE_FreeHlpFile(win->back.set[0].page->file);
        memmove(&win->back.set[0], &win->back.set[1],
                (num - 1) * sizeof(win->back.set[0]));
        win->back.index--;
620
    }
621 622
    win->back.set[win->back.index++] = *wpage;
    wpage->page->file->wRefCount++;
623 624
}

625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
/***********************************************************************
 *
 *           WINHELP_FindLink
 */
static HLPFILE_LINK* WINHELP_FindLink(WINHELP_WINDOW* win, LPARAM pos)
{
    HLPFILE_LINK*           link;
    POINTL                  mouse_ptl, char_ptl, char_next_ptl;
    DWORD                   cp;

    if (!win->page) return NULL;

    mouse_ptl.x = (short)LOWORD(pos);
    mouse_ptl.y = (short)HIWORD(pos);
    cp = SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_CHARFROMPOS,
                      0, (LPARAM)&mouse_ptl);

    for (link = win->page->first_link; link; link = link->next)
    {
        if (link->cpMin <= cp && cp <= link->cpMax)
        {
            /* check whether we're at end of line */
            SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR,
                         (LPARAM)&char_ptl, cp);
            SendMessageW(GetDlgItem(win->hMainWnd, CTL_ID_TEXT), EM_POSFROMCHAR,
                         (LPARAM)&char_next_ptl, cp + 1);
651 652 653 654 655 656 657 658 659 660
            if (link->bHotSpot)
            {
                HLPFILE_HOTSPOTLINK*    hslink = (HLPFILE_HOTSPOTLINK*)link;
                if ((mouse_ptl.x < char_ptl.x + hslink->x) ||
                    (mouse_ptl.x >= char_ptl.x + hslink->x + hslink->width) ||
                    (mouse_ptl.y < char_ptl.y + hslink->y) ||
                    (mouse_ptl.y >= char_ptl.y + hslink->y + hslink->height))
                    continue;
                break;
            }
661 662 663 664 665 666 667 668 669 670 671
            if (char_next_ptl.y != char_ptl.y || mouse_ptl.x >= char_next_ptl.x)
                link = NULL;
            break;
        }
    }
    return link;
}

static LRESULT CALLBACK WINHELP_RicheditWndProc(HWND hWnd, UINT msg,
                                                WPARAM wParam, LPARAM lParam)
{
672
    WINHELP_WINDOW *win = (WINHELP_WINDOW*) GetWindowLongPtrW(GetParent(hWnd), 0);
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
    DWORD messagePos;
    POINT pt;
    switch(msg)
    {
        case WM_SETCURSOR:
            messagePos = GetMessagePos();
            pt.x = (short)LOWORD(messagePos);
            pt.y = (short)HIWORD(messagePos);
            ScreenToClient(hWnd, &pt);
            if (win->page && WINHELP_FindLink(win, MAKELPARAM(pt.x, pt.y)))
            {
                SetCursor(win->hHandCur);
                return 0;
            }
            /* fall through */
        default:
            return CallWindowProcA(win->origRicheditWndProc, hWnd, msg, wParam, lParam);
    }
}

Alexandre Julliard's avatar
Alexandre Julliard committed
693 694 695 696
/***********************************************************************
 *
 *           WINHELP_CreateHelpWindow
 */
697
BOOL WINHELP_CreateHelpWindow(WINHELP_WNDPAGE* wpage, int nCmdShow, BOOL remember)
Alexandre Julliard's avatar
Alexandre Julliard committed
698
{
699 700
    WINHELP_WINDOW*     win = NULL;
    BOOL                bPrimary, bPopup, bReUsed = FALSE;
701
    HICON               hIcon;
702
    HWND                hTextWnd = NULL;
703

704
    bPrimary = !lstrcmpiA(wpage->wininfo->name, "main");
705
    bPopup = !bPrimary && (wpage->wininfo->win_style & WS_POPUP);
Alexandre Julliard's avatar
Alexandre Julliard committed
706

707 708 709 710
    if (!bPopup)
    {
        for (win = Globals.win_list; win; win = win->next)
        {
711
            if (!lstrcmpiA(win->info->name, wpage->wininfo->name))
712
            {
713 714 715 716 717 718 719 720
                if (win->page == wpage->page && win->info == wpage->wininfo)
                {
                    /* see #22979, some hlp files have a macro (run at page opening), which
                     * jumps to the very same page
                     * Exit gracefully in that case
                     */
                    return TRUE;
                }
721 722
                WINHELP_DeleteButtons(win);
                bReUsed = TRUE;
723
                SetWindowTextA(win->hMainWnd, WINHELP_GetCaption(wpage));
724
                if (win->info != wpage->wininfo)
725
                {
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
                    POINT   pt = {0, 0};
                    SIZE    sz = {0, 0};
                    DWORD   flags = SWP_NOSIZE | SWP_NOMOVE;

                    if (wpage->wininfo->origin.x != CW_USEDEFAULT &&
                        wpage->wininfo->origin.y != CW_USEDEFAULT)
                    {
                        pt = wpage->wininfo->origin;
                        flags &= ~SWP_NOSIZE;
                    }
                    if (wpage->wininfo->size.cx != CW_USEDEFAULT &&
                        wpage->wininfo->size.cy != CW_USEDEFAULT)
                    {
                        sz = wpage->wininfo->size;
                        flags &= ~SWP_NOMOVE;
                    }
                    SetWindowPos(win->hMainWnd, HWND_TOP, pt.x, pt.y, sz.cx, sz.cy, flags);
743 744
                }

745
                if (wpage->page && win->page && wpage->page->file != win->page->file)
746
                    WINHELP_DeleteBackSet(win);
747 748
                WINHELP_InitFonts(win->hMainWnd);

749 750
                win->page = wpage->page;
                win->info = wpage->wininfo;
751 752
                hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
                WINHELP_SetupText(hTextWnd, win, wpage->relative);
753

754 755
                InvalidateRect(win->hMainWnd, NULL, TRUE);
                if (win->hHistoryWnd) InvalidateRect(win->hHistoryWnd, NULL, TRUE);
756

757 758 759 760
                break;
            }
        }
    }
761

762 763 764
    if (!win)
    {
        /* Initialize WINHELP_WINDOW struct */
765
        win = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINHELP_WINDOW));
766 767 768 769 770
        if (!win) return FALSE;
        win->next = Globals.win_list;
        Globals.win_list = win;

        win->hHandCur = LoadCursorW(0, (LPWSTR)IDC_HAND);
771
        win->back.index = 0;
772
        win->font_scale = 1;
773
        WINHELP_GrabWindow(win);
774
    }
775 776
    win->page = wpage->page;
    win->info = wpage->wininfo;
777
    WINHELP_GrabWindow(win);
Alexandre Julliard's avatar
Alexandre Julliard committed
778

779
    if (!bPopup && wpage->page && remember)
780
    {
781
        WINHELP_RememberPage(win, wpage);
782
    }
783

784 785 786
    if (bPopup)
        Globals.active_popup = win;
    else
787
        Globals.active_win = win;
Alexandre Julliard's avatar
Alexandre Julliard committed
788

789
    /* Initialize default pushbuttons */
790
    if (bPrimary && wpage->page)
Alexandre Julliard's avatar
Alexandre Julliard committed
791
    {
792 793
        CHAR    buffer[MAX_STRING_LEN];

794
        LoadStringA(Globals.hInstance, STID_CONTENTS, buffer, sizeof(buffer));
795
        MACRO_CreateButton("BTN_CONTENTS", buffer, "Contents()");
796
        LoadStringA(Globals.hInstance, STID_INDEX, buffer, sizeof(buffer));
797
        MACRO_CreateButton("BTN_INDEX", buffer, "Finder()");
798
        LoadStringA(Globals.hInstance, STID_BACK, buffer, sizeof(buffer));
799
        MACRO_CreateButton("BTN_BACK", buffer, "Back()");
800
        if (win->back.index <= 1) MACRO_DisableButton("BTN_BACK");
Alexandre Julliard's avatar
Alexandre Julliard committed
801 802
    }

803
    if (!bReUsed)
804
    {
805
        win->hMainWnd = CreateWindowExA((bPopup) ? WS_EX_TOOLWINDOW : 0, MAIN_WIN_CLASS_NAME,
806
                                       WINHELP_GetCaption(wpage),
807 808 809
                                       bPrimary ? WS_OVERLAPPEDWINDOW : wpage->wininfo->win_style,
                                       wpage->wininfo->origin.x, wpage->wininfo->origin.y,
                                       wpage->wininfo->size.cx, wpage->wininfo->size.cy,
810
                                       bPopup ? Globals.active_win->hMainWnd : NULL,
811
                                       bPrimary ? LoadMenuW(Globals.hInstance, MAKEINTRESOURCEW(MAIN_MENU)) : 0,
812 813 814
                                       Globals.hInstance, win);
        if (!bPopup)
            /* Create button box and text Window */
815
            CreateWindowA(BUTTON_BOX_WIN_CLASS_NAME, "", WS_CHILD | WS_VISIBLE,
816 817
                         0, 0, 0, 0, win->hMainWnd, (HMENU)CTL_ID_BUTTON, Globals.hInstance, NULL);

818
        hTextWnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
819 820
                                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);
821 822 823
        SendMessageW(hTextWnd, EM_SETEVENTMASK, 0,
                    SendMessageW(hTextWnd, EM_GETEVENTMASK, 0, 0) | ENM_MOUSEEVENTS);
        win->origRicheditWndProc = (WNDPROC)SetWindowLongPtrA(hTextWnd, GWLP_WNDPROC,
824
                                                             (LONG_PTR)WINHELP_RicheditWndProc);
825
    }
826

827
    hIcon = (wpage->page) ? wpage->page->file->hIcon : NULL;
828
    if (!hIcon) hIcon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_WINHELP), IMAGE_ICON,
829
                                  GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
830
    SendMessageW(win->hMainWnd, WM_SETICON, ICON_SMALL, (DWORD_PTR)hIcon);
831

832 833 834 835 836
    /* Initialize file specific pushbuttons */
    if (!(wpage->wininfo->win_style & WS_POPUP) && wpage->page)
    {
        HLPFILE_MACRO  *macro;
        for (macro = wpage->page->file->first_macro; macro; macro = macro->next)
837
            MACRO_ExecuteMacro(win, macro->lpszMacro);
838 839

        for (macro = wpage->page->first_macro; macro; macro = macro->next)
840
            MACRO_ExecuteMacro(win, macro->lpszMacro);
841
    }
842 843 844 845 846
    /* See #17681, in some cases, the newly created window is closed by the macros it contains
     * (braindead), so deal with this case
     */
    for (win = Globals.win_list; win; win = win->next)
    {
847
        if (!lstrcmpiA(win->info->name, wpage->wininfo->name)) break;
848 849
    }
    if (!win || !WINHELP_ReleaseWindow(win)) return TRUE;
850

851
    if (bPopup)
852
    {
853
        DWORD   mask = SendMessageW(hTextWnd, EM_GETEVENTMASK, 0, 0);
854

855
        win->font_scale = Globals.active_win->font_scale;
856
        WINHELP_SetupText(hTextWnd, win, wpage->relative);
857

858 859
        /* we need the window to be shown for richedit to compute the size */
        ShowWindow(win->hMainWnd, nCmdShow);
860 861 862
        SendMessageW(hTextWnd, EM_SETEVENTMASK, 0, mask | ENM_REQUESTRESIZE);
        SendMessageW(hTextWnd, EM_REQUESTRESIZE, 0, 0);
        SendMessageW(hTextWnd, EM_SETEVENTMASK, 0, mask);
863 864 865 866 867 868 869
    }
    else
    {
        WINHELP_SetupText(hTextWnd, win, wpage->relative);
        WINHELP_LayoutMainWindow(win);
        ShowWindow(win->hMainWnd, nCmdShow);
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
870

871
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
872 873
}

874 875 876
/******************************************************************
 *             WINHELP_OpenHelpWindow
 * Main function to search for a page and display it in a window
877
 */
878 879 880
BOOL WINHELP_OpenHelpWindow(HLPFILE_PAGE* (*lookup)(HLPFILE*, LONG, ULONG*),
                            HLPFILE* hlpfile, LONG val, HLPFILE_WINDOWINFO* wi,
                            int nCmdShow)
881
{
882
    WINHELP_WNDPAGE     wpage;
883

884
    wpage.page = lookup(hlpfile, val, &wpage.relative);
885 886
    if (wpage.page) wpage.page->file->wRefCount++;
    wpage.wininfo = wi;
887
    return WINHELP_CreateHelpWindow(&wpage, nCmdShow, TRUE);
888 889
}

890 891 892 893
/******************************************************************
 *             WINHELP_HandleTextMouse
 *
 */
894
static BOOL WINHELP_HandleTextMouse(WINHELP_WINDOW* win, UINT msg, LPARAM lParam)
895 896 897 898 899
{
    HLPFILE*                hlpfile;
    HLPFILE_LINK*           link;
    BOOL                    ret = FALSE;

900
    switch (msg)
901
    {
902
    case WM_LBUTTONDOWN:
903
        if ((link = WINHELP_FindLink(win, lParam)))
904 905 906 907 908 909 910 911 912
        {
            HLPFILE_WINDOWINFO*     wi;

            switch (link->cookie)
            {
            case hlp_link_link:
                if ((hlpfile = WINHELP_LookupHelpFile(link->string)))
                {
                    if (link->window == -1)
913
                    {
914
                        wi = win->info;
915 916
                        if (wi->win_style & WS_POPUP) wi = Globals.active_win->info;
                    }
917
                    else if (link->window < hlpfile->numWindows)
918 919 920 921 922 923 924 925 926 927 928 929
                        wi = &hlpfile->windows[link->window];
                    else
                    {
                        WINE_WARN("link to window %d/%d\n", link->window, hlpfile->numWindows);
                        break;
                    }
                    WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash, wi, SW_NORMAL);
                }
                break;
            case hlp_link_popup:
                if ((hlpfile = WINHELP_LookupHelpFile(link->string)))
                    WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, link->hash,
930
                                           WINHELP_GetPopupWindowInfo(hlpfile, win, lParam),
931 932 933
                                           SW_NORMAL);
                break;
            case hlp_link_macro:
934
                MACRO_ExecuteMacro(win, link->string);
935 936 937 938 939 940 941 942 943 944 945
                break;
            default:
                WINE_FIXME("Unknown link cookie %d\n", link->cookie);
            }
            ret = TRUE;
        }
        break;
    }
    return ret;
}

946 947
/***********************************************************************
 *
948
 *           WINHELP_CheckPopup
949
 */
950
static BOOL WINHELP_CheckPopup(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT* lret)
951
{
952
    WINHELP_WINDOW*     popup;
953

954
    if (!Globals.active_popup) return FALSE;
955 956

    switch (msg)
Alexandre Julliard's avatar
Alexandre Julliard committed
957
    {
958 959 960 961
    case WM_NOTIFY:
        {
            MSGFILTER*  msgf = (MSGFILTER*)lParam;
            if (msgf->nmhdr.code == EN_MSGFILTER)
962
            {
963 964 965 966
                if (!WINHELP_CheckPopup(hWnd, msgf->msg, msgf->wParam, msgf->lParam, NULL))
                    return FALSE;
                if (lret) *lret = 1;
                return TRUE;
967
            }
968 969
        }
        break;
970
    case WM_ACTIVATE:
971
        if (LOWORD(wParam) != WA_INACTIVE || (HWND)lParam == Globals.active_win->hMainWnd ||
972 973 974
            (HWND)lParam == Globals.active_popup->hMainWnd ||
            GetWindow((HWND)lParam, GW_OWNER) == Globals.active_win->hMainWnd)
            break;
975
        /* fall through */
976
    case WM_LBUTTONDOWN:
977 978
        if (msg == WM_LBUTTONDOWN)
            WINHELP_HandleTextMouse(Globals.active_popup, msg, lParam);
979 980 981 982 983 984
        /* fall through */
    case WM_MBUTTONDOWN:
    case WM_RBUTTONDOWN:
    case WM_NCLBUTTONDOWN:
    case WM_NCMBUTTONDOWN:
    case WM_NCRBUTTONDOWN:
985
        popup = Globals.active_popup;
986
        Globals.active_popup = NULL;
987
        WINHELP_ReleaseWindow(popup);
988
        if (lret) *lret = 1;
989 990 991 992
        return TRUE;
    }
    return FALSE;
}
993

994 995 996 997 998 999 1000
/***********************************************************************
 *
 *           WINHELP_ButtonWndProc
 */
static LRESULT CALLBACK WINHELP_ButtonWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0;
1001

1002 1003
    if (msg == WM_KEYDOWN)
    {
1004 1005 1006 1007 1008 1009 1010
        switch (wParam)
        {
        case VK_UP:
        case VK_DOWN:
        case VK_PRIOR:
        case VK_NEXT:
        case VK_ESCAPE:
1011
            return SendMessageA(GetParent(hWnd), msg, wParam, lParam);
1012
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1013
    }
1014

1015
    return CallWindowProcA(Globals.button_proc, hWnd, msg, wParam, lParam);
1016 1017
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1018 1019 1020 1021
/***********************************************************************
 *
 *           WINHELP_ButtonBoxWndProc
 */
1022
static LRESULT CALLBACK WINHELP_ButtonBoxWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1023
{
1024 1025 1026 1027 1028
    WINDOWPOS      *winpos;
    WINHELP_WINDOW *win;
    WINHELP_BUTTON *button;
    SIZE button_size;
    INT  x, y;
Alexandre Julliard's avatar
Alexandre Julliard committed
1029

1030
    if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, NULL)) return 0L;
Alexandre Julliard's avatar
Alexandre Julliard committed
1031

1032
    switch (msg)
Alexandre Julliard's avatar
Alexandre Julliard committed
1033 1034
    {
    case WM_WINDOWPOSCHANGING:
1035
        winpos = (WINDOWPOS*) lParam;
1036
        win = (WINHELP_WINDOW*) GetWindowLongPtrW(GetParent(hWnd), 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
1037

1038 1039 1040 1041
        /* Update buttons */
        button_size.cx = 0;
        button_size.cy = 0;
        for (button = win->first_button; button; button = button->next)
Alexandre Julliard's avatar
Alexandre Julliard committed
1042
	{
1043 1044 1045
            HDC  hDc;
            SIZE textsize;
            if (!button->hWnd)
1046
            {
1047
                button->hWnd = CreateWindowA(STRING_BUTTON, button->lpszName,
1048 1049 1050 1051
                                            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                            0, 0, 0, 0,
                                            hWnd, (HMENU) button->wParam,
                                            Globals.hInstance, 0);
1052 1053
                if (button->hWnd)
                {
1054
                    if (Globals.button_proc == NULL)
1055 1056
                    {
                        NONCLIENTMETRICSW ncm;
1057
                        Globals.button_proc = (WNDPROC) GetWindowLongPtrA(button->hWnd, GWLP_WNDPROC);
1058 1059 1060 1061 1062 1063

                        ncm.cbSize = sizeof(NONCLIENTMETRICSW);
                        SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
                                              sizeof(NONCLIENTMETRICSW), &ncm, 0);
                        Globals.hButtonFont = CreateFontIndirectW(&ncm.lfMenuFont);
                    }
1064
                    SetWindowLongPtrA(button->hWnd, GWLP_WNDPROC, (LONG_PTR) WINHELP_ButtonWndProc);
1065
                    if (Globals.hButtonFont)
1066
                        SendMessageW(button->hWnd, WM_SETFONT, (WPARAM)Globals.hButtonFont, TRUE);
1067 1068
                }
            }
1069
            hDc = GetDC(button->hWnd);
1070
            GetTextExtentPointA(hDc, button->lpszName, strlen(button->lpszName), &textsize);
1071 1072 1073 1074
            ReleaseDC(button->hWnd, hDc);

            button_size.cx = max(button_size.cx, textsize.cx + BUTTON_CX);
            button_size.cy = max(button_size.cy, textsize.cy + BUTTON_CY);
Alexandre Julliard's avatar
Alexandre Julliard committed
1075 1076
	}

1077 1078 1079
        x = 0;
        y = 0;
        for (button = win->first_button; button; button = button->next)
Alexandre Julliard's avatar
Alexandre Julliard committed
1080
	{
1081
            SetWindowPos(button->hWnd, HWND_TOP, x, y, button_size.cx, button_size.cy, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
1082

1083 1084 1085 1086
            if (x + 2 * button_size.cx <= winpos->cx)
                x += button_size.cx;
            else
                x = 0, y += button_size.cy;
Alexandre Julliard's avatar
Alexandre Julliard committed
1087
	}
1088 1089
        winpos->cy = y + (x ? button_size.cy : 0);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1090 1091

    case WM_COMMAND:
1092
        SendMessageW(GetParent(hWnd), msg, wParam, lParam);
1093
        break;
1094 1095 1096 1097 1098 1099 1100 1101 1102

    case WM_KEYDOWN:
        switch (wParam)
        {
        case VK_UP:
        case VK_DOWN:
        case VK_PRIOR:
        case VK_NEXT:
        case VK_ESCAPE:
1103
            return SendMessageA(GetParent(hWnd), msg, wParam, lParam);
1104 1105
        }
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1106 1107
    }

1108
    return DefWindowProcA(hWnd, msg, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1109 1110
}

1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
/******************************************************************
 *		WINHELP_HistoryWndProc
 *
 *
 */
static LRESULT CALLBACK WINHELP_HistoryWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    WINHELP_WINDOW*     win;
    PAINTSTRUCT         ps;
    HDC                 hDc;
1121
    TEXTMETRICW         tm;
1122
    unsigned int        i;
1123 1124 1125 1126 1127
    RECT                r;

    switch (msg)
    {
    case WM_NCCREATE:
1128 1129
        win = (WINHELP_WINDOW*)((LPCREATESTRUCTA)lParam)->lpCreateParams;
        SetWindowLongPtrW(hWnd, 0, (ULONG_PTR)win);
1130 1131 1132 1133
        win->hHistoryWnd = hWnd;
        break;
    case WM_CREATE:
        hDc = GetDC(hWnd);
1134
        GetTextMetricsW(hDc, &tm);
1135 1136 1137
        GetWindowRect(hWnd, &r);

        r.right = r.left + 30 * tm.tmAveCharWidth;
1138
        r.bottom = r.top + ARRAY_SIZE(Globals.history.set) * tm.tmHeight;
1139
        AdjustWindowRect(&r, GetWindowLongW(hWnd, GWL_STYLE), FALSE);
1140 1141 1142 1143 1144 1145 1146 1147
        if (r.left < 0) {r.right -= r.left; r.left = 0;}
        if (r.top < 0) {r.bottom -= r.top; r.top = 0;}

        MoveWindow(hWnd, r.left, r.top, r.right, r.bottom, TRUE);
        ReleaseDC(hWnd, hDc);
        break;
    case WM_LBUTTONDOWN:
        hDc = GetDC(hWnd);
1148
        GetTextMetricsW(hDc, &tm);
1149
        i = HIWORD(lParam) / tm.tmHeight;
1150
        if (i < Globals.history.index)
1151
            WINHELP_CreateHelpWindow(&Globals.history.set[i], SW_SHOW, TRUE);
1152 1153 1154 1155
        ReleaseDC(hWnd, hDc);
        break;
    case WM_PAINT:
        hDc = BeginPaint(hWnd, &ps);
1156
        GetTextMetricsW(hDc, &tm);
1157

1158
        for (i = 0; i < Globals.history.index; i++)
1159
        {
1160 1161
            if (Globals.history.set[i].page->file == Globals.active_win->page->file)
            {
1162
                TextOutA(hDc, 0, i * tm.tmHeight,
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
                        Globals.history.set[i].page->lpszTitle,
                        strlen(Globals.history.set[i].page->lpszTitle));
            }
            else
            {
                char        buffer[1024];
                const char* ptr1;
                const char* ptr2;
                unsigned    len;

                ptr1 = strrchr(Globals.history.set[i].page->file->lpszPath, '\\');
                if (!ptr1) ptr1 = Globals.history.set[i].page->file->lpszPath;
                else ptr1++;
                ptr2 = strrchr(ptr1, '.');
                len = ptr2 ? ptr2 - ptr1 : strlen(ptr1);
                if (len > sizeof(buffer)) len = sizeof(buffer);
                memcpy(buffer, ptr1, len);
                if (len < sizeof(buffer)) buffer[len++] = ':';
1181
                lstrcpynA(&buffer[len], Globals.history.set[i].page->lpszTitle, sizeof(buffer) - len);
1182
                TextOutA(hDc, 0, i * tm.tmHeight, buffer, strlen(buffer));
1183
            }
1184 1185 1186 1187
        }
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
1188
        win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1189 1190 1191 1192
        if (hWnd == win->hHistoryWnd)
            win->hHistoryWnd = 0;
        break;
    }
1193
    return DefWindowProcA(hWnd, msg, wParam, lParam);
1194 1195
}

1196 1197 1198 1199
/**************************************************************************
 * cb_KWBTree
 *
 * HLPFILE_BPTreeCallback enumeration function for '|KWBTREE' internal file.
Alexandre Julliard's avatar
Alexandre Julliard committed
1200 1201
 *
 */
1202
static void cb_KWBTree(void *p, void **next, void *cookie)
Alexandre Julliard's avatar
Alexandre Julliard committed
1203
{
1204
    HWND hListWnd = cookie;
1205
    int count;
1206

1207
    WINE_TRACE("Adding %s to search list\n", debugstr_a((char *)p));
1208 1209 1210
    SendMessageA(hListWnd, LB_INSERTSTRING, -1, (LPARAM)p);
    count = SendMessageW(hListWnd, LB_GETCOUNT, 0, 0);
    SendMessageW(hListWnd, LB_SETITEMDATA, count-1, (LPARAM)p);
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224
    *next = (char*)p + strlen((char*)p) + 7;
}

struct index_data
{
    HLPFILE*    hlpfile;
    BOOL        jump;
    ULONG       offset;
};

/**************************************************************************
 * WINHELP_IndexDlgProc
 *
 */
1225
static INT_PTR CALLBACK WINHELP_IndexDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1226 1227 1228
{
    static struct index_data* id;
    int sel;
Alexandre Julliard's avatar
Alexandre Julliard committed
1229

1230
    switch (msg)
Alexandre Julliard's avatar
Alexandre Julliard committed
1231
    {
1232
    case WM_INITDIALOG:
1233
        id = (struct index_data*)((PROPSHEETPAGEA*)lParam)->lParam;
1234 1235 1236 1237 1238 1239 1240
        HLPFILE_BPTreeEnum(id->hlpfile->kwbtree, cb_KWBTree,
                           GetDlgItem(hWnd, IDC_INDEXLIST));
        id->jump = FALSE;
        id->offset = 1;
        return TRUE;
    case WM_COMMAND:
        switch (HIWORD(wParam))
1241
        {
1242 1243
        case LBN_DBLCLK:
            if (LOWORD(wParam) == IDC_INDEXLIST)
1244
                SendMessageW(GetParent(hWnd), PSM_PRESSBUTTON, PSBTN_OK, 0);
1245
            break;
1246 1247
        }
        break;
1248 1249 1250 1251
    case WM_NOTIFY:
	switch (((NMHDR*)lParam)->code)
	{
	case PSN_APPLY:
1252
            sel = SendDlgItemMessageW(hWnd, IDC_INDEXLIST, LB_GETCURSEL, 0, 0);
1253 1254 1255 1256 1257
            if (sel != LB_ERR)
            {
                BYTE *p;
                int count;

1258
                p = (BYTE*)SendDlgItemMessageW(hWnd, IDC_INDEXLIST, LB_GETITEMDATA, sel, 0);
1259 1260 1261
                count = *(short*)((char *)p + strlen((char *)p) + 1);
                if (count > 1)
                {
1262 1263
                    MessageBoxA(hWnd, "count > 1 not supported yet", "Error", MB_OK | MB_ICONSTOP);
                    SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_INVALID);
1264 1265
                    return TRUE;
                }
1266 1267 1268
                id->offset = *(ULONG*)((char *)p + strlen((char *)p) + 3);
                id->offset = *(long*)(id->hlpfile->kwdata + id->offset + 9);
                if (id->offset == 0xFFFFFFFF)
1269
                {
1270 1271
                    MessageBoxA(hWnd, "macro keywords not supported yet", "Error", MB_OK | MB_ICONSTOP);
                    SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_INVALID);
1272 1273
                    return TRUE;
                }
1274
                id->jump = TRUE;
1275
                SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_NOERROR);
1276 1277 1278
            }
            return TRUE;
        default:
1279
            return FALSE;
1280
        }
1281
        break;
1282 1283 1284 1285 1286 1287
    default:
        break;
    }
    return FALSE;
}

1288 1289 1290 1291
/**************************************************************************
 * WINHELP_SearchDlgProc
 *
 */
1292
static INT_PTR CALLBACK WINHELP_SearchDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1293 1294 1295 1296 1297 1298 1299 1300 1301
{
    switch (msg)
    {
    case WM_INITDIALOG:
        return TRUE;
    case WM_NOTIFY:
	switch (((NMHDR*)lParam)->code)
	{
	case PSN_APPLY:
1302
            SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, PSNRET_NOERROR);
1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313
            return TRUE;
        default:
            return FALSE;
        }
        break;
    default:
        break;
    }
    return FALSE;
}

1314
/***********************************************************************
1315
 *
1316
 *           WINHELP_MainWndProc
1317
 */
1318
static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1319
{
1320 1321 1322 1323
    WINHELP_WINDOW *win;
    WINHELP_BUTTON *button;
    HWND hTextWnd;
    LRESULT ret;
1324

1325
    if (WINHELP_CheckPopup(hWnd, msg, wParam, lParam, &ret)) return ret;
1326

1327
    switch (msg)
1328
    {
1329
    case WM_NCCREATE:
1330 1331
        win = (WINHELP_WINDOW*) ((LPCREATESTRUCTA) lParam)->lpCreateParams;
        SetWindowLongPtrW(hWnd, 0, (ULONG_PTR) win);
1332
        if (!win->page && Globals.isBook)
1333
            PostMessageW(hWnd, WM_COMMAND, MNID_FILE_OPEN, 0);
1334 1335
        win->hMainWnd = hWnd;
        break;
1336

1337
    case WM_WINDOWPOSCHANGED:
1338
        WINHELP_LayoutMainWindow((WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0));
1339
        break;
1340

1341
    case WM_COMMAND:
1342
        win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1343
        switch (LOWORD(wParam))
1344 1345 1346 1347 1348 1349 1350 1351 1352
	{
            /* Menu FILE */
	case MNID_FILE_OPEN:    MACRO_FileOpen();       break;
	case MNID_FILE_PRINT:	MACRO_Print();          break;
	case MNID_FILE_SETUP:	MACRO_PrinterSetup();   break;
	case MNID_FILE_EXIT:	MACRO_Exit();           break;

            /* Menu EDIT */
	case MNID_EDIT_COPYDLG:
1353
            SendDlgItemMessageW(hWnd, CTL_ID_TEXT, WM_COPY, 0, 0);
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
            break;
	case MNID_EDIT_ANNOTATE:MACRO_Annotate();       break;

            /* Menu Bookmark */
	case MNID_BKMK_DEFINE:  MACRO_BookmarkDefine(); break;

            /* Menu Help */
	case MNID_HELP_HELPON:	MACRO_HelpOn();         break;
	case MNID_HELP_HELPTOP: MACRO_HelpOnTop();      break;
	case MNID_HELP_ABOUT:	MACRO_About();          break;

            /* Context help */
        case MNID_CTXT_ANNOTATE:MACRO_Annotate();       break;
        case MNID_CTXT_COPY:    MACRO_CopyDialog();     break;
        case MNID_CTXT_PRINT:   MACRO_Print();          break;
        case MNID_OPTS_HISTORY: MACRO_History();        break;
        case MNID_OPTS_FONTS_SMALL:
        case MNID_CTXT_FONTS_SMALL:
1372
            win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1373 1374 1375 1376 1377 1378 1379 1380
            if (win->font_scale != 0)
            {
                win->font_scale = 0;
                WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
            }
            break;
        case MNID_OPTS_FONTS_NORMAL:
        case MNID_CTXT_FONTS_NORMAL:
1381
            win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1382 1383 1384 1385 1386 1387 1388 1389
            if (win->font_scale != 1)
            {
                win->font_scale = 1;
                WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
            }
            break;
        case MNID_OPTS_FONTS_LARGE:
        case MNID_CTXT_FONTS_LARGE:
1390
            win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402
            if (win->font_scale != 2)
            {
                win->font_scale = 2;
                WINHELP_SetupText(GetDlgItem(hWnd, CTL_ID_TEXT), win, 0 /* FIXME */);
            }
            break;

	default:
            /* Buttons */
            for (button = win->first_button; button; button = button->next)
                if (wParam == button->wParam) break;
            if (button)
1403
                MACRO_ExecuteMacro(win, button->lpszMacro);
1404
            else if (!HIWORD(wParam))
1405 1406
                MessageBoxW(0, MAKEINTRESOURCEW(STID_NOT_IMPLEMENTED),
                            MAKEINTRESOURCEW(STID_WHERROR), MB_OK);
1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418
            break;
	}
        break;
/* EPP     case WM_DESTROY: */
/* EPP         if (Globals.hPopupWnd) DestroyWindow(Globals.hPopupWnd); */
/* EPP         break; */
    case WM_COPYDATA:
        return WINHELP_HandleCommand((HWND)wParam, lParam);

    case WM_CHAR:
        if (wParam == 3)
        {
1419
            SendDlgItemMessageW(hWnd, CTL_ID_TEXT, WM_COPY, 0, 0);
1420 1421 1422 1423 1424
            return 0;
        }
        break;

    case WM_KEYDOWN:
1425
        win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1426
        hTextWnd = GetDlgItem(win->hMainWnd, CTL_ID_TEXT);
1427 1428 1429 1430

        switch (wParam)
        {
        case VK_UP:
1431
            SendMessageW(hTextWnd, EM_SCROLL, SB_LINEUP, 0);
1432
            return 0;
1433
        case VK_DOWN:
1434
            SendMessageW(hTextWnd, EM_SCROLL, SB_LINEDOWN, 0);
1435
            return 0;
1436
        case VK_PRIOR:
1437
            SendMessageW(hTextWnd, EM_SCROLL, SB_PAGEUP, 0);
1438
            return 0;
1439
        case VK_NEXT:
1440
            SendMessageW(hTextWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460
            return 0;
        case VK_ESCAPE:
            MACRO_Exit();
            return 0;
        }
        break;

    case WM_NOTIFY:
        if (wParam == CTL_ID_TEXT)
        {
            RECT        rc;

            switch (((NMHDR*)lParam)->code)
            {
            case EN_MSGFILTER:
                {
                    const MSGFILTER*    msgf = (const MSGFILTER*)lParam;
                    switch (msgf->msg)
                    {
                    case WM_KEYUP:
1461
                        if (msgf->wParam == VK_ESCAPE)
1462
                            WINHELP_ReleaseWindow((WINHELP_WINDOW*)GetWindowLongPtrW(hWnd, 0));
1463 1464 1465 1466 1467 1468
                        break;
                    case WM_RBUTTONDOWN:
                    {
                        HMENU       hMenu;
                        POINT       pt;

1469 1470
                        win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
                        hMenu = LoadMenuW(Globals.hInstance, MAKEINTRESOURCEW(CONTEXT_MENU));
1471 1472 1473 1474 1475 1476 1477 1478
                        switch (win->font_scale)
                        {
                        case 0:
                            CheckMenuItem(hMenu, MNID_CTXT_FONTS_SMALL,
                                          MF_BYCOMMAND|MF_CHECKED);
                            break;
                        default:
                            WINE_FIXME("Unsupported %d\n", win->font_scale);
1479
                            /* fall through */
1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497
                        case 1:
                            CheckMenuItem(hMenu, MNID_CTXT_FONTS_NORMAL,
                                          MF_BYCOMMAND|MF_CHECKED);
                            break;
                        case 2:
                            CheckMenuItem(hMenu, MNID_CTXT_FONTS_LARGE,
                                          MF_BYCOMMAND|MF_CHECKED);
                            break;
                        }
                        pt.x = (int)(short)LOWORD(msgf->lParam);
                        pt.y = (int)(short)HIWORD(msgf->lParam);
                        ClientToScreen(msgf->nmhdr.hwndFrom, &pt);
                        TrackPopupMenu(GetSubMenu(hMenu, 0), TPM_LEFTALIGN|TPM_TOPALIGN,
                                       pt.x, pt.y, 0, hWnd, NULL);
                        DestroyMenu(hMenu);
                    }
                    break;
                    default:
1498
                        return WINHELP_HandleTextMouse((WINHELP_WINDOW*)GetWindowLongPtrW(hWnd, 0),
1499 1500 1501 1502 1503 1504 1505
                                                       msgf->msg, msgf->lParam);
                    }
                }
                break;

            case EN_REQUESTRESIZE:
                rc = ((REQRESIZE*)lParam)->rc;
1506 1507
                win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
                AdjustWindowRect(&rc, GetWindowLongW(win->hMainWnd, GWL_STYLE),
1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518
                                 FALSE);
                SetWindowPos(win->hMainWnd, HWND_TOP, 0, 0,
                             rc.right - rc.left, rc.bottom - rc.top,
                             SWP_NOMOVE | SWP_NOZORDER);
                WINHELP_LayoutMainWindow(win);
                break;
            }
        }
        break;

    case WM_INITMENUPOPUP:
1519
        win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1520
        CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_SMALL,
1521
                      (win->font_scale == 0) ? MF_CHECKED : MF_UNCHECKED);
1522
        CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_NORMAL,
1523
                      (win->font_scale == 1) ? MF_CHECKED : MF_UNCHECKED);
1524
        CheckMenuItem((HMENU)wParam, MNID_OPTS_FONTS_LARGE,
1525
                      (win->font_scale == 2) ? MF_CHECKED : MF_UNCHECKED);
1526
        break;
1527
    case WM_DESTROY:
1528
        win = (WINHELP_WINDOW*) GetWindowLongPtrW(hWnd, 0);
1529
        WINHELP_DeleteWindow(win);
1530 1531
        break;
    }
1532
    return DefWindowProcA(hWnd, msg, wParam, lParam);
1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543
}

/**************************************************************************
 * WINHELP_CreateIndexWindow
 *
 * Displays a dialog with keywords of current help file.
 *
 */
BOOL WINHELP_CreateIndexWindow(BOOL is_search)
{
    HPROPSHEETPAGE      psPage[3];
1544 1545
    PROPSHEETPAGEA      psp;
    PROPSHEETHEADERA    psHead;
1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565
    struct index_data   id;
    char                buf[256];

    if (Globals.active_win && Globals.active_win->page && Globals.active_win->page->file)
        id.hlpfile = Globals.active_win->page->file;
    else
        return FALSE;

    if (id.hlpfile->kwbtree == NULL)
    {
        WINE_TRACE("No index provided\n");
        return FALSE;
    }

    InitCommonControls();

    id.jump = FALSE;
    memset(&psp, 0, sizeof(psp));
    psp.dwSize = sizeof(psp);
    psp.dwFlags = 0;
1566 1567
    psp.hInstance = Globals.hInstance;

1568
    psp.u.pszTemplate = MAKEINTRESOURCEA(IDD_INDEX);
1569 1570
    psp.lParam = (LPARAM)&id;
    psp.pfnDlgProc = WINHELP_IndexDlgProc;
1571
    psPage[0] = CreatePropertySheetPageA(&psp);
1572

1573
    psp.u.pszTemplate = MAKEINTRESOURCEA(IDD_SEARCH);
1574 1575
    psp.lParam = (LPARAM)&id;
    psp.pfnDlgProc = WINHELP_SearchDlgProc;
1576
    psPage[1] = CreatePropertySheetPageA(&psp);
1577

1578 1579 1580
    memset(&psHead, 0, sizeof(psHead));
    psHead.dwSize = sizeof(psHead);

1581
    LoadStringA(Globals.hInstance, STID_PSH_INDEX, buf, sizeof(buf));
1582 1583 1584
    strcat(buf, Globals.active_win->info->caption);

    psHead.pszCaption = buf;
1585 1586
    psHead.nPages = 2;
    psHead.u2.nStartPage = is_search ? 1 : 0;
1587 1588 1589 1590
    psHead.hwndParent = Globals.active_win->hMainWnd;
    psHead.u3.phpage = psPage;
    psHead.dwFlags = PSH_NOAPPLYNOW;

1591
    PropertySheetA(&psHead);
1592
    if (id.jump)
1593
    {
1594 1595
        WINE_TRACE("got %d as an offset\n", id.offset);
        WINHELP_OpenHelpWindow(HLPFILE_PageByOffset, id.hlpfile, id.offset,
1596
                               Globals.active_win->info, SW_NORMAL);
1597
    }
1598 1599
    return TRUE;
}
1600 1601 1602 1603 1604 1605 1606

/***********************************************************************
 *
 *           RegisterWinClasses
 */
static BOOL WINHELP_RegisterWinClasses(void)
{
1607
    WNDCLASSEXA class_main, class_button_box, class_history;
1608

1609
    class_main.cbSize              = sizeof(class_main);
1610 1611 1612
    class_main.style               = CS_HREDRAW | CS_VREDRAW;
    class_main.lpfnWndProc         = WINHELP_MainWndProc;
    class_main.cbClsExtra          = 0;
1613
    class_main.cbWndExtra          = sizeof(WINHELP_WINDOW *);
1614
    class_main.hInstance           = Globals.hInstance;
1615 1616
    class_main.hIcon               = LoadIconW(Globals.hInstance, MAKEINTRESOURCEW(IDI_WINHELP));
    class_main.hCursor             = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1617 1618 1619
    class_main.hbrBackground       = (HBRUSH)(COLOR_WINDOW+1);
    class_main.lpszMenuName        = 0;
    class_main.lpszClassName       = MAIN_WIN_CLASS_NAME;
1620
    class_main.hIconSm             = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_WINHELP), IMAGE_ICON,
1621 1622
                                               GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
                                               LR_SHARED);
1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633

    class_button_box               = class_main;
    class_button_box.lpfnWndProc   = WINHELP_ButtonBoxWndProc;
    class_button_box.cbWndExtra    = 0;
    class_button_box.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
    class_button_box.lpszClassName = BUTTON_BOX_WIN_CLASS_NAME;

    class_history                  = class_main;
    class_history.lpfnWndProc      = WINHELP_HistoryWndProc;
    class_history.lpszClassName    = HISTORY_WIN_CLASS_NAME;

1634 1635 1636
    return (RegisterClassExA(&class_main) &&
            RegisterClassExA(&class_button_box) &&
            RegisterClassExA(&class_history));
1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650
}

/***********************************************************************
 *
 *           WinMain
 */
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
{
    MSG                 msg;
    LONG                lHash = 0;
    HLPFILE*            hlpfile;
    static CHAR         default_wndname[] = "main";
    LPSTR               wndname = default_wndname;
    WINHELP_DLL*        dll;
1651
    HACCEL              hAccel;
1652 1653 1654

    Globals.hInstance = hInstance;

1655 1656 1657
    if (LoadLibraryA("riched20.dll") == NULL)
        return MessageBoxW(0, MAKEINTRESOURCEW(STID_NO_RICHEDIT),
                           MAKEINTRESOURCEW(STID_WHERROR), MB_OK);
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667

    /* Get options */
    while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
    {
        CHAR   option;
        LPCSTR topic_id;
        if (*cmdline++ == ' ') continue;

        option = *cmdline;
        if (option) cmdline++;
1668
        while (*cmdline == ' ') cmdline++;
1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689
        switch (option)
	{
	case 'i':
	case 'I':
            topic_id = cmdline;
            while (*cmdline && *cmdline != ' ') cmdline++;
            if (*cmdline) *cmdline++ = '\0';
            lHash = HLPFILE_Hash(topic_id);
            break;

	case '3':
	case '4':
            Globals.wVersion = option - '0';
            break;

        case 'x':
            show = SW_HIDE;
            Globals.isBook = FALSE;
            break;

        default:
1690
            WINE_FIXME("Unsupported cmd line: %s\n", debugstr_a(cmdline));
1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722
            break;
	}
    }

    /* Create primary window */
    if (!WINHELP_RegisterWinClasses())
    {
        WINE_FIXME("Couldn't register classes\n");
        return 0;
    }

    if (*cmdline)
    {
        char*   ptr;
        if ((*cmdline == '"') && (ptr = strchr(cmdline+1, '"')))
        {
            cmdline++;
            *ptr = '\0';
        }
        if ((ptr = strchr(cmdline, '>')))
        {
            *ptr = '\0';
            wndname = ptr + 1;
        }
        hlpfile = WINHELP_LookupHelpFile(cmdline);
        if (!hlpfile) return 0;
    }
    else hlpfile = NULL;
    WINHELP_OpenHelpWindow(HLPFILE_PageByHash, hlpfile, lHash,
                           WINHELP_GetWindowInfo(hlpfile, wndname), show);

    /* Message loop */
1723
    hAccel = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(MAIN_ACCEL));
1724
    while ((Globals.win_list || Globals.active_popup) && GetMessageW(&msg, 0, 0, 0))
1725
    {
1726 1727 1728 1729
        HWND hWnd = Globals.active_win ? Globals.active_win->hMainWnd : NULL;
        if (!TranslateAcceleratorW(hWnd, hAccel, &msg))
	{
            TranslateMessage(&msg);
1730
            DispatchMessageW(&msg);
1731
        }
1732 1733 1734 1735 1736 1737 1738
    }
    for (dll = Globals.dlls; dll; dll = dll->next)
    {
        if (dll->class & DC_INITTERM) dll->handler(DW_TERM, 0, 0);
    }
    return 0;
}