Change the translation of the "Help" menu item to "?", so that the menu can be displa...
[reactos.git] / rosapps / smartpdf / src / SumatraPDF.cpp
1 /* Copyright Krzysztof Kowalczyk 2006-2007
2 License: GPLv2 */
3
4 // have to undef _WIN32_IE here to use TB_* constants
5 // (_WIN32_IE was defined in *.cbp - codeblocks project file)
6 // ==> we have to use a stable API instead of MinGW 5.1.3 :-\
7
8 #ifdef __GNUC__
9 #undef _WIN32_IE
10 #endif
11
12 #include "SumatraPDF.h"
13
14 #include "str_util.h"
15 #include "file_util.h"
16 #include "geom_util.h"
17 #include "win_util.h"
18 #include "translations.h"
19
20 #include "SumatraDialogs.h"
21 #include "FileHistory.h"
22 #include "AppPrefs.h"
23 #include "DisplayModelSplash.h"
24
25 /* TODO: this and StandardSecurityHandler::getAuthData() and new GlobalParams
26 should be moved to another file (PopplerInit(), PopplerDeinit() */
27 #include "PDFDoc.h"
28 #include "SecurityHandler.h"
29 #include "GlobalParams.h"
30
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdarg.h>
34 #include <ctype.h>
35 #include <direct.h> /* for _mkdir() */
36
37 #include <shellapi.h>
38 #include <shlobj.h>
39
40 #include "str_strsafe.h"
41
42 #include <windowsx.h>
43
44 // some stupid things are in headers of MinGW 5.1.3 :-\
45 // why we have to define these constants & prototypes again (?!)
46 #ifdef __GNUC__
47 #ifndef VK_OEM_PLUS
48 #define VK_OEM_PLUS 0xBB
49 #endif
50 #ifndef VK_OEM_MINUS
51 #define VK_OEM_MINUS 0xBD
52 #endif
53
54 extern "C" {
55 extern BOOL WINAPI GetDefaultPrinterA(LPSTR,LPDWORD);
56 extern BOOL WINAPI GetDefaultPrinterW(LPWSTR,LPDWORD);
57 }
58 #endif
59
60 // this sucks but I don't know any other way
61 #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
62
63 //#define FANCY_UI 1
64
65 /* Define if you want to conserve memory by always freeing cached bitmaps
66 for pages not visible. Only enable for stress-testing the logic. On
67 desktop machine we usually have plenty memory */
68 //#define CONSERVE_MEMORY 1
69
70 /* Next action for the benchmark mode */
71 #define MSG_BENCH_NEXT_ACTION WM_USER + 1
72
73 #define ZOOM_IN_FACTOR 1.2
74 #define ZOOM_OUT_FACTOR 1.0 / ZOOM_IN_FACTOR
75
76 /* if TRUE, we're in debug mode where we show links as blue rectangle on
77 the screen. Makes debugging code related to links easier.
78 TODO: make a menu item in DEBUG build to turn it on/off. */
79 #ifdef DEBUG
80 static BOOL gDebugShowLinks = TRUE;
81 #else
82 static BOOL gDebugShowLinks = FALSE;
83 #endif
84
85 /* default UI settings */
86 #define DEFAULT_DISPLAY_MODE DM_CONTINUOUS
87
88 //#define DEFAULT_ZOOM ZOOM_FIT_WIDTH
89 #define DEFAULT_ZOOM ZOOM_FIT_PAGE
90 #define DEFAULT_ROTATION 0
91
92 //#define START_WITH_ABOUT 1
93
94 /* define if want to use double-buffering for rendering the PDF. Takes more memory!. */
95 #define DOUBLE_BUFFER 1
96
97 #define DRAGQUERY_NUMFILES 0xFFFFFFFF
98
99 #define MAX_LOADSTRING 100
100
101 #define WM_CREATE_FAILED -1
102 #define WM_CREATE_OK 0
103 #define WM_NCPAINT_HANDLED 0
104 #define WM_VSCROLL_HANDLED 0
105 #define WM_HSCROLL_HANDLED 0
106
107 #define ABOUT_WIN_DX 440
108 #define ABOUT_WIN_DY 300
109
110 #define WM_APP_REPAINT_DELAYED (WM_APP + 10)
111 #define WM_APP_REPAINT_NOW (WM_APP + 11)
112
113 /* A caption is 4 white/blue 2 pixel line and a 3 pixel white line */
114 #define CAPTION_DY 2*(2*4)+3
115
116 #define COL_CAPTION_BLUE RGB(0,0x50,0xa0)
117 #define COL_WHITE RGB(0xff,0xff,0xff)
118 #define COL_BLACK RGB(0,0,0)
119 #define COL_WINDOW_BG RGB(0xcc, 0xcc, 0xcc)
120 #define COL_WINDOW_SHADOW RGB(0x40, 0x40, 0x40)
121
122 #define FRAME_CLASS_NAME _T("SUMATRA_PDF_FRAME")
123 #define CANVAS_CLASS_NAME _T("SUMATRA_PDF_CANVAS")
124 #define ABOUT_CLASS_NAME _T("SUMATRA_PDF_ABOUT")
125 #define APP_NAME _T("SumatraPDF")
126 #define PDF_DOC_NAME _T("Adobe PDF Document")
127 #define ABOUT_WIN_TITLE _TR("About SumatraPDF")
128 #define PREFS_FILE_NAME _T("sumatrapdfprefs.txt")
129 #define APP_SUB_DIR _T("SumatraPDF")
130
131 #define BENCH_ARG_TXT "-bench"
132 #define PRINT_TO_ARG_TXT "-print-to"
133 #define NO_REGISTER_EXT_ARG_TXT "-no-register-ext"
134 #define PRINT_TO_DEFAULT_ARG_TXT "-print-to-default"
135 #define EXIT_ON_PRINT_ARG_TXT "-exit-on-print"
136 #define ENUM_PRINTERS_ARG_TXT "-enum-printers"
137
138 /* Default size for the window, happens to be american A4 size (I think) */
139 #define DEF_WIN_DX 612
140 #define DEF_WIN_DY 792
141
142 #define REPAINT_TIMER_ID 1
143 #define REPAINT_DELAY_IN_MS 400
144
145 /* A special "pointer" vlaue indicating that we tried to render this bitmap
146 but couldn't (e.g. due to lack of memory) */
147 #define BITMAP_CANNOT_RENDER (RenderedBitmap*)NULL
148
149 #define WS_REBAR (WS_CHILD | WS_CLIPCHILDREN | WS_BORDER | RBS_VARHEIGHT | \
150 RBS_BANDBORDERS | CCS_NODIVIDER | CCS_NOPARENTALIGN)
151
152 #define MAX_RECENT_FILES_IN_MENU 15
153
154 typedef struct StrList {
155 struct StrList * next;
156 char * str;
157 } StrList;
158
159 static FileHistoryList * gFileHistoryRoot = NULL;
160
161 static HINSTANCE ghinst = NULL;
162 TCHAR windowTitle[MAX_LOADSTRING];
163
164 static WindowInfo* gWindowList;
165
166 static HCURSOR gCursorArrow;
167 static HCURSOR gCursorHand;
168 static HCURSOR gCursorDrag;
169 static HBRUSH gBrushBg;
170 static HBRUSH gBrushWhite;
171 static HBRUSH gBrushShadow;
172 static HBRUSH gBrushLinkDebug;
173
174 static HPEN ghpenWhite;
175 static HPEN ghpenBlue;
176
177 #ifdef _WINDLL
178 static bool gRunningDLL = true;
179 #else
180 static bool gRunningDLL = false;
181 #endif
182 //static AppVisualStyle gVisualStyle = VS_WINDOWS;
183
184 static char * gBenchFileName;
185 static int gBenchPageNum = INVALID_PAGE_NO;
186 BOOL gShowToolbar = TRUE;
187 BOOL gUseFitz = TRUE;
188 /* If false, we won't ask the user if he wants Sumatra to handle PDF files */
189 BOOL gPdfAssociateDontAskAgain = FALSE;
190 /* If gPdfAssociateDontAskAgain is TRUE, says whether we should silently associate
191 or not */
192 BOOL gPdfAssociateShouldAssociate = TRUE;
193 #ifdef DOUBLE_BUFFER
194 static bool gUseDoubleBuffer = true;
195 #else
196 static bool gUseDoubleBuffer = false;
197 #endif
198
199 #define MAX_PAGE_REQUESTS 8
200 static PageRenderRequest gPageRenderRequests[MAX_PAGE_REQUESTS];
201 static int gPageRenderRequestsCount = 0;
202
203 static HANDLE gPageRenderThreadHandle;
204 static HANDLE gPageRenderSem;
205 static PageRenderRequest * gCurPageRenderReq;
206
207 static int gReBarDy;
208 static int gReBarDyFrame;
209 static HWND gHwndAbout;
210
211 typedef struct ToolbarButtonInfo {
212 /* information provided at compile time */
213 int bitmapResourceId;
214 int cmdId;
215 TCHAR * toolTip;
216
217 /* information calculated at runtime */
218 int index;
219 } ToolbarButtonInfo;
220
221 #define IDB_SEPARATOR -1
222
223 ToolbarButtonInfo gToolbarButtons[] = {
224 { IDB_SILK_OPEN, IDM_OPEN, _TRN(TEXT("Open")), 0 },
225 { IDB_SEPARATOR, IDB_SEPARATOR, 0, 0 },
226 { IDB_SILK_PREV, IDM_GOTO_PREV_PAGE, _TRN(TEXT("Previous Page")), 0 },
227 { IDB_SILK_NEXT, IDM_GOTO_NEXT_PAGE, _TRN(TEXT("Next Page")), 0 },
228 { IDB_SEPARATOR, IDB_SEPARATOR, 0, 0 },
229 { IDB_SILK_ZOOM_IN, IDT_VIEW_ZOOMIN, _TRN(TEXT("Zoom In")), 0 },
230 { IDB_SILK_ZOOM_OUT, IDT_VIEW_ZOOMOUT, _TRN(TEXT("Zoom Out")), 0 }
231 }; /* @note: TEXT() casts */
232
233 #define DEFAULT_LANGUAGE "en"
234
235 #define TOOLBAR_BUTTONS_COUNT dimof(gToolbarButtons)
236
237 static const char *g_currLangName;
238
239 struct LangDef {
240 const char* _langName;
241 int _langId;
242 } g_langs[] = {
243 {"en", IDM_LANG_EN},
244 {"pl", IDM_LANG_PL},
245 {"fr", IDM_LANG_FR},
246 {"de", IDM_LANG_DE},
247 };
248
249 #define LANGS_COUNT dimof(g_langs)
250
251 static void WindowInfo_ResizeToPage(WindowInfo *win, int pageNo);
252 static void CreateToolbar(WindowInfo *win, HINSTANCE hInst);
253 static void RebuildProgramMenus(void);
254
255 const char* CurrLangNameGet() {
256 if (!g_currLangName)
257 return DEFAULT_LANGUAGE;
258 return g_currLangName;
259 }
260
261 bool CurrLangNameSet(const char* langName) {
262 bool validLang = false;
263 for (int i=0; i < LANGS_COUNT; i++) {
264 if (str_eq(langName, g_langs[i]._langName)) {
265 validLang = true;
266 break;
267 }
268 }
269 assert(validLang);
270 if (!validLang) return false;
271 free((void*)g_currLangName);
272 g_currLangName = str_dup(langName);
273
274 bool ok = Translations_SetCurrentLanguage(langName);
275 assert(ok);
276
277 return true;
278 }
279
280 void CurrLangNameFree() {
281 free((void*)g_currLangName);
282 g_currLangName = NULL;
283 }
284
285 /*void LaunchBrowser(const TCHAR *url)
286 {
287 launch_url(url);
288 }*/
289
290 static BOOL pageRenderAbortCb(void *data)
291 {
292 PageRenderRequest *req = (PageRenderRequest*)data;
293 if (req->abort) {
294 DBG_OUT("Rendering of page %d aborted\n", req->pageNo);
295 return TRUE;
296 }
297 else
298 return FALSE;
299 }
300
301 void RenderQueue_RemoveForDisplayModel(DisplayModel *dm) {
302 LockCache();
303 int reqCount = gPageRenderRequestsCount;
304 int curPos = 0;
305 for(int i = 0; i < reqCount; i++) {
306 PageRenderRequest *req = &(gPageRenderRequests[i]);
307 bool shouldRemove = (req->dm == dm);
308 if (i != curPos)
309 gPageRenderRequests[curPos] = gPageRenderRequests[i];
310 if (shouldRemove)
311 --gPageRenderRequestsCount;
312 else
313 ++curPos;
314 }
315 UnlockCache();
316 }
317
318 /* Wait until rendering of a page beloging to <dm> has finished. */
319 /* TODO: this might take some time, would be good to show a dialog to let the
320 user know he has to wait until we finish */
321 void cancelRenderingForDisplayModel(DisplayModel *dm) {
322
323 DBG_OUT("cancelRenderingForDisplayModel()\n");
324 bool renderingFinished = false;;
325 for (;;) {
326 LockCache();
327 if (!gCurPageRenderReq || (gCurPageRenderReq->dm != dm))
328 renderingFinished = true;
329 else
330 gCurPageRenderReq->abort = TRUE;
331 UnlockCache();
332 if (renderingFinished)
333 break;
334 /* TODO: busy loop is not good, but I don't have a better idea */
335 sleep_milliseconds(500);
336 }
337 }
338
339 /* Render a bitmap for page <pageNo> in <dm>. */
340 void RenderQueue_Add(DisplayModel *dm, int pageNo) {
341 DBG_OUT("RenderQueue_Add(pageNo=%d)\n", pageNo);
342 assert(dm);
343 if (!dm) return; //goto Exit; /* @note: because of crosses initialization of [...] */
344
345 LockCache();
346 PdfPageInfo *pageInfo = dm->getPageInfo(pageNo);
347 int rotation = dm->rotation();
348 normalizeRotation(&rotation);
349 double zoomLevel = dm->zoomReal();
350
351 if (BitmapCache_Exists(dm, pageNo, zoomLevel, rotation)) {
352 goto LeaveCsAndExit;
353 }
354
355 if (gCurPageRenderReq &&
356 (gCurPageRenderReq->pageNo == pageNo) && (gCurPageRenderReq->dm == dm)) {
357 if ((gCurPageRenderReq->zoomLevel != zoomLevel) || (gCurPageRenderReq->rotation != rotation)) {
358 /* Currently rendered page is for the same page but with different zoom
359 or rotation, so abort it */
360 DBG_OUT(" aborting rendering\n");
361 gCurPageRenderReq->abort = TRUE;
362 } else {
363 /* we're already rendering exactly the same page */
364 DBG_OUT(" already rendering this page\n");
365 goto LeaveCsAndExit;
366 }
367 }
368
369 for (int i=0; i < gPageRenderRequestsCount; i++) {
370 PageRenderRequest* req = &(gPageRenderRequests[i]);
371 if ((req->pageNo == pageNo) && (req->dm == dm)) {
372 if ((req->zoomLevel == zoomLevel) && (req->rotation == rotation)) {
373 /* Request with exactly the same parameters already queued for
374 rendering. Move it to the top of the queue so that it'll
375 be rendered faster. */
376 PageRenderRequest tmp;
377 tmp = gPageRenderRequests[gPageRenderRequestsCount-1];
378 gPageRenderRequests[gPageRenderRequestsCount-1] = *req;
379 *req = tmp;
380 DBG_OUT(" already queued\n");
381 goto LeaveCsAndExit;
382 } else {
383 /* There was a request queued for the same page but with different
384 zoom or rotation, so only replace this request */
385 DBG_OUT("Replacing request for page %d with new request\n", req->pageNo);
386 req->zoomLevel = zoomLevel;
387 req->rotation = rotation;
388 goto LeaveCsAndExit;
389
390 }
391 }
392 }
393
394 PageRenderRequest* newRequest;
395 /* add request to the queue */
396 if (gPageRenderRequestsCount == MAX_PAGE_REQUESTS) {
397 /* queue is full -> remove the oldest items on the queue */
398 memmove(&(gPageRenderRequests[0]), &(gPageRenderRequests[1]), sizeof(PageRenderRequest)*(MAX_PAGE_REQUESTS-1));
399 newRequest = &(gPageRenderRequests[MAX_PAGE_REQUESTS-1]);
400 } else {
401 newRequest = &(gPageRenderRequests[gPageRenderRequestsCount]);
402 gPageRenderRequestsCount++;
403 }
404 assert(gPageRenderRequestsCount <= MAX_PAGE_REQUESTS);
405 newRequest->dm = dm;
406 newRequest->pageNo = pageNo;
407 newRequest->zoomLevel = zoomLevel;
408 newRequest->rotation = rotation;
409 newRequest->abort = FALSE;
410
411 UnlockCache();
412 /* tell rendering thread there's a new request to render */
413 LONG prevCount;
414 ReleaseSemaphore(gPageRenderSem, 1, &prevCount);
415 Exit:
416 return;
417 LeaveCsAndExit:
418 UnlockCache();
419 return;
420 }
421
422 void RenderQueue_Pop(PageRenderRequest *req)
423 {
424 LockCache();
425 assert(gPageRenderRequestsCount > 0);
426 assert(gPageRenderRequestsCount <= MAX_PAGE_REQUESTS);
427 --gPageRenderRequestsCount;
428 *req = gPageRenderRequests[gPageRenderRequestsCount];
429 assert(gPageRenderRequestsCount >= 0);
430 UnlockCache();
431 }
432
433 static HMENU FindMenuItem(WindowInfo *win, UINT id)
434 {
435 HMENU menuMain = GetMenu(win->hwndFrame);
436
437 /* TODO: to be fully valid, it would have to be recursive */
438 for (int i = 0; i < GetMenuItemCount(menuMain); i++) {
439 UINT thisId = GetMenuItemID(menuMain, i);
440 HMENU subMenu = GetSubMenu(menuMain, i);
441 if (id == thisId)
442 return subMenu;
443 for (int j = 0; j < GetMenuItemCount(subMenu); j++) {
444 thisId = GetMenuItemID(menuMain, j);
445 if (id == thisId)
446 return GetSubMenu(subMenu, j);
447 }
448 }
449 return NULL;
450 }
451
452 static HMENU GetFileMenu(HWND hwnd)
453 {
454 return GetSubMenu(GetMenu(hwnd), 0);
455 }
456
457 static void SwitchToDisplayMode(WindowInfo *win, DisplayMode displayMode)
458 {
459 HMENU menuMain;
460 UINT id;
461
462 menuMain = GetMenu(win->hwndFrame);
463 CheckMenuItem(menuMain, IDM_VIEW_SINGLE_PAGE, MF_BYCOMMAND | MF_UNCHECKED);
464 CheckMenuItem(menuMain, IDM_VIEW_CONTINUOUS, MF_BYCOMMAND | MF_UNCHECKED);
465 CheckMenuItem(menuMain, IDM_VIEW_FACING, MF_BYCOMMAND | MF_UNCHECKED);
466 CheckMenuItem(menuMain, IDM_VIEW_CONTINUOUS_FACING, MF_BYCOMMAND | MF_UNCHECKED);
467
468 win->dm->changeDisplayMode(displayMode);
469 if (DM_SINGLE_PAGE == displayMode) {
470 id = IDM_VIEW_SINGLE_PAGE;
471 } else if (DM_FACING == displayMode) {
472 id = IDM_VIEW_FACING;
473 } else if (DM_CONTINUOUS == displayMode) {
474 id = IDM_VIEW_CONTINUOUS;
475 } else if (DM_CONTINUOUS_FACING == displayMode) {
476 id = IDM_VIEW_CONTINUOUS_FACING;
477 } else
478 assert(0);
479
480 CheckMenuItem(menuMain, id, MF_BYCOMMAND | MF_CHECKED);
481 }
482
483 static UINT AllocNewMenuId(void)
484 {
485 static UINT firstId = 1000;
486 ++firstId;
487 return firstId;
488 }
489
490 #define SEP_ITEM "-----"
491
492 typedef struct MenuDef {
493 const char *m_title;
494 int m_id;
495 } MenuDef;
496
497 MenuDef menuDefFile[] = {
498 { _TRN("&Open\tCtrl-O"), IDM_OPEN },
499 { _TRN("&Close\tCtrl-W"), IDM_CLOSE },
500 { _TRN("&Save as"), IDM_SAVEAS },
501 { _TRN("&Print"), IDM_PRINT },
502 { SEP_ITEM, 0 },
503 { _TRN("Make SumatraPDF a default PDF reader"), IDM_MAKE_DEFAULT_READER },
504 { SEP_ITEM , 0 },
505 { _TRN("E&xit\tCtrl-Q"), IDM_EXIT }
506 };
507
508 MenuDef menuDefView[] = {
509 { _TRN("Single page"), IDM_VIEW_SINGLE_PAGE },
510 { _TRN("Facing"), IDM_VIEW_FACING },
511 { _TRN("Continuous"), IDM_VIEW_CONTINUOUS },
512 { _TRN("Continuous facing"), IDM_VIEW_CONTINUOUS_FACING },
513 { SEP_ITEM, 0 },
514 { _TRN("Rotate left"), IDM_VIEW_ROTATE_LEFT },
515 { _TRN("Rotate right"), IDM_VIEW_ROTATE_RIGHT },
516 { SEP_ITEM, 0 },
517 { _TRN("Show toolbar"), IDM_VIEW_SHOW_HIDE_TOOLBAR },
518 { SEP_ITEM, 0 },
519 { _TRN("Use MuPDF rendering engine"), IDM_VIEW_USE_FITZ },
520 };
521
522 MenuDef menuDefGoTo[] = {
523 { _TRN("Next Page"), IDM_GOTO_NEXT_PAGE },
524 { _TRN("Previous Page"), IDM_GOTO_PREV_PAGE },
525 { _TRN("First Page\tHome"), IDM_GOTO_FIRST_PAGE },
526 { _TRN("Last Page\tEnd"), IDM_GOTO_LAST_PAGE },
527 { _TRN("Page...\tCtrl-G"), IDM_GOTO_PAGE },
528 };
529
530 MenuDef menuDefZoom[] = {
531 { _TRN("Fit &Page\tCtrl-0"), IDM_ZOOM_FIT_PAGE },
532 { _TRN("Act&ual Size\tCtrl-1"), IDM_ZOOM_ACTUAL_SIZE },
533 { _TRN("Fit Widt&h\tCtrl-2"), IDM_ZOOM_FIT_WIDTH },
534 { SEP_ITEM },
535 { _TRN("6400%"), IDM_ZOOM_6400 },
536 { _TRN("3200%"), IDM_ZOOM_3200 },
537 { _TRN("1600%"), IDM_ZOOM_1600 },
538 { _TRN("800%"), IDM_ZOOM_800 },
539 { _TRN("400%"), IDM_ZOOM_400 },
540 { _TRN("200%"), IDM_ZOOM_200 },
541 { _TRN("150%"), IDM_ZOOM_150 },
542 { _TRN("125%"), IDM_ZOOM_125 },
543 { _TRN("100%"), IDM_ZOOM_100 },
544 { _TRN("50%"), IDM_ZOOM_50 },
545 { _TRN("25%"), IDM_ZOOM_25 },
546 { _TRN("12.5%"), IDM_ZOOM_12_5 },
547 { _TRN("8.33%"), IDM_ZOOM_8_33 },
548 };
549
550 MenuDef menuDefLang[] = {
551 { _TRN("&English"), IDM_LANG_EN },
552 { _TRN("&French"), IDM_LANG_FR },
553 { _TRN("&German"), IDM_LANG_DE },
554 { _TRN("&Polish"), IDM_LANG_PL },
555 };
556
557 MenuDef menuDefHelp[] = {
558 { _TRN("&Visit website"), IDM_VISIT_WEBSITE },
559 { _TRN("&About"), IDM_ABOUT }
560 };
561
562 static void AddFileMenuItem(HMENU menuFile, FileHistoryList *node)
563 {
564 assert(node);
565 if (!node) return;
566 assert(menuFile);
567 if (!menuFile) return;
568
569 UINT newId = node->menuId;
570 if (INVALID_MENU_ID == node->menuId)
571 newId = AllocNewMenuId();
572 const char* txt = FilePath_GetBaseName(node->state.filePath);
573 AppendMenu(menuFile, MF_ENABLED | MF_STRING, newId, (TCHAR*)txt); /* @note: TCHAR* cast */
574 node->menuId = newId;
575 }
576
577 static HMENU BuildMenuFromMenuDef(MenuDef menuDefs[], int menuItems)
578 {
579 HMENU m = CreateMenu();
580 if (NULL == m) return NULL;
581 for (int i=0; i < menuItems; i++) {
582 MenuDef md = menuDefs[i];
583 const char *title = md.m_title;
584 int id = md.m_id;
585 if (str_eq(title, SEP_ITEM))
586 AppendMenu(m, MF_SEPARATOR, 0, NULL);
587 else {
588 const WCHAR* wtitle = Translatations_GetTranslationW(title);
589 if (wtitle)
590 AppendMenuW(m, MF_STRING, (UINT_PTR)id, wtitle);
591 }
592 }
593 return m;
594 }
595
596 static HMENU g_currMenu = NULL;
597
598 static void DestroyCurrentMenu()
599 {
600 DestroyMenu(g_currMenu);
601 g_currMenu = NULL;
602 }
603
604 static void AddRecentFilesToMenu(HMENU m)
605 {
606 if (!gFileHistoryRoot) return;
607
608 AppendMenu(m, MF_SEPARATOR, 0, NULL);
609
610 int itemsAdded = 0;
611 FileHistoryList *curr = gFileHistoryRoot;
612 while (curr) {
613 assert(curr->state.filePath);
614 if (curr->state.filePath) {
615 AddFileMenuItem(m, curr);
616 assert(curr->menuId != INVALID_MENU_ID);
617 ++itemsAdded;
618 if (itemsAdded >= MAX_RECENT_FILES_IN_MENU) {
619 DBG_OUT(" not adding, reached max %d items\n", MAX_RECENT_FILES_IN_MENU);
620 return;
621 }
622 }
623 curr = curr->next;
624 }
625 }
626
627 static HMENU ForceRebuildMenu()
628 {
629 HMENU tmp;
630 DestroyCurrentMenu();
631 g_currMenu = CreateMenu();
632 tmp = BuildMenuFromMenuDef(menuDefFile, dimof(menuDefFile));
633 AddRecentFilesToMenu(tmp);
634 AppendMenuW(g_currMenu, MF_POPUP | MF_STRING, (UINT_PTR)tmp, _TRW("&File"));
635 tmp = BuildMenuFromMenuDef(menuDefView, dimof(menuDefView));
636 AppendMenuW(g_currMenu, MF_POPUP | MF_STRING, (UINT_PTR)tmp, _TRW("&View"));
637 tmp = BuildMenuFromMenuDef(menuDefGoTo, dimof(menuDefGoTo));
638 AppendMenuW(g_currMenu, MF_POPUP | MF_STRING, (UINT_PTR)tmp, _TRW("&Go To"));
639 tmp = BuildMenuFromMenuDef(menuDefZoom, dimof(menuDefZoom));
640 AppendMenuW(g_currMenu, MF_POPUP | MF_STRING, (UINT_PTR)tmp, _TRW("&Zoom"));
641 tmp = BuildMenuFromMenuDef(menuDefLang, dimof(menuDefLang));
642 AppendMenuW(g_currMenu, MF_POPUP | MF_STRING, (UINT_PTR)tmp, _TRW("&Language"));
643 tmp = BuildMenuFromMenuDef(menuDefHelp, dimof(menuDefHelp));
644 AppendMenuW(g_currMenu, MF_POPUP | MF_STRING, (UINT_PTR)tmp, _TRW("&Help"));
645 return g_currMenu;
646 }
647
648 static HMENU GetProgramMenu()
649 {
650 if (NULL == g_currMenu)
651 ForceRebuildMenu();
652 assert(g_currMenu);
653 return g_currMenu;
654 }
655
656 /* Return the full exe path of my own executable.
657 Caller needs to free() the result. */
658 static char *ExePathGet(void)
659 {
660 char *cmdline = GetCommandLineA();
661 return str_parse_possibly_quoted(&cmdline);
662 }
663
664 /* Set the client area size of the window 'hwnd' to 'dx'/'dy'. */
665 static void WinResizeClientArea(HWND hwnd, int dx, int dy)
666 {
667 RECT rc;
668 GetClientRect(hwnd, &rc);
669 if ((rect_dx(&rc) == dx) && (rect_dy(&rc) == dy))
670 return;
671 RECT rw;
672 GetWindowRect(hwnd, &rw);
673 int win_dx = rect_dx(&rw) + (dx - rect_dx(&rc));
674 int win_dy = rect_dy(&rw) + (dy - rect_dy(&rc));
675 SetWindowPos(hwnd, NULL, 0, 0, win_dx, win_dy, SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOMOVE| SWP_NOZORDER);
676 }
677
678 static void SetCanvasSizeToDxDy(WindowInfo *win, int w, int h)
679 {
680 RECT canvasRect;
681 GetWindowRect(win->hwndCanvas, &canvasRect);
682 RECT frameRect;
683 GetWindowRect(win->hwndFrame, &frameRect);
684 int dx = rect_dx(&frameRect) - rect_dx(&canvasRect);
685 assert(dx >= 0);
686 int dy = rect_dy(&frameRect) - rect_dy(&canvasRect);
687 assert(dy >= 0);
688 SetWindowPos(win->hwndFrame, NULL, 0, 0, w+dx, h+dy, SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOMOVE| SWP_NOZORDER);
689 //SetWindowPos(win->hwndCanvas, NULL, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOMOVE| SWP_NOZORDER);
690 }
691
692 static void CaptionPens_Create(void)
693 {
694 LOGPEN pen;
695
696 assert(!ghpenWhite);
697 pen.lopnStyle = PS_SOLID;
698 pen.lopnWidth.x = 1;
699 pen.lopnWidth.y = 1;
700 pen.lopnColor = COL_WHITE;
701 ghpenWhite = CreatePenIndirect(&pen);
702 pen.lopnColor = COL_CAPTION_BLUE;
703 ghpenBlue = CreatePenIndirect(&pen);
704 }
705
706 static void CaptionPens_Destroy(void)
707 {
708 if (ghpenWhite) {
709 DeleteObject(ghpenWhite);
710 ghpenWhite = NULL;
711 }
712
713 if (ghpenBlue) {
714 DeleteObject(ghpenBlue);
715 ghpenBlue = NULL;
716 }
717 }
718
719 static void AddFileToHistory(const char *filePath)
720 {
721 FileHistoryList * node;
722 uint32_t oldMenuId = INVALID_MENU_ID;
723
724 assert(filePath);
725 if (!filePath) return;
726
727 /* if a history entry with the same name already exists, then delete it.
728 That way we don't have duplicates and the file moves to the front of the list */
729 node = FileHistoryList_Node_FindByFilePath(&gFileHistoryRoot, filePath);
730 if (node) {
731 oldMenuId = node->menuId;
732 FileHistoryList_Node_RemoveAndFree(&gFileHistoryRoot, node);
733 }
734 node = FileHistoryList_Node_CreateFromFilePath(filePath);
735 if (!node)
736 return;
737 node->menuId = oldMenuId;
738 FileHistoryList_Node_InsertHead(&gFileHistoryRoot, node);
739 }
740
741 extern "C" char *GetPasswordForFile(WindowInfo *win, const char *fileName);
742
743 /* Get password for a given 'fileName', can be NULL if user cancelled the
744 dialog box.
745 Caller needs to free() the result. */
746 char *GetPasswordForFile(WindowInfo *win, const char *fileName)
747 {
748 fileName = FilePath_GetBaseName(fileName);
749 return Dialog_GetPassword(win, fileName);
750 }
751
752 void *StandardSecurityHandler::getAuthData()
753 {
754 WindowInfo * win;
755 const char * pwd;
756 StandardAuthData * authData;
757
758 win = (WindowInfo*)doc->getGUIData();
759 assert(win);
760 if (!win)
761 return NULL;
762
763 pwd = GetPasswordForFile(win, doc->getFileName()->getCString());
764 if (!pwd)
765 return NULL;
766
767 authData = new StandardAuthData(new GooString(pwd), new GooString(pwd));
768 free((void*)pwd);
769 return (void*)authData;
770 }
771
772 /* Return true if this program has been started from "Program Files" directory
773 (which is an indicator that it has been installed */
774 static bool runningFromProgramFiles(void)
775 {
776 char programFilesDir[MAX_PATH];
777 BOOL fOk = SHGetSpecialFolderPath(NULL, (TCHAR*)programFilesDir, CSIDL_PROGRAM_FILES, FALSE); /* @note: TCHAR* cast */
778 char *exePath = ExePathGet();
779 if (!exePath) return true; // again, assume it is
780 bool fromProgramFiles = false;
781 if (fOk) {
782 if (str_startswithi(exePath, programFilesDir))
783 fromProgramFiles = true;
784 } else {
785 // SHGetSpecialFolderPath() might fail on win95/98 so need a different check
786 if (strstr(exePath, "Program Files"))
787 fromProgramFiles = true;
788 }
789 free(exePath);
790 return fromProgramFiles;
791 }
792
793 static bool IsRunningInPortableMode(void)
794 {
795 return !runningFromProgramFiles();
796 }
797
798 static void AppGetAppDir(DString* pDs)
799 {
800 char dir[MAX_PATH];
801
802 SHGetSpecialFolderPath(NULL, (TCHAR*)dir, CSIDL_APPDATA, TRUE); /* @note: TCHAR* cast */
803 DStringSprintf(pDs, "%s/%s", dir, APP_SUB_DIR);
804 _mkdir(pDs->pString);
805 }
806
807 /* Generate the full path for a filename used by the app in the userdata path. */
808 static void AppGenDataFilename(char* pFilename, DString* pDs)
809 {
810 assert(0 == pDs->length);
811 assert(pFilename);
812 if (!pFilename) return;
813 assert(pDs);
814 if (!pDs) return;
815
816 bool portable = IsRunningInPortableMode();
817 if (portable) {
818 /* Use the same path as the binary */
819 char *exePath = ExePathGet();
820 if (!exePath) return;
821 char *dir = FilePath_GetDir(exePath);
822 if (dir)
823 DStringSprintf(pDs, "%s", dir);
824 free((void*)exePath);
825 free((void*)dir);
826 } else {
827 AppGetAppDir(pDs);
828 }
829 if (!str_endswithi(pDs->pString, DIR_SEP_STR) && !(DIR_SEP_CHAR == pFilename[0])) {
830 DStringAppend(pDs, DIR_SEP_STR, -1);
831 }
832 DStringAppend(pDs, pFilename, -1);
833 }
834
835 static void Prefs_GetFileName(DString* pDs)
836 {
837 assert(0 == pDs->length);
838 AppGenDataFilename((char*)PREFS_FILE_NAME, pDs); /* @note: char* cast */
839 }
840
841 /* Load preferences from the preferences file. */
842 static void Prefs_Load(void)
843 {
844 DString path;
845 static bool loaded = false;
846 char * prefsTxt = NULL;
847
848 assert(!loaded);
849 loaded = true;
850
851 DBG_OUT("Prefs_Load()\n");
852
853 DStringInit(&path);
854 Prefs_GetFileName(&path);
855
856 size_t prefsFileLen;
857 prefsTxt = file_read_all(path.pString, &prefsFileLen);
858 if (str_empty(prefsTxt)) {
859 DBG_OUT(" no prefs file or is empty\n");
860 return;
861 }
862 DBG_OUT("Prefs file %s:\n%s\n", path.pString, prefsTxt);
863
864 bool fOk = Prefs_Deserialize(prefsTxt, &gFileHistoryRoot);
865 assert(fOk);
866
867 DStringFree(&path);
868 free((void*)prefsTxt);
869 }
870
871 static struct idToZoomMap {
872 UINT id;
873 double zoom;
874 } gZoomMenuItemsId[] = {
875 { IDM_ZOOM_6400, 6400.0 },
876 { IDM_ZOOM_3200, 3200.0 },
877 { IDM_ZOOM_1600, 1600.0 },
878 { IDM_ZOOM_800, 800.0 },
879 { IDM_ZOOM_400, 400.0 },
880 { IDM_ZOOM_200, 200.0 },
881 { IDM_ZOOM_150, 150.0 },
882 { IDM_ZOOM_125, 125.0 },
883 { IDM_ZOOM_100, 100.0 },
884 { IDM_ZOOM_50, 50.0 },
885 { IDM_ZOOM_25, 25.0 },
886 { IDM_ZOOM_12_5, 12.5 },
887 { IDM_ZOOM_8_33, 8.33 },
888 { IDM_ZOOM_FIT_PAGE, ZOOM_FIT_PAGE },
889 { IDM_ZOOM_FIT_WIDTH, ZOOM_FIT_WIDTH },
890 { IDM_ZOOM_ACTUAL_SIZE, 100.0 }
891 };
892
893 static UINT MenuIdFromVirtualZoom(double virtualZoom)
894 {
895 for (int i=0; i < dimof(gZoomMenuItemsId); i++) {
896 if (virtualZoom == gZoomMenuItemsId[i].zoom)
897 return gZoomMenuItemsId[i].id;
898 }
899 return IDM_ZOOM_ACTUAL_SIZE;
900 }
901
902 static void ZoomMenuItemCheck(HMENU hmenu, UINT menuItemId)
903 {
904 BOOL found = FALSE;
905
906 for (int i=0; i<dimof(gZoomMenuItemsId); i++) {
907 UINT checkState = MF_BYCOMMAND | MF_UNCHECKED;
908 if (menuItemId == gZoomMenuItemsId[i].id) {
909 assert(!found);
910 found = TRUE;
911 checkState = MF_BYCOMMAND | MF_CHECKED;
912 }
913 CheckMenuItem(hmenu, gZoomMenuItemsId[i].id, checkState);
914 }
915 assert(found);
916 }
917
918 static double ZoomMenuItemToZoom(UINT menuItemId)
919 {
920 for (int i=0; i<dimof(gZoomMenuItemsId); i++) {
921 if (menuItemId == gZoomMenuItemsId[i].id) {
922 return gZoomMenuItemsId[i].zoom;
923 }
924 }
925 assert(0);
926 return 100.0;
927 }
928
929 static void Win32_Win_GetSize(HWND hwnd, int *dxOut, int *dyOut)
930 {
931 RECT r;
932 *dxOut = 0;
933 *dyOut = 0;
934
935 if (GetWindowRect(hwnd, &r)) {
936 *dxOut = (r.right - r.left);
937 *dyOut = (r.bottom - r.top);
938 }
939 }
940
941 static void Win32_Win_GetPos(HWND hwnd, int *xOut, int *yOut)
942 {
943 RECT r;
944 *xOut = 0;
945 *yOut = 0;
946
947 if (GetWindowRect(hwnd, &r)) {
948 *xOut = r.left;
949 *yOut = r.top;
950 }
951 }
952
953 static void Win32_Win_SetPos(HWND hwnd, int x, int y)
954 {
955 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE);
956 }
957
958 static void UpdateDisplayStateWindowPos(WindowInfo *win, DisplayState *ds)
959 {
960 int posX, posY;
961
962 Win32_Win_GetPos(win->hwndCanvas, &posX, &posY);
963
964 ds->windowX = posX;
965 ds->windowY = posY;
966 }
967
968 static void UpdateCurrentFileDisplayStateForWin(WindowInfo *win)
969 {
970 DisplayState ds;
971 const char * fileName = NULL;
972 FileHistoryList*node = NULL;
973
974 if (!win)
975 return;
976 if (WS_SHOWING_PDF != win->state)
977 return;
978 if (!win->dm)
979 return;
980
981 fileName = win->dm->fileName();
982 assert(fileName);
983 if (!fileName)
984 return;
985
986 if (!gRunningDLL)
987 {
988 node = FileHistoryList_Node_FindByFilePath(&gFileHistoryRoot, fileName);
989 assert(node);
990 if (!node)
991 return;
992 }
993
994 DisplayState_Init(&ds);
995 if (!displayStateFromDisplayModel(&ds, win->dm))
996 return;
997
998 UpdateDisplayStateWindowPos(win, &ds);
999 DisplayState_Free(&(node->state));
1000 node->state = ds;
1001 node->state.visible = TRUE;
1002 }
1003
1004 static void UpdateCurrentFileDisplayState(void)
1005 {
1006 WindowInfo * currWin;
1007 FileHistoryList * currFile;
1008
1009 currFile = gFileHistoryRoot;
1010 while (currFile) {
1011 currFile->state.visible = FALSE;
1012 currFile = currFile->next;
1013 }
1014
1015 currWin = gWindowList;
1016 while (currWin) {
1017 UpdateCurrentFileDisplayStateForWin(currWin);
1018 currWin = currWin->next;
1019 }
1020 }
1021
1022 static void Prefs_Save(void)
1023 {
1024 DString path;
1025 DString prefsStr;
1026
1027 #if 0
1028 if (gPrefsSaved)
1029 return;
1030 gPrefsSaved = TRUE;
1031 #endif
1032
1033 DStringInit(&prefsStr);
1034
1035 /* mark currently shown files as visible */
1036 UpdateCurrentFileDisplayState();
1037
1038 bool fOk = Prefs_Serialize(&gFileHistoryRoot, &prefsStr);
1039 if (!fOk)
1040 goto Exit;
1041
1042 DStringInit(&path);
1043 Prefs_GetFileName(&path);
1044 DBG_OUT("prefs file=%s\nprefs:\n%s\n", path.pString, prefsStr.pString);
1045 /* TODO: consider 2-step process:
1046 * write to a temp file
1047 * rename temp file to final file */
1048 write_to_file((TCHAR*)path.pString, (void*)prefsStr.pString, prefsStr.length); /* @note: TCHAR* cast */
1049
1050 Exit:
1051 DStringFree(&prefsStr);
1052 DStringFree(&path);
1053 }
1054
1055 static bool WindowInfo_Dib_Init(WindowInfo *win) {
1056 assert(NULL == win->dibInfo);
1057 win->dibInfo = (BITMAPINFO*)malloc(sizeof(BITMAPINFO) + 12);
1058 if (!win->dibInfo)
1059 return false;
1060 win->dibInfo->bmiHeader.biSize = sizeof(win->dibInfo->bmiHeader);
1061 win->dibInfo->bmiHeader.biPlanes = 1;
1062 win->dibInfo->bmiHeader.biBitCount = 24;
1063 win->dibInfo->bmiHeader.biCompression = BI_RGB;
1064 win->dibInfo->bmiHeader.biXPelsPerMeter = 2834;
1065 win->dibInfo->bmiHeader.biYPelsPerMeter = 2834;
1066 win->dibInfo->bmiHeader.biClrUsed = 0;
1067 win->dibInfo->bmiHeader.biClrImportant = 0;
1068 return true;
1069 }
1070
1071 static void WindowInfo_Dib_Deinit(WindowInfo *win) {
1072 free((void*)win->dibInfo);
1073 win->dibInfo = NULL;
1074 }
1075
1076 static void WindowInfo_DoubleBuffer_Delete(WindowInfo *win) {
1077 if (win->bmpDoubleBuffer) {
1078 DeleteObject(win->bmpDoubleBuffer);
1079 win->bmpDoubleBuffer = NULL;
1080 }
1081
1082 if (win->hdcDoubleBuffer) {
1083 DeleteDC(win->hdcDoubleBuffer);
1084 win->hdcDoubleBuffer = NULL;
1085 }
1086 win->hdcToDraw = NULL;
1087 }
1088
1089 static bool WindowInfo_DoubleBuffer_New(WindowInfo *win)
1090 {
1091 WindowInfo_DoubleBuffer_Delete(win);
1092
1093 win->hdc = GetDC(win->hwndCanvas);
1094 win->hdcToDraw = win->hdc;
1095 win->GetCanvasSize();
1096 if (!gUseDoubleBuffer || (0 == win->winDx()) || (0 == win->winDy()))
1097 return true;
1098
1099 win->hdcDoubleBuffer = CreateCompatibleDC(win->hdc);
1100 if (!win->hdcDoubleBuffer)
1101 return false;
1102
1103 win->bmpDoubleBuffer = CreateCompatibleBitmap(win->hdc, win->winDx(), win->winDy());
1104 if (!win->bmpDoubleBuffer) {
1105 WindowInfo_DoubleBuffer_Delete(win);
1106 return false;
1107 }
1108 /* TODO: do I need this ? */
1109 SelectObject(win->hdcDoubleBuffer, win->bmpDoubleBuffer);
1110 /* fill out everything with background color */
1111 RECT r = {0};
1112 r.bottom = win->winDy();
1113 r.right = win->winDx();
1114 FillRect(win->hdcDoubleBuffer, &r, gBrushBg);
1115 win->hdcToDraw = win->hdcDoubleBuffer;
1116 return TRUE;
1117 }
1118
1119 static void WindowInfo_DoubleBuffer_Show(WindowInfo *win, HDC hdc)
1120 {
1121 if (win->hdc != win->hdcToDraw) {
1122 assert(win->hdcToDraw == win->hdcDoubleBuffer);
1123 BitBlt(hdc, 0, 0, win->winDx(), win->winDy(), win->hdcDoubleBuffer, 0, 0, SRCCOPY);
1124 }
1125 }
1126
1127 static void WindowInfo_Delete(WindowInfo *win)
1128 {
1129 if (win->dm) {
1130 RenderQueue_RemoveForDisplayModel(win->dm);
1131 cancelRenderingForDisplayModel(win->dm);
1132 }
1133 delete win->dm;
1134 win->dm = NULL;
1135 WindowInfo_Dib_Deinit(win);
1136 WindowInfo_DoubleBuffer_Delete(win);
1137 delete win;
1138 }
1139
1140 static WindowInfo* WindowInfo_FindByHwnd(HWND hwnd)
1141 {
1142 WindowInfo *win = gWindowList;
1143 while (win) {
1144 if (hwnd == win->hwndFrame)
1145 return win;
1146 if (hwnd == win->hwndCanvas)
1147 return win;
1148 if (hwnd == win->hwndReBar)
1149 return win;
1150 win = win->next;
1151 }
1152 return NULL;
1153 }
1154
1155 static WindowInfo *WindowInfo_New(HWND hwndFrame) {
1156 WindowInfo * win = WindowInfo_FindByHwnd(hwndFrame);
1157 assert(!win);
1158
1159 win = new WindowInfo();;
1160 if (!win)
1161 return NULL;
1162
1163 if (!WindowInfo_Dib_Init(win))
1164 goto Error;
1165
1166 win->state = WS_ABOUT;
1167 win->hwndFrame = hwndFrame;
1168 win->mouseAction = MA_IDLE;
1169 return win;
1170 Error:
1171 WindowInfo_Delete(win);
1172 return NULL;
1173 }
1174
1175 static void WindowInfoList_Add(WindowInfo *win) {
1176 win->next = gWindowList;
1177 gWindowList = win;
1178 }
1179
1180 static bool WindowInfoList_ExistsWithError(void) {
1181 WindowInfo *cur = gWindowList;
1182 while (cur) {
1183 if (WS_ERROR_LOADING_PDF == cur->state)
1184 return true;
1185 cur = cur->next;
1186 }
1187 return false;
1188 }
1189
1190 static void WindowInfoList_Remove(WindowInfo *to_remove) {
1191 assert(to_remove);
1192 if (!to_remove)
1193 return;
1194 if (gWindowList == to_remove) {
1195 gWindowList = to_remove->next;
1196 return;
1197 }
1198 WindowInfo* curr = gWindowList;
1199 while (curr) {
1200 if (to_remove == curr->next) {
1201 curr->next = to_remove->next;
1202 return;
1203 }
1204 curr = curr->next;
1205 }
1206 }
1207
1208 static void WindowInfoList_DeleteAll(void) {
1209 WindowInfo* curr = gWindowList;
1210 while (curr) {
1211 WindowInfo* next = curr->next;
1212 WindowInfo_Delete(curr);
1213 curr = next;
1214 }
1215 gWindowList = NULL;
1216 }
1217
1218 static int WindowInfoList_Len(void) {
1219 int len = 0;
1220 WindowInfo* curr = gWindowList;
1221 while (curr) {
1222 ++len;
1223 curr = curr->next;
1224 }
1225 return len;
1226 }
1227
1228 static void WindowInfo_RedrawAll(WindowInfo *win, bool update=false) {
1229 InvalidateRect(win->hwndCanvas, NULL, false);
1230 if (update)
1231 UpdateWindow(win->hwndCanvas);
1232 }
1233
1234 static bool FileCloseMenuEnabled(void) {
1235 WindowInfo* win = gWindowList;
1236 while (win) {
1237 if (win->state == WS_SHOWING_PDF)
1238 return true;
1239 win = win->next;
1240 }
1241 return false;
1242 }
1243
1244 static void ToolbarUpdateStateForWindow(WindowInfo *win) {
1245 LPARAM enable = (LPARAM)MAKELONG(1,0);
1246 LPARAM disable = (LPARAM)MAKELONG(0,0);
1247
1248 for (int i=0; i < TOOLBAR_BUTTONS_COUNT; i++) {
1249 int cmdId = gToolbarButtons[i].cmdId;
1250 if (IDB_SEPARATOR == cmdId)
1251 continue;
1252 LPARAM buttonState = enable;
1253 if (IDM_OPEN != cmdId) {
1254 if (WS_SHOWING_PDF != win->state)
1255 buttonState = disable;
1256 }
1257 SendMessage(win->hwndToolbar, TB_ENABLEBUTTON, cmdId, buttonState);
1258 }
1259 }
1260
1261 static void MenuUpdateShowToolbarStateForWindow(WindowInfo *win) {
1262 HMENU hmenu = GetMenu(win->hwndFrame);
1263 if (gShowToolbar)
1264 CheckMenuItem(hmenu, IDM_VIEW_SHOW_HIDE_TOOLBAR, MF_BYCOMMAND | MF_CHECKED);
1265 else
1266 CheckMenuItem(hmenu, IDM_VIEW_SHOW_HIDE_TOOLBAR, MF_BYCOMMAND | MF_UNCHECKED);
1267 }
1268
1269 static void MenuUpdateUseFitzStateForWindow(WindowInfo *win) {
1270 HMENU hmenu = GetMenu(win->hwndFrame);
1271 if (gUseFitz)
1272 CheckMenuItem(hmenu, IDM_VIEW_USE_FITZ, MF_BYCOMMAND | MF_CHECKED);
1273 else
1274 CheckMenuItem(hmenu, IDM_VIEW_USE_FITZ, MF_BYCOMMAND | MF_UNCHECKED);
1275 }
1276
1277 // show which language is being used via check in Language/* menu
1278 static void MenuUpdateLanguage(WindowInfo *win) {
1279 HMENU hmenu = GetMenu(win->hwndFrame);
1280 for (int i = 0; i < LANGS_COUNT; i++) {
1281 const char *langName = g_langs[i]._langName;
1282 int langMenuId = g_langs[i]._langId;
1283 if (str_eq(CurrLangNameGet(), langName))
1284 CheckMenuItem(hmenu, langMenuId, MF_BYCOMMAND | MF_CHECKED);
1285 else
1286 CheckMenuItem(hmenu, langMenuId, MF_BYCOMMAND | MF_UNCHECKED);
1287 }
1288 }
1289
1290 static void MenuUpdateStateForWindow(WindowInfo *win) {
1291 static UINT menusToDisableIfNoPdf[] = {
1292 IDM_VIEW_SINGLE_PAGE, IDM_VIEW_FACING, IDM_VIEW_CONTINUOUS, IDM_VIEW_CONTINUOUS_FACING,
1293 IDM_VIEW_ROTATE_LEFT, IDM_VIEW_ROTATE_RIGHT, IDM_GOTO_NEXT_PAGE, IDM_GOTO_PREV_PAGE,
1294 IDM_GOTO_FIRST_PAGE, IDM_GOTO_LAST_PAGE, IDM_GOTO_PAGE, IDM_ZOOM_FIT_PAGE,
1295 IDM_ZOOM_ACTUAL_SIZE, IDM_ZOOM_FIT_WIDTH, IDM_ZOOM_6400, IDM_ZOOM_3200,
1296 IDM_ZOOM_1600, IDM_ZOOM_800, IDM_ZOOM_400, IDM_ZOOM_200, IDM_ZOOM_150,
1297 IDM_ZOOM_125, IDM_ZOOM_100, IDM_ZOOM_50, IDM_ZOOM_25, IDM_ZOOM_12_5,
1298 IDM_ZOOM_8_33, IDM_SAVEAS };
1299
1300 bool fileCloseEnabled = FileCloseMenuEnabled();
1301 HMENU hmenu = GetMenu(win->hwndFrame);
1302 if (fileCloseEnabled)
1303 EnableMenuItem(hmenu, IDM_CLOSE, MF_BYCOMMAND | MF_ENABLED);
1304 else
1305 EnableMenuItem(hmenu, IDM_CLOSE, MF_BYCOMMAND | MF_GRAYED);
1306
1307 bool filePrintEnabled = false;
1308 if (win->dm && win->dm->pdfEngine() && win->dm->pdfEngine()->printingAllowed())
1309 filePrintEnabled = true;
1310 if (filePrintEnabled)
1311 EnableMenuItem(hmenu, IDM_PRINT, MF_BYCOMMAND | MF_ENABLED);
1312 else
1313 EnableMenuItem(hmenu, IDM_PRINT, MF_BYCOMMAND | MF_GRAYED);
1314
1315 MenuUpdateShowToolbarStateForWindow(win);
1316 MenuUpdateUseFitzStateForWindow(win);
1317 MenuUpdateLanguage(win);
1318
1319 for (int i = 0; i < dimof(menusToDisableIfNoPdf); i++) {
1320 UINT menuId = menusToDisableIfNoPdf[i];
1321 if (WS_SHOWING_PDF == win->state)
1322 EnableMenuItem(hmenu, menuId, MF_BYCOMMAND | MF_ENABLED);
1323 else
1324 EnableMenuItem(hmenu, menuId, MF_BYCOMMAND | MF_GRAYED);
1325 }
1326 /* Hide scrollbars if not showing a PDF */
1327 /* TODO: doesn't really fit the name of the function */
1328 if (WS_SHOWING_PDF == win->state)
1329 ShowScrollBar(win->hwndCanvas, SB_BOTH, TRUE);
1330 else {
1331 ShowScrollBar(win->hwndCanvas, SB_BOTH, FALSE);
1332 win_set_text(win->hwndFrame, APP_NAME);
1333 }
1334 }
1335
1336 /* Disable/enable menu items and toolbar buttons depending on wheter a
1337 given window shows a PDF file or not. */
1338 static void MenuToolbarUpdateStateForAllWindows(void) {
1339 WindowInfo* win = gWindowList;
1340 while (win) {
1341 MenuUpdateStateForWindow(win);
1342 ToolbarUpdateStateForWindow(win);
1343 win = win->next;
1344 }
1345 }
1346
1347 static WindowInfo* WindowInfo_CreateEmpty(void) {
1348 HWND hwndFrame, hwndCanvas;
1349 WindowInfo* win;
1350
1351 #if FANCY_UI
1352 hwndFrame = CreateWindowEx(
1353 // WS_EX_TOOLWINDOW,
1354 0,
1355 // WS_OVERLAPPEDWINDOW,
1356 // WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
1357 //WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_HSCROLL | WS_VSCROLL,
1358 FRAME_CLASS_NAME, windowTitle,
1359 WS_POPUP,
1360 CW_USEDEFAULT, CW_USEDEFAULT,
1361 DEF_WIN_DX, DEF_WIN_DY,
1362 NULL, NULL,
1363 ghinst, NULL);
1364 #else
1365 hwndFrame = CreateWindow(
1366 FRAME_CLASS_NAME, windowTitle,
1367 WS_OVERLAPPEDWINDOW,
1368 CW_USEDEFAULT, CW_USEDEFAULT,
1369 DEF_WIN_DX, DEF_WIN_DY,
1370 NULL, NULL,
1371 ghinst, NULL);
1372 HMENU m = GetProgramMenu();
1373 SetMenu(hwndFrame, m);
1374 #endif
1375
1376 if (!hwndFrame)
1377 return NULL;
1378
1379 win = WindowInfo_New(hwndFrame);
1380 hwndCanvas = CreateWindow(
1381 CANVAS_CLASS_NAME, NULL,
1382 WS_CHILD | WS_HSCROLL | WS_VSCROLL,
1383 CW_USEDEFAULT, CW_USEDEFAULT,
1384 DEF_WIN_DX, DEF_WIN_DY,
1385 hwndFrame, NULL,
1386 ghinst, NULL);
1387 if (!hwndCanvas)
1388 return NULL;
1389 win->hwndCanvas = hwndCanvas;
1390 CreateToolbar(win, ghinst);
1391 return win;
1392 }
1393
1394 BOOL GetDesktopWindowClientRect(RECT *r)
1395 {
1396 HWND hwnd = GetDesktopWindow();
1397 if (!hwnd) return FALSE;
1398 return GetClientRect(hwnd, r);
1399 }
1400
1401 void GetCanvasDxDyDiff(WindowInfo *win, int *dxOut, int *dyOut)
1402 {
1403 RECT canvasRect;
1404 GetWindowRect(win->hwndCanvas, &canvasRect);
1405 RECT totalRect;
1406 GetWindowRect(win->hwndFrame, &totalRect);
1407 *dxOut = rect_dx(&totalRect) - rect_dx(&canvasRect);
1408 // TODO: should figure out why it fires in DLL
1409 assert(gRunningDLL || *dxOut >= 0);
1410 *dyOut = rect_dy(&totalRect) - rect_dy(&canvasRect);
1411 // TODO: should figure out why it fires in DLL
1412 assert(gRunningDLL || *dyOut >= 0);
1413 }
1414
1415 SizeI GetMaxCanvasSize(WindowInfo *win)
1416 {
1417 AppBarData abd;
1418 RECT r;
1419 GetDesktopWindowClientRect(&r);
1420 // substract the area of the window not used for canvas
1421 int dx, dy;
1422 GetCanvasDxDyDiff(win, &dx, &dy); // TODO: lame name
1423 int maxCanvasDx = rect_dx(&r) - dx;
1424 int maxCanvasDy = rect_dy(&r) - dy;
1425 if (abd.isHorizontal()) {
1426 assert(maxCanvasDx >= abd.dx());
1427 maxCanvasDx -= abd.dx();
1428 } else {
1429 assert(abd.isVertical());
1430 assert(maxCanvasDy >= abd.dy());
1431 maxCanvasDy -= abd.dy();
1432 }
1433 return SizeI(maxCanvasDx, maxCanvasDy);
1434 }
1435
1436
1437 static void RecalcSelectionPosition (WindowInfo *win) {
1438 SelectionOnPage * selOnPage = win->selectionOnPage;
1439 RectD selD;
1440 PdfPageInfo* pageInfo;
1441
1442 while (selOnPage != NULL) {
1443 pageInfo = win->dm->getPageInfo(selOnPage->pageNo);
1444 /* if page is not visible, we hide seletion by simply moving it off
1445 * the canvas */
1446 if (!pageInfo->visible) {
1447 selOnPage->selectionCanvas.x = -100;
1448 selOnPage->selectionCanvas.y = -100;
1449 selOnPage->selectionCanvas.dx = 0;
1450 selOnPage->selectionCanvas.dy = 0;
1451 } else {//page is visible
1452 RectD_Copy (&selD, &selOnPage->selectionPage);
1453 win->dm->rectCvtUserToScreen (selOnPage->pageNo, &selD);
1454 RectI_FromRectD (&selOnPage->selectionCanvas, &selD);
1455 }
1456 selOnPage = selOnPage->next;
1457 }
1458 }
1459
1460 static WindowInfo* LoadPdf(const char *fileName, bool ignoreHistorySizePos = true, bool ignoreHistory = false)
1461 {
1462 assert(fileName);
1463 if (!fileName) return NULL;
1464
1465 FileHistoryList * fileFromHistory = NULL;
1466 if (!ignoreHistory)
1467 fileFromHistory = FileHistoryList_Node_FindByFilePath(&gFileHistoryRoot, fileName);
1468
1469 WindowInfo * win;
1470 bool reuseExistingWindow = false;
1471 if ((1 == WindowInfoList_Len()) && (WS_SHOWING_PDF != gWindowList->state)) {
1472 win = gWindowList;
1473 reuseExistingWindow = true;
1474 } else {
1475 win = WindowInfo_CreateEmpty();
1476 if (!win)
1477 return NULL;
1478 }
1479
1480 win->GetCanvasSize();
1481 SizeI maxCanvasSize = GetMaxCanvasSize(win);
1482 SizeD totalDrawAreaSize(win->winSize());
1483 if (fileFromHistory && !ignoreHistorySizePos) {
1484 WinResizeClientArea(win->hwndCanvas, fileFromHistory->state.windowDx, fileFromHistory->state.windowDy);
1485 totalDrawAreaSize = SizeD(fileFromHistory->state.windowDx, fileFromHistory->state.windowDy);
1486 Win32_Win_SetPos(win->hwndFrame, fileFromHistory->state.windowX, fileFromHistory->state.windowY);
1487 }
1488 #if 0 // not ready yet
1489 else {
1490 IntelligentWindowResize(win);
1491 }
1492 #endif
1493
1494 /* TODO: make sure it doesn't have a stupid position like
1495 outside of the screen etc. */
1496 #if 0
1497 if (totalDrawAreaSize.dxI() > maxCanvasSize.dx)
1498 totalDrawAreaSize.setDx(maxCanvasSize.dx);
1499 if (totalDrawAreaSize.dyI() > maxCanvasSize.dy)
1500 totalDrawAreaSize.setDy(maxCanvasSize.dy);
1501
1502 WinResizeClientArea(win->hwndCanvas, totalDrawAreaSize.dxI(), totalDrawAreaSize.dyI());
1503 #endif
1504
1505 /* In theory I should get scrollbars sizes using Win32_GetScrollbarSize(&scrollbarYDx, &scrollbarXDy);
1506 but scrollbars are not part of the client area on windows so it's better
1507 not to have them taken into account by DisplayModelSplash code.
1508 TODO: I think it's broken anyway and DisplayModelSplash needs to know if
1509 scrollbars are part of client area in order to accomodate windows
1510 UI properly */
1511 DisplayMode displayMode = DEFAULT_DISPLAY_MODE;
1512 int offsetX = 0;
1513 int offsetY = 0;
1514 int startPage = 1;
1515 int scrollbarYDx = 0;
1516 int scrollbarXDy = 0;
1517 if (fileFromHistory) {
1518 startPage = fileFromHistory->state.pageNo;
1519 displayMode = fileFromHistory->state.displayMode;
1520 offsetX = fileFromHistory->state.scrollX;
1521 offsetY = fileFromHistory->state.scrollY;
1522 }
1523
1524 if (gUseFitz) {
1525 win->dm = DisplayModelFitz_CreateFromFileName(fileName,
1526 totalDrawAreaSize, scrollbarYDx, scrollbarXDy, displayMode, startPage, win);
1527 } else {
1528 win->dm = DisplayModelSplash_CreateFromFileName(fileName,
1529 totalDrawAreaSize, scrollbarYDx, scrollbarXDy, displayMode, startPage, win);
1530 }
1531
1532 double zoomVirtual = DEFAULT_ZOOM;
1533 int rotation = DEFAULT_ROTATION;
1534 if (!win->dm) {
1535 if (!reuseExistingWindow && WindowInfoList_ExistsWithError()) {
1536 /* don't create more than one window with errors */
1537 WindowInfo_Delete(win);
1538 return NULL;
1539 }
1540 win->state = WS_ERROR_LOADING_PDF;
1541 DBG_OUT("failed to load file %s\n", fileName);
1542 //goto Exit; /* @note: because of "crosses initialization of [...]" */
1543 if (!reuseExistingWindow)
1544 WindowInfoList_Add(win);
1545 MenuToolbarUpdateStateForAllWindows();
1546 assert(win);
1547 DragAcceptFiles(win->hwndFrame, TRUE);
1548 DragAcceptFiles(win->hwndCanvas, TRUE);
1549 ShowWindow(win->hwndFrame, SW_SHOW);
1550 ShowWindow(win->hwndCanvas, SW_SHOW);
1551 UpdateWindow(win->hwndFrame);
1552 UpdateWindow(win->hwndCanvas);
1553 return win;
1554 }
1555
1556 win->dm->setAppData((void*)win);
1557
1558 if (!fileFromHistory) {
1559 AddFileToHistory(fileName);
1560 RebuildProgramMenus();
1561 }
1562
1563 /* TODO: if fileFromHistory, set the state based on gFileHistoryList node for
1564 this entry */
1565 win->state = WS_SHOWING_PDF;
1566 if (fileFromHistory) {
1567 zoomVirtual = fileFromHistory->state.zoomVirtual;
1568 rotation = fileFromHistory->state.rotation;
1569 }
1570
1571 UINT menuId = MenuIdFromVirtualZoom(zoomVirtual);
1572 ZoomMenuItemCheck(GetMenu(win->hwndFrame), menuId);
1573
1574 win->dm->relayout(zoomVirtual, rotation);
1575 if (!win->dm->validPageNo(startPage))
1576 startPage = 1;
1577 /* TODO: need to calculate proper offsetY, currently giving large offsetY
1578 remembered for continuous mode breaks things (makes all pages invisible) */
1579 offsetY = 0;
1580 /* TODO: make sure offsetX isn't bogus */
1581 win->dm->goToPage(startPage, offsetY, offsetX);
1582
1583 /* only resize the window if it's a newly opened window */
1584 if (!reuseExistingWindow && !fileFromHistory)
1585 WindowInfo_ResizeToPage(win, startPage);
1586
1587 if (reuseExistingWindow)
1588 WindowInfo_RedrawAll(win);
1589
1590 Exit:
1591 if (!reuseExistingWindow)
1592 WindowInfoList_Add(win);
1593 MenuToolbarUpdateStateForAllWindows();
1594 assert(win);
1595 DragAcceptFiles(win->hwndFrame, TRUE);
1596 DragAcceptFiles(win->hwndCanvas, TRUE);
1597 ShowWindow(win->hwndFrame, SW_SHOW);
1598 ShowWindow(win->hwndCanvas, SW_SHOW);
1599 UpdateWindow(win->hwndFrame);
1600 UpdateWindow(win->hwndCanvas);
1601 return win;
1602 }
1603
1604 static HFONT Win32_Font_GetSimple(HDC hdc, char *fontName, int fontSize)
1605 {
1606 HFONT font_dc;
1607 HFONT font;
1608 LOGFONT lf = {0};
1609
1610 font_dc = (HFONT)GetStockObject(SYSTEM_FONT);
1611 if (!GetObject(font_dc, sizeof(LOGFONT), &lf))
1612 return NULL;
1613
1614 lf.lfHeight = (LONG)-fontSize;
1615 lf.lfWidth = 0;
1616 //lf.lfHeight = -MulDiv(fontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1617 lf.lfItalic = FALSE;
1618 lf.lfUnderline = FALSE;
1619 lf.lfStrikeOut = FALSE;
1620 lf.lfCharSet = DEFAULT_CHARSET;
1621 lf.lfOutPrecision = OUT_TT_PRECIS;
1622 lf.lfQuality = DEFAULT_QUALITY;
1623 //lf.lfQuality = CLEARTYPE_QUALITY;
1624 lf.lfPitchAndFamily = DEFAULT_PITCH;
1625 strcpy_s((char*)lf.lfFaceName, LF_FACESIZE, fontName); /* @note: char* cast */
1626 lf.lfWeight = FW_DONTCARE;
1627 font = CreateFontIndirect(&lf);
1628 return font;
1629 }
1630
1631 static void Win32_Font_Delete(HFONT font)
1632 {
1633 DeleteObject(font);
1634 }
1635
1636 void DisplayModel::pageChanged(void)
1637 {
1638 WindowInfo *win = (WindowInfo*)appData();
1639 assert(win);
1640 if (!win) return;
1641
1642 #if 0
1643 if (!win->dmSplash->pdfDoc)
1644 return;
1645 #endif
1646
1647 int currPageNo = currentPageNo();
1648 int pageCount = win->dm->pageCount();
1649 const char *baseName = FilePath_GetBaseName(win->dm->fileName());
1650 if (pageCount <= 0)
1651 win_set_text(win->hwndFrame, (TCHAR*)baseName); /* @note: TCHAR* cast */
1652 else {
1653 char titleBuf[256];
1654 HRESULT hr = StringCchPrintfA(titleBuf, dimof(titleBuf), "%s page %d of %d", baseName, currPageNo, pageCount);
1655 win_set_text(win->hwndFrame, (TCHAR*)titleBuf); /* @note: TCHAR* cast */
1656 /* @note: FIXME: hr is unused ATM */
1657 }
1658 }
1659
1660 /* Call from non-UI thread to cause repainting of the display */
1661 static void triggerRepaintDisplayPotentiallyDelayed(WindowInfo *win, bool delayed)
1662 {
1663 assert(win);
1664 if (!win) return;
1665 if (delayed)
1666 PostMessage(win->hwndCanvas, WM_APP_REPAINT_DELAYED, 0, 0);
1667 else
1668 PostMessage(win->hwndCanvas, WM_APP_REPAINT_NOW, 0, 0);
1669 }
1670
1671 static void triggerRepaintDisplayNow(WindowInfo* win)
1672 {
1673 triggerRepaintDisplayPotentiallyDelayed(win, false);
1674 }
1675
1676 void DisplayModel::repaintDisplay(bool delayed)
1677 {
1678 WindowInfo* win = (WindowInfo*)appData();
1679 triggerRepaintDisplayPotentiallyDelayed(win, delayed);
1680 }
1681
1682 void DisplayModel::setScrollbarsState(void)
1683 {
1684 WindowInfo *win = (WindowInfo*)this->appData();
1685 assert(win);
1686 if (!win) return;
1687
1688 SCROLLINFO si = {0};
1689 si.cbSize = sizeof(si);
1690 si.fMask = SIF_ALL;
1691
1692 int canvasDx = _canvasSize.dxI();
1693 int canvasDy = _canvasSize.dyI();
1694 int drawAreaDx = drawAreaSize.dxI();
1695 int drawAreaDy = drawAreaSize.dyI();
1696
1697 if (drawAreaDx >= canvasDx) {
1698 si.nPos = 0;
1699 si.nMin = 0;
1700 si.nMax = 99;
1701 si.nPage = 100;
1702 } else {
1703 si.nPos = (int)areaOffset.x;
1704 si.nMin = 0;
1705 si.nMax = canvasDx-1;
1706 si.nPage = drawAreaDx;
1707 }
1708 SetScrollInfo(win->hwndCanvas, SB_HORZ, &si, TRUE);
1709
1710 if (drawAreaDy >= canvasDy) {
1711 si.nPos = 0;
1712 si.nMin = 0;
1713 si.nMax = 99;
1714 si.nPage = 100;
1715 } else {
1716 si.nPos = (int)areaOffset.y;
1717 si.nMin = 0;
1718 si.nMax = canvasDy-1;
1719 si.nPage = drawAreaDy;
1720 }
1721 SetScrollInfo(win->hwndCanvas, SB_VERT, &si, TRUE);
1722 }
1723
1724 static void WindowInfo_ResizeToWindow(WindowInfo *win)
1725 {
1726 assert(win);
1727 if (!win) return;
1728 assert(win->dm);
1729 if (!win->dm) return;
1730
1731 win->dm->changeTotalDrawAreaSize(win->winSize());
1732 }
1733
1734 void WindowInfo_ResizeToPage(WindowInfo *win, int pageNo)
1735 {
1736 bool fullScreen = false;
1737
1738 assert(win);
1739 if (!win) return;
1740 assert(win->dm);
1741 if (!win->dm)
1742 return;
1743
1744 /* TODO: should take current monitor into account? */
1745 HDC hdc = GetDC(win->hwndCanvas);
1746 int displayDx = GetDeviceCaps(hdc, HORZRES);
1747 int displayDy = GetDeviceCaps(hdc, VERTRES);
1748
1749 int dx, dy;
1750 if (fullScreen) {
1751 /* TODO: fullscreen not yet supported */
1752 assert(0);
1753 dx = displayDx;
1754 dy = displayDy;
1755 } else {
1756 assert(win->dm->validPageNo(pageNo));
1757 if (!win->dm->validPageNo(pageNo))
1758 return;
1759 PdfPageInfo *pageInfo = win->dm->getPageInfo(pageNo);
1760 assert(pageInfo);
1761 if (!pageInfo)
1762 return;
1763 DisplaySettings *displaySettings = globalDisplaySettings();
1764 dx = pageInfo->currDx + displaySettings->paddingPageBorderLeft + displaySettings->paddingPageBorderRight;
1765 dy = pageInfo->currDy + displaySettings->paddingPageBorderTop + displaySettings->paddingPageBorderBottom;
1766 if (dx > displayDx - 10)
1767 dx = displayDx - 10;
1768 if (dy > displayDy - 10)
1769 dy = displayDy - 10;
1770 }
1771
1772 WinResizeClientArea(win->hwndCanvas, dx, dy);
1773 }
1774
1775 static void WindowInfo_ToggleZoom(WindowInfo *win)
1776 {
1777 DisplayModel * dm;
1778
1779 assert(win);
1780 if (!win) return;
1781
1782 dm = win->dm;
1783 assert(dm);
1784 if (!dm) return;
1785
1786 if (ZOOM_FIT_PAGE == dm->zoomVirtual())
1787 dm->setZoomVirtual(ZOOM_FIT_WIDTH);
1788 else if (ZOOM_FIT_WIDTH == dm->zoomVirtual())
1789 dm->setZoomVirtual(ZOOM_FIT_PAGE);
1790 }
1791
1792 static BOOL WindowInfo_PdfLoaded(WindowInfo *win)
1793 {
1794 assert(win);
1795 if (!win) return FALSE;
1796 if (!win->dm) return FALSE;
1797 #if 0
1798 assert(win->dmSplash->pdfDoc);
1799 assert(win->dmSplash->pdfDoc->isOk());
1800 #endif
1801 return TRUE;
1802 }
1803
1804 int WindowsVerMajor()
1805 {
1806 DWORD version = GetVersion();
1807 return (int)(version & 0xFF);
1808 }
1809
1810 int WindowsVerMinor()
1811 {
1812 DWORD version = GetVersion();
1813 return (int)((version & 0xFF00) >> 8);
1814 }
1815
1816 bool WindowsVer2000OrGreater()
1817 {
1818 if (WindowsVerMajor() >= 5)
1819 return true;
1820 return false;
1821 }
1822
1823 static bool AlreadyRegisteredForPdfExtentions(void)
1824 {
1825 bool registered = false;
1826 HKEY key = NULL;
1827 char nameBuf[sizeof(APP_NAME)+8];
1828 DWORD cbNameBuf = sizeof(nameBuf);
1829 DWORD keyType;
1830
1831 /* HKEY_CLASSES_ROOT\.pdf */
1832 if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT(".pdf"), 0, KEY_QUERY_VALUE, &key)) /* @note: TEXT() cast */
1833 return false;
1834
1835 if (ERROR_SUCCESS != RegQueryValueEx(key, NULL, NULL, &keyType, (LPBYTE)nameBuf, &cbNameBuf))
1836 goto Exit;
1837
1838 if (REG_SZ != keyType)
1839 goto Exit;
1840
1841 if (cbNameBuf != sizeof(APP_NAME))
1842 goto Exit;
1843
1844 if (0 == memcmp(APP_NAME, nameBuf, sizeof(APP_NAME)))
1845 registered = true;
1846
1847 Exit:
1848 RegCloseKey(key);
1849 return registered;
1850 }
1851
1852 static void AssociateExeWithPdfExtensions()
1853 {
1854 char tmp[256];
1855 HKEY key = NULL, kicon = NULL, kshell = NULL, kopen = NULL, kcmd = NULL;
1856 DWORD disp;
1857 HRESULT hr;
1858
1859 char * exePath = ExePathGet();
1860 assert(exePath);
1861 if (!exePath) return;
1862
1863 HKEY hkeyToUse = HKEY_CURRENT_USER;
1864 if (WindowsVer2000OrGreater())
1865 hkeyToUse = HKEY_LOCAL_MACHINE;
1866
1867 /* key\.pdf */ /* @note: TEXT() && TCHAR* casts */
1868 if (RegCreateKeyEx(hkeyToUse,
1869 TEXT(".pdf"), 0, NULL, REG_OPTION_NON_VOLATILE,
1870 KEY_WRITE, NULL, &key, &disp))
1871 goto Exit;
1872
1873 if (RegSetValueEx(key, TEXT(""), 0, REG_SZ, (const BYTE*)APP_NAME, sizeof(APP_NAME)))
1874 goto Exit;
1875
1876 RegCloseKey(key);
1877 key = NULL;
1878
1879 /* key\APP_NAME */
1880 if (RegCreateKeyEx(hkeyToUse,
1881 APP_NAME, 0, NULL, REG_OPTION_NON_VOLATILE,
1882 KEY_WRITE, NULL, &key, &disp))
1883 goto Exit;
1884
1885 if (RegSetValueEx(key, TEXT(""), 0, REG_SZ, (const BYTE*)PDF_DOC_NAME, sizeof(PDF_DOC_NAME)))
1886 goto Exit;
1887
1888 /* key\APP_NAME\DefaultIcon */
1889 if (RegCreateKeyEx(key,
1890 TEXT("DefaultIcon"), 0, NULL, REG_OPTION_NON_VOLATILE,
1891 KEY_WRITE, NULL, &kicon, &disp))
1892 goto Exit;
1893
1894 /* Note: I don't understand why icon index has to be 0, but it just has to */
1895 hr = StringCchPrintfA(tmp, dimof(tmp), "%s,0", exePath);
1896 if (RegSetValueEx(kicon, TEXT(""), 0, REG_SZ, (const BYTE*)tmp, strlen(tmp)+1))
1897 goto Exit;
1898
1899 RegCloseKey(kicon);
1900 kicon = NULL;
1901
1902 /* HKEY_CLASSES_ROOT\APP_NAME\Shell\Open\Command */
1903 if (RegCreateKeyEx(key,
1904 TEXT("shell"), 0, NULL, REG_OPTION_NON_VOLATILE,
1905 KEY_WRITE, NULL, &kshell, &disp))
1906 goto Exit;
1907
1908 if (RegCreateKeyEx(kshell,
1909 TEXT("open"), 0, NULL, REG_OPTION_NON_VOLATILE,
1910 KEY_WRITE, NULL, &kopen, &disp))
1911 goto Exit;
1912
1913 if (RegCreateKeyEx(kopen,
1914 TEXT("command"), 0, NULL, REG_OPTION_NON_VOLATILE,
1915 KEY_WRITE, NULL, &kcmd, &disp))
1916 goto Exit;
1917
1918 hr = StringCchPrintfA(tmp, dimof(tmp), "\"%s\" \"%%1\"", exePath);
1919 if (RegSetValueEx(kcmd, TEXT(""), 0, REG_SZ, (const BYTE*)tmp, strlen(tmp)+1))
1920 goto Exit;
1921
1922 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, 0, 0);
1923
1924 Exit:
1925 if (kcmd)
1926 RegCloseKey(kcmd);
1927 if (kopen)
1928 RegCloseKey(kopen);
1929 if (kshell)
1930 RegCloseKey(kshell);
1931 if (key)
1932 RegCloseKey(key);
1933 free(exePath);
1934 }
1935
1936 static void RegisterForPdfExtentions(HWND hwnd)
1937 {
1938 if (AlreadyRegisteredForPdfExtentions())
1939 return;
1940
1941 if (IsRunningInPortableMode())
1942 return;
1943
1944 /* Ask user for permission, unless he previously said he doesn't want to
1945 see this dialog */
1946 if (!gPdfAssociateDontAskAgain) {
1947 int result = Dialog_PdfAssociate(hwnd, &gPdfAssociateDontAskAgain);
1948 if (DIALOG_NO_PRESSED == result) {
1949 gPdfAssociateShouldAssociate = FALSE;
1950 } else {
1951 assert(DIALOG_OK_PRESSED == result);
1952 gPdfAssociateShouldAssociate = TRUE;
1953 }
1954 }
1955
1956 if (gPdfAssociateShouldAssociate)
1957 AssociateExeWithPdfExtensions();
1958 }
1959
1960 static void OnDropFiles(WindowInfo *win, HDROP hDrop)
1961 {
1962 int i;
1963 char filename[MAX_PATH];
1964 const int files_count = DragQueryFile(hDrop, DRAGQUERY_NUMFILES, 0, 0);
1965
1966 for (i = 0; i < files_count; i++)
1967 {
1968 DragQueryFile(hDrop, i, (TCHAR*)filename, MAX_PATH); /* @note: TCHAR* cast */
1969 LoadPdf(filename);
1970 }
1971 DragFinish(hDrop);
1972
1973 if (files_count > 0)
1974 WindowInfo_RedrawAll(win);
1975 }
1976
1977 static void DrawLineSimple(HDC hdc, int sx, int sy, int ex, int ey)
1978 {
1979 MoveToEx(hdc, sx, sy, NULL);
1980 LineTo(hdc, ex, ey);
1981 }
1982
1983 #if 0
1984 /* Draw caption area for a given window 'win' in the classic AmigaOS style */
1985 static void AmigaCaptionDraw(WindowInfo *win)
1986 {
1987 HGDIOBJ prevPen;
1988 HDC hdc = win->hdc;
1989
1990 assert(VS_AMIGA == gVisualStyle);
1991
1992 prevPen = SelectObject(hdc, ghpenWhite);
1993
1994 /* white */
1995 DrawLineSimple(hdc, 0, 0, win->winDx, 0);
1996 DrawLineSimple(hdc, 0, 1, win->winDx, 1);
1997
1998 /* white */
1999 DrawLineSimple(hdc, 0, 4, win->winDx, 4);
2000 DrawLineSimple(hdc, 0, 5, win->winDx, 5);
2001
2002 /* white */
2003 DrawLineSimple(hdc, 0, 8, win->winDx, 8);
2004 DrawLineSimple(hdc, 0, 9, win->winDx, 9);
2005
2006 /* white */
2007 DrawLineSimple(hdc, 0, 12, win->winDx, 12);
2008 DrawLineSimple(hdc, 0, 13, win->winDx, 13);
2009
2010 /* white */
2011 DrawLineSimple(hdc, 0, 16, win->winDx, 16);
2012 DrawLineSimple(hdc, 0, 17, win->winDx, 17);
2013 DrawLineSimple(hdc, 0, 18, win->winDx, 18);
2014
2015 SelectObject(hdc, ghpenBlue);
2016
2017 /* blue */
2018 DrawLineSimple(hdc, 0, 2, win->winDx, 2);
2019 DrawLineSimple(hdc, 0, 3, win->winDx, 3);
2020
2021 /* blue */
2022 DrawLineSimple(hdc, 0, 6, win->winDx, 6);
2023 DrawLineSimple(hdc, 0, 7, win->winDx, 7);
2024
2025 /* blue */
2026 DrawLineSimple(hdc, 0, 10, win->winDx, 10);
2027 DrawLineSimple(hdc, 0, 11, win->winDx, 11);
2028
2029 /* blue */
2030 DrawLineSimple(hdc, 0, 14, win->winDx, 14);
2031 DrawLineSimple(hdc, 0, 15, win->winDx, 15);
2032
2033 SelectObject(hdc, prevPen);
2034 }
2035 #endif
2036
2037 static void WinResizeIfNeeded(WindowInfo *win, bool resizeWindow=true)
2038 {
2039 RECT rc;
2040 GetClientRect(win->hwndCanvas, &rc);
2041 int win_dx = rect_dx(&rc);
2042 int win_dy = rect_dy(&rc);
2043
2044 if (win->hdcToDraw &&
2045 (win_dx == win->winDx()) &&
2046 (win_dy == win->winDy()))
2047 {
2048 return;
2049 }
2050
2051 WindowInfo_DoubleBuffer_New(win);
2052 if (resizeWindow)
2053 WindowInfo_ResizeToWindow(win);
2054 }
2055
2056 static void PostBenchNextAction(HWND hwnd)
2057 {
2058 PostMessage(hwnd, MSG_BENCH_NEXT_ACTION, 0, 0);
2059 }
2060
2061 static void OnBenchNextAction(WindowInfo *win)
2062 {
2063 if (!win->dm)
2064 return;
2065
2066 if (win->dm->goToNextPage(0))
2067 PostBenchNextAction(win->hwndFrame);
2068 }
2069
2070 static void DrawCenteredText(HDC hdc, RECT *r, char *txt)
2071 {
2072 SetBkMode(hdc, TRANSPARENT);
2073 DrawText(hdc, (TCHAR*)txt, strlen(txt), r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); /* @note: TCHAR* cast */
2074 }
2075
2076 static void SeeLastError(void) {
2077 char *msgBuf = NULL;
2078 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
2079 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2080 (LPSTR) &msgBuf, 0, NULL);
2081 if (!msgBuf) return;
2082 printf("SeeLastError(): %s\n", msgBuf);
2083 OutputDebugStringA(msgBuf);
2084 LocalFree(msgBuf);
2085 }
2086
2087 static void PaintTransparentRectangle(WindowInfo *win, HDC hdc, RectI *rect) {
2088 HBITMAP hbitmap; // bitmap handle
2089 BITMAPINFO bmi; // bitmap header
2090 VOID *pvBits; // pointer to DIB section
2091 BLENDFUNCTION bf; // structure for alpha blending
2092 HDC rectDC = CreateCompatibleDC(hdc);
2093 const DWORD selectionColorYellow = 0xfff5fc0c;
2094 const DWORD selectionColorBlack = 0xff000000;
2095 const int margin = 1;
2096
2097 ZeroMemory(&bmi, sizeof(BITMAPINFO));
2098
2099 bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
2100 bmi.bmiHeader.biWidth = rect->dx;
2101 bmi.bmiHeader.biHeight = rect->dy;
2102 bmi.bmiHeader.biPlanes = 1;
2103 bmi.bmiHeader.biBitCount = 32;
2104 bmi.bmiHeader.biCompression = BI_RGB;
2105 bmi.bmiHeader.biSizeImage = rect->dx * rect->dy * 4;
2106
2107 hbitmap = CreateDIBSection (rectDC, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0);
2108 SelectObject(rectDC, hbitmap);
2109
2110 for (int y = 0; y < rect->dy; y++) {
2111 for (int x = 0; x < rect->dx; x++) {
2112 if (x < margin || x > rect->dx - margin - 1
2113 || y < margin || y > rect->dy - margin - 1)
2114 ((UINT32 *)pvBits)[x + y * rect->dx] = selectionColorBlack;
2115 else
2116 ((UINT32 *)pvBits)[x + y * rect->dx] = selectionColorYellow;
2117 }
2118 }
2119 bf.BlendOp = AC_SRC_OVER;
2120 bf.BlendFlags = 0;
2121 bf.SourceConstantAlpha = 0x5f;
2122 bf.AlphaFormat = AC_SRC_ALPHA;
2123
2124 /*if (!AlphaBlend(hdc, rect->x, rect->y, rect->dx, rect->dy,
2125 rectDC, 0, 0, rect->dx, rect->dy, bf))
2126 DBG_OUT("AlphaBlending error\n");*/
2127 /* @note: error: 'AlphaBlend' was not declared in this scope; even with WINVER: 0x0500 set in rbuild file, weird; @FIXME */
2128 DeleteObject (hbitmap);
2129 DeleteDC (rectDC);
2130 }
2131
2132 static void PaintSelection (WindowInfo *win, HDC hdc) {
2133 if (win->mouseAction == MA_SELECTING) {
2134 // during selecting
2135 RectI selRect;
2136
2137 selRect.x = min (win->selectionRect.x,
2138 win->selectionRect.x + win->selectionRect.dx);
2139 selRect.y = min (win->selectionRect.y,
2140 win->selectionRect.y + win->selectionRect.dy);
2141 selRect.dx = abs (win->selectionRect.dx);
2142 selRect.dy = abs (win->selectionRect.dy);
2143
2144 if (selRect.dx != 0 && selRect.dy != 0)
2145 PaintTransparentRectangle (win, hdc, &selRect);
2146 } else {
2147 // after selection is done
2148 SelectionOnPage *selOnPage = win->selectionOnPage;
2149 // TODO: Move recalcing to better place
2150 RecalcSelectionPosition(win);
2151 while (selOnPage != NULL) {
2152 if (selOnPage->selectionCanvas.dx != 0 && selOnPage->selectionCanvas.dy != 0)
2153 PaintTransparentRectangle(win, hdc, &selOnPage->selectionCanvas);
2154 selOnPage = selOnPage->next;
2155 }
2156 }
2157 }
2158
2159 static void WindowInfo_Paint(WindowInfo *win, HDC hdc, PAINTSTRUCT *ps)
2160 {
2161 RECT bounds;
2162 RenderedBitmap * renderedBmp = NULL;
2163
2164 assert(win);
2165 if (!win) return;
2166 DisplayModel* dm = win->dm;
2167 assert(dm);
2168 if (!dm) return;
2169 #if 0 // TODO: write the equivalent dm->isOk() ?
2170 assert(dm->pdfDoc);
2171 if (!dm->pdfDoc) return;
2172 #endif
2173
2174 assert(win->hdcToDraw);
2175 hdc = win->hdcToDraw;
2176
2177 FillRect(hdc, &(ps->rcPaint), gBrushBg);
2178
2179 DBG_OUT("WindowInfo_Paint() ");
2180 for (int pageNo = 1; pageNo <= dm->pageCount(); ++pageNo) {
2181 PdfPageInfo *pageInfo = dm->getPageInfo(pageNo);
2182 if (!pageInfo->visible)
2183 continue;
2184 assert(pageInfo->shown);
2185 if (!pageInfo->shown)
2186 continue;
2187
2188 //BitmapCacheEntry *entry = BitmapCache_Find(dm, pageNo, dm->zoomReal(), dm->rotation());
2189 BitmapCacheEntry *entry = BitmapCache_Find(dm, pageNo);
2190 if (entry) {
2191 if ((dm->rotation() != entry->rotation) || (dm->zoomReal() != entry->zoomLevel))
2192 entry = NULL;
2193 else
2194 renderedBmp = entry->bitmap;
2195 }
2196
2197 if (!renderedBmp)
2198 DBG_OUT(" missing bitmap on visible page %d\n", pageNo);
2199
2200 int xSrc = (int)pageInfo->bitmapX;
2201 int ySrc = (int)pageInfo->bitmapY;
2202 int bmpDx = (int)pageInfo->bitmapDx;
2203 int bmpDy = (int)pageInfo->bitmapDy;
2204 int xDest = (int)pageInfo->screenX;
2205 int yDest = (int)pageInfo->screenY;
2206
2207 if (!entry) {
2208 /* TODO: assert is queued for rendering ? */
2209 HFONT fontRightTxt = Win32_Font_GetSimple(hdc, "Tahoma", 14);
2210 HFONT origFont = (HFONT)SelectObject(hdc, fontRightTxt); /* Just to remember the orig font */
2211 bounds.left = xDest;
2212 bounds.top = yDest;
2213 bounds.right = xDest + bmpDx;
2214 bounds.bottom = yDest + bmpDy;
2215 FillRect(hdc, &bounds, gBrushWhite);
2216 DrawCenteredText(hdc, &bounds, "Please wait - rendering...");
2217 DBG_OUT("drawing empty %d ", pageNo);
2218 if (origFont)
2219 SelectObject(hdc, origFont);
2220 Win32_Font_Delete(fontRightTxt);
2221 continue;
2222 }
2223
2224 if (BITMAP_CANNOT_RENDER == renderedBmp) {
2225 bounds.left = xDest;
2226 bounds.top = yDest;
2227 bounds.right = xDest + bmpDx;
2228 bounds.bottom = yDest + bmpDy;
2229 FillRect(hdc, &bounds, gBrushWhite);
2230 DrawCenteredText(hdc, &bounds, "Couldn't render the page");
2231 continue;
2232 }
2233
2234 DBG_OUT("page %d ", pageNo);
2235
2236 int renderedBmpDx = renderedBmp->dx();
2237 int renderedBmpDy = renderedBmp->dy();
2238 int currPageDx = pageInfo->currDx;
2239 int currPageDy = pageInfo->currDy;
2240 HBITMAP hbmp = renderedBmp->createDIBitmap(hdc);
2241 if (!hbmp)
2242 continue;
2243
2244 HDC bmpDC = CreateCompatibleDC(hdc);
2245 if (bmpDC) {
2246 SelectObject(bmpDC, hbmp);
2247 #if 0
2248 if ((currPageDx != renderedBmpDx) || (currPageDy != renderedBmpDy))
2249 StretchBlt(hdc, xDest, yDest, bmpDx, bmpDy, bmpDC, xSrc, ySrc, renderedBmpDx, renderedBmpDy, SRCCOPY);
2250 else
2251 #endif
2252 BitBlt(hdc, xDest, yDest, bmpDx, bmpDy, bmpDC, xSrc, ySrc, SRCCOPY);
2253 DeleteDC(bmpDC);
2254 }
2255 DeleteObject(hbmp);
2256 }
2257
2258 if (win->showSelection)
2259 PaintSelection(win, hdc);
2260
2261 DBG_OUT("\n");
2262 if (!gDebugShowLinks)
2263 return;
2264
2265 RectI drawAreaRect;
2266 /* debug code to visualize links */
2267 drawAreaRect.x = (int)dm->areaOffset.x;
2268 drawAreaRect.y = (int)dm->areaOffset.y;
2269 drawAreaRect.dx = dm->drawAreaSize.dxI();
2270 drawAreaRect.dy = dm->drawAreaSize.dyI();
2271
2272 for (int linkNo = 0; linkNo < dm->linkCount(); ++linkNo) {
2273 PdfLink *pdfLink = dm->link(linkNo);
2274
2275 RectI rectLink, intersect;
2276 rectLink.x = pdfLink->rectCanvas.x;
2277 rectLink.y = pdfLink->rectCanvas.y;
2278 rectLink.dx = pdfLink->rectCanvas.dx;
2279 rectLink.dy = pdfLink->rectCanvas.dy;
2280
2281 if (RectI_Intersect(&rectLink, &drawAreaRect, &intersect)) {
2282 RECT rectScreen;
2283 rectScreen.left = (LONG) ((double)intersect.x - dm->areaOffset.x);
2284 rectScreen.top = (LONG) ((double)intersect.y - dm->areaOffset.y);
2285 rectScreen.right = rectScreen.left + rectLink.dx;
2286 rectScreen.bottom = rectScreen.top + rectLink.dy;
2287 FillRect(hdc, &rectScreen, gBrushLinkDebug);
2288 DBG_OUT(" link on screen rotate=%d, (x=%d, y=%d, dx=%d, dy=%d)\n",
2289 dm->rotation() + dm->pagesInfo[pdfLink->pageNo-1].rotation,
2290 rectScreen.left, rectScreen.top, rect_dx(&rectScreen), rect_dy(&rectScreen));
2291 }
2292 }
2293 }
2294
2295 /* TODO: change the name to DrawAbout.
2296 Draws the about screen a remember some state for hyperlinking.
2297 It transcribes the design I did in graphics software - hopeless
2298 to understand without seeing the design. */
2299 #define ABOUT_RECT_PADDING 8
2300 #define ABOUT_RECT_BORDER_DX_DY 4
2301 #define ABOUT_LINE_OUTER_SIZE 2
2302 #define ABOUT_LINE_RECT_SIZE 5
2303 #define ABOUT_LINE_SEP_SIZE 1
2304 #define ABOUT_LEFT_RIGHT_SPACE_DX 8
2305 #define ABOUT_MARGIN_DX 10
2306 #define ABOUT_BOX_MARGIN_DY 6
2307
2308 #define ABOUT_BORDER_COL COL_BLACK
2309
2310 #define SUMATRA_TXT "Sumatra PDF"
2311 #define SUMATRA_TXT_FONT "Arial Black"
2312 #define SUMATRA_TXT_FONT_SIZE 24
2313 #define BETA_TXT "Beta v0.7"
2314 #define BETA_TXT_FONT "Arial Black"
2315 #define BETA_TXT_FONT_SIZE 12
2316 #define LEFT_TXT_FONT "Arial"
2317 #define LEFT_TXT_FONT_SIZE 12
2318 #define RIGHT_TXT_FONT "Arial Black"
2319 #define RIGHT_TXT_FONT_SIZE 12
2320
2321 #define ABOUT_BG_COLOR RGB(255,242,0)
2322 #define ABOUT_RECT_BG_COLOR RGB(247,148,29)
2323
2324 #define ABOUT_TXT_DY 6
2325
2326 typedef struct AboutLayoutInfoEl {
2327 /* static data, must be provided */
2328 const char * leftTxt;
2329 const char * rightTxt;
2330 const char * url;
2331
2332 /* data calculated by the layout */
2333 int leftTxtPosX;
2334 int leftTxtPosY;
2335 int leftTxtDx;
2336 int leftTxtDy;
2337
2338 int rightTxtPosX;
2339 int rightTxtPosY;
2340 int rightTxtDx;
2341 int rightTxtDy;
2342 } AboutLayoutInfoEl;
2343
2344 AboutLayoutInfoEl gAboutLayoutInfo[] = {
2345 { "design", "Krzysztof Kowalczyk", "http://blog.kowalczyk.info",
2346 0, 0, 0, 0, 0, 0, 0, 0 },
2347
2348 { "programming", "Krzysztof Kowalczyk", "http://blog.kowalczyk.info",
2349 0, 0, 0, 0, 0, 0, 0, 0 },
2350
2351 { "pdf rendering 1", "poppler + xpdf", "http://poppler.freedesktop.org/",
2352 0, 0, 0, 0, 0, 0, 0, 0 },
2353
2354 { "pdf rendering 2", "MuPDF", "http://ccxvii.net/apparition/",
2355 0, 0, 0, 0, 0, 0, 0, 0 },
2356
2357 { "license", "GPL v2", "http://www.gnu.org/copyleft/gpl.html",
2358 0, 0, 0, 0, 0, 0, 0, 0 },
2359
2360 { "website", "http://blog.kowalczyk.info/software/sumatra", "http://blog.kowalczyk.info/software/sumatrapdf",
2361 0, 0, 0, 0, 0, 0, 0, 0 },
2362
2363 { "forums", "http://blog.kowalczyk.info/forum_sumatra", "http://blog.kowalczyk.info/forum_sumatra",
2364 0, 0, 0, 0, 0, 0, 0, 0 },
2365
2366 { "program icon", "Goce Mitevski", "http://monsteer.deviantart.com",
2367 0, 0, 0, 0, 0, 0, 0, 0 },
2368
2369 { "toolbar icons", "Mark James", "http://www.famfamfam.com/lab/icons/silk/",
2370 0, 0, 0, 0, 0, 0, 0, 0 },
2371
2372 { NULL, NULL, NULL,
2373 0, 0, 0, 0, 0, 0, 0, 0 }
2374 };
2375
2376 static const char *AboutGetLink(WindowInfo *win, int x, int y)
2377 {
2378 for (int i = 0; gAboutLayoutInfo[i].leftTxt; i++) {
2379 if ((x < gAboutLayoutInfo[i].rightTxtPosX) ||
2380 (x > gAboutLayoutInfo[i].rightTxtPosX + gAboutLayoutInfo[i].rightTxtDx))
2381 continue;
2382 if ((y < gAboutLayoutInfo[i].rightTxtPosY) ||
2383 (y > gAboutLayoutInfo[i].rightTxtPosY + gAboutLayoutInfo[i].rightTxtDy))
2384 continue;
2385 return gAboutLayoutInfo[i].url;
2386 }
2387 return NULL;
2388 }
2389
2390 static void DrawAbout(HWND hwnd, HDC hdc, PAINTSTRUCT *ps)
2391 {
2392 RECT rcTmp;
2393 SIZE txtSize;
2394 int totalDx, totalDy;
2395 int leftDy, rightDy;
2396 int leftLargestDx, rightLargestDx;
2397 int sumatraPdfTxtDx, sumatraPdfTxtDy;
2398 int betaTxtDx, betaTxtDy;
2399 int linePosX, linePosY, lineDy;
2400 int currY;
2401 int fontDyDiff;
2402 int offX, offY;
2403 int x, y;
2404 int boxDy;
2405
2406 DString str;
2407 DStringInit(&str);
2408
2409 HBRUSH brushBg = CreateSolidBrush(ABOUT_BG_COLOR);
2410 HBRUSH brushRectBg = CreateSolidBrush(ABOUT_RECT_BG_COLOR);
2411
2412 HPEN penRectBorder = CreatePen(PS_SOLID, ABOUT_RECT_BORDER_DX_DY, COL_BLACK);
2413 HPEN penBorder = CreatePen(PS_SOLID, ABOUT_LINE_OUTER_SIZE, COL_BLACK);
2414 HPEN penDivideLine = CreatePen(PS_SOLID, ABOUT_LINE_SEP_SIZE, COL_BLACK);
2415
2416 RECT rc;
2417 GetClientRect(hwnd, &rc);
2418
2419 int areaDx = rect_dx(&rc);
2420 int areaDy = rect_dy(&rc);
2421
2422 HFONT fontSumatraTxt = Win32_Font_GetSimple(hdc, SUMATRA_TXT_FONT, SUMATRA_TXT_FONT_SIZE);
2423 HFONT fontBetaTxt = Win32_Font_GetSimple(hdc, BETA_TXT_FONT, BETA_TXT_FONT_SIZE);
2424 HFONT fontLeftTxt = Win32_Font_GetSimple(hdc, LEFT_TXT_FONT, LEFT_TXT_FONT_SIZE);
2425 HFONT fontRightTxt = Win32_Font_GetSimple(hdc, RIGHT_TXT_FONT, RIGHT_TXT_FONT_SIZE);
2426
2427 HFONT origFont = (HFONT)SelectObject(hdc, fontSumatraTxt); /* Just to remember the orig font */
2428
2429 SetBkMode(hdc, TRANSPARENT);
2430
2431 /* Layout stuff */
2432 const char *txt = SUMATRA_TXT;
2433 GetTextExtentPoint32(hdc, (TCHAR*)txt, strlen(txt), &txtSize); /* @note: TCHAR* cast */
2434 sumatraPdfTxtDx = txtSize.cx;
2435 sumatraPdfTxtDy = txtSize.cy;
2436
2437 boxDy = sumatraPdfTxtDy + ABOUT_BOX_MARGIN_DY * 2;
2438 txt = BETA_TXT;
2439 GetTextExtentPoint32(hdc, (TCHAR*)txt, strlen(txt), &txtSize); /* @note: TCHAR* cast */
2440 betaTxtDx = txtSize.cx;
2441 betaTxtDy = txtSize.cy;
2442
2443 (HFONT)SelectObject(hdc, fontLeftTxt);
2444 leftLargestDx = 0;
2445 leftDy = 0;
2446 for (int i = 0; gAboutLayoutInfo[i].leftTxt != NULL; i++) {
2447 txt = gAboutLayoutInfo[i].leftTxt;
2448 GetTextExtentPoint32(hdc, (TCHAR*)txt, strlen(txt), &txtSize); /* @note: TCHAR* cast */
2449 gAboutLayoutInfo[i].leftTxtDx = (int)txtSize.cx;
2450 gAboutLayoutInfo[i].leftTxtDy = (int)txtSize.cy;
2451 if (0 == i)
2452 leftDy = gAboutLayoutInfo[i].leftTxtDy;
2453 else
2454 assert(leftDy == gAboutLayoutInfo[i].leftTxtDy);
2455 if (leftLargestDx < gAboutLayoutInfo[i].leftTxtDx)
2456 leftLargestDx = gAboutLayoutInfo[i].leftTxtDx;
2457 }
2458
2459 (HFONT)SelectObject(hdc, fontRightTxt);
2460 rightLargestDx = 0;
2461 rightDy = 0;
2462 for (int i = 0; gAboutLayoutInfo[i].leftTxt != NULL; i++) {
2463 txt = gAboutLayoutInfo[i].rightTxt;
2464 GetTextExtentPoint32(hdc, (TCHAR*)txt, strlen(txt), &txtSize); /* @note: TCHAR* cast */
2465 gAboutLayoutInfo[i].rightTxtDx = (int)txtSize.cx;
2466 gAboutLayoutInfo[i].rightTxtDy = (int)txtSize.cy;
2467 if (0 == i)
2468 rightDy = gAboutLayoutInfo[i].rightTxtDy;
2469 else
2470 assert(rightDy == gAboutLayoutInfo[i].rightTxtDy);
2471 if (rightLargestDx < gAboutLayoutInfo[i].rightTxtDx)
2472 rightLargestDx = gAboutLayoutInfo[i].rightTxtDx;
2473 }
2474
2475 fontDyDiff = (rightDy - leftDy) / 2;
2476
2477 /* in the x order */
2478 totalDx = ABOUT_LINE_OUTER_SIZE + ABOUT_MARGIN_DX + leftLargestDx;
2479 totalDx += ABOUT_LEFT_RIGHT_SPACE_DX + ABOUT_LINE_SEP_SIZE + ABOUT_LEFT_RIGHT_SPACE_DX;
2480 totalDx += rightLargestDx + ABOUT_MARGIN_DX + ABOUT_LINE_OUTER_SIZE;
2481
2482 totalDy = 0;
2483 totalDy += boxDy;
2484 totalDy += ABOUT_LINE_OUTER_SIZE;
2485 totalDy += (dimof(gAboutLayoutInfo)-1) * (rightDy + ABOUT_TXT_DY);
2486 totalDy += ABOUT_LINE_OUTER_SIZE + 4;
2487
2488 offX = (areaDx - totalDx) / 2;
2489 offY = (areaDy - totalDy) / 2;
2490
2491 rcTmp.left = offX;
2492 rcTmp.top = offY;
2493 rcTmp.right = totalDx + offX;
2494 rcTmp.bottom = totalDy + offY;
2495
2496 FillRect(hdc, &rc, brushBg);
2497
2498 SelectObject(hdc, brushBg);
2499 SelectObject(hdc, penBorder);
2500
2501 Rectangle(hdc, offX, offY + ABOUT_LINE_OUTER_SIZE, offX + totalDx, offY + boxDy + ABOUT_LINE_OUTER_SIZE);
2502
2503 SetTextColor(hdc, ABOUT_BORDER_COL);
2504 (HFONT)SelectObject(hdc, fontSumatraTxt);
2505 x = offX + (totalDx - sumatraPdfTxtDx) / 2;
2506 y = offY + (boxDy - sumatraPdfTxtDy) / 2;
2507 txt = SUMATRA_TXT;
2508 TextOut(hdc, x, y, (TCHAR*)txt, strlen(txt)); /* @note: TCHAR* cast */
2509
2510 //SetTextColor(hdc, ABOUT_RECT_BG_COLOR);
2511 (HFONT)SelectObject(hdc, fontBetaTxt);
2512 //SelectObject(hdc, brushRectBg);
2513 x = offX + (totalDx - sumatraPdfTxtDx) / 2 + sumatraPdfTxtDx + 6;
2514 y = offY + (boxDy - sumatraPdfTxtDy) / 2;
2515 txt = BETA_TXT;
2516 TextOut(hdc, x, y, (TCHAR*)txt, strlen(txt)); /* @note: TCHAR* cast */
2517 SetTextColor(hdc, ABOUT_BORDER_COL);
2518
2519 offY += boxDy;
2520 Rectangle(hdc, offX, offY, offX + totalDx, offY + totalDy - boxDy);
2521
2522 linePosX = ABOUT_LINE_OUTER_SIZE + ABOUT_MARGIN_DX + leftLargestDx + ABOUT_LEFT_RIGHT_SPACE_DX;
2523 linePosY = 4;
2524 lineDy = (dimof(gAboutLayoutInfo)-1) * (rightDy + ABOUT_TXT_DY);
2525
2526 /* render text on the left*/
2527 currY = linePosY;
2528 (HFONT)SelectObject(hdc, fontLeftTxt);
2529 for (int i = 0; gAboutLayoutInfo[i].leftTxt != NULL; i++) {
2530 txt = gAboutLayoutInfo[i].leftTxt;
2531 x = linePosX + offX - ABOUT_LEFT_RIGHT_SPACE_DX - gAboutLayoutInfo[i].leftTxtDx;
2532 y = currY + fontDyDiff + offY;
2533 gAboutLayoutInfo[i].leftTxtPosX = x;
2534 gAboutLayoutInfo[i].leftTxtPosY = y;
2535 TextOut(hdc, x, y, (TCHAR*)txt, strlen(txt)); /* @note: TCHAR* cast */
2536 currY += rightDy + ABOUT_TXT_DY;
2537 }
2538
2539 /* render text on the rigth */
2540 currY = linePosY;
2541 (HFONT)SelectObject(hdc, fontRightTxt);
2542 for (int i = 0; gAboutLayoutInfo[i].leftTxt != NULL; i++) {
2543 txt = gAboutLayoutInfo[i].rightTxt;
2544 x = linePosX + offX + ABOUT_LEFT_RIGHT_SPACE_DX;
2545 y = currY + offY;
2546 gAboutLayoutInfo[i].rightTxtPosX = x;
2547 gAboutLayoutInfo[i].rightTxtPosY = y;
2548 TextOut(hdc, x, y, (TCHAR*)txt, strlen(txt)); /* @note: TCHAR* cast */
2549 currY += rightDy + ABOUT_TXT_DY;
2550 }
2551
2552 SelectObject(hdc, penDivideLine);
2553 MoveToEx(hdc, linePosX + offX, linePosY + offY, NULL);
2554 LineTo(hdc, linePosX + offX, linePosY + lineDy + offY);
2555
2556 if (origFont)
2557 SelectObject(hdc, origFont);
2558
2559 Win32_Font_Delete(fontSumatraTxt);
2560 Win32_Font_Delete(fontBetaTxt);
2561 Win32_Font_Delete(fontLeftTxt);
2562 Win32_Font_Delete(fontRightTxt);
2563
2564 DeleteObject(brushBg);
2565 DeleteObject(brushRectBg);
2566 DeleteObject(penBorder);
2567 DeleteObject(penDivideLine);
2568 DeleteObject(penRectBorder);
2569 }
2570
2571 static void WinMoveDocBy(WindowInfo *win, int dx, int dy)
2572 {
2573 assert(win);
2574 if (!win) return;
2575 assert (WS_SHOWING_PDF == win->state);
2576 if (WS_SHOWING_PDF != win->state) return;
2577 assert(win->dm);
2578 if (!win->dm) return;
2579 assert(!win->linkOnLastButtonDown);
2580 if (win->linkOnLastButtonDown) return;
2581 if (0 != dx)
2582 win->dm->scrollXBy(dx);
2583 if (0 != dy)
2584 win->dm->scrollYBy(dy, FALSE);
2585 }
2586
2587 static void CopySelectionTextToClipboard(WindowInfo *win)
2588 {
2589 SelectionOnPage * selOnPage;
2590
2591 assert(win);
2592 if (!win) return;
2593
2594 if (!win->selectionOnPage) return;
2595
2596 HGLOBAL handle;
2597 unsigned short *ucsbuf;
2598 int ucsbuflen = 4096;
2599
2600 if (!OpenClipboard(NULL)) return;
2601
2602 EmptyClipboard();
2603
2604 handle = GlobalAlloc(GMEM_MOVEABLE, ucsbuflen * sizeof(unsigned short));
2605 if (!handle) {
2606 CloseClipboard();
2607 return;
2608 }
2609 ucsbuf = (unsigned short *) GlobalLock(handle);
2610
2611 selOnPage = win->selectionOnPage;
2612
2613 int copied = 0;
2614 while (selOnPage != NULL) {
2615 int charCopied = win->dm->getTextInRegion(selOnPage->pageNo,
2616 &selOnPage->selectionPage, ucsbuf + copied, ucsbuflen - copied - 1);
2617 copied += charCopied;
2618 if (ucsbuflen - copied == 1)
2619 break;
2620 selOnPage = selOnPage->next;
2621 }
2622 ucsbuf[copied] = 0;
2623
2624 GlobalUnlock(handle);
2625
2626 SetClipboardData(CF_UNICODETEXT, handle);
2627 CloseClipboard();
2628 }
2629
2630 static void DeleteOldSelectionInfo (WindowInfo *win) {
2631 SelectionOnPage *selOnPage = win->selectionOnPage;
2632 while (selOnPage != NULL) {
2633 SelectionOnPage *tmp = selOnPage->next;
2634 free(selOnPage);
2635 selOnPage = tmp;
2636 }
2637 win->selectionOnPage = NULL;
2638 }
2639
2640 static void ConvertSelectionRectToSelectionOnPage (WindowInfo *win) {
2641 RectI pageOnScreen, intersect;
2642
2643 for (int pageNo = win->dm->pageCount(); pageNo >= 1; --pageNo) {
2644 PdfPageInfo *pageInfo = win->dm->getPageInfo(pageNo);
2645 if (!pageInfo->visible)
2646 continue;
2647 assert(pageInfo->shown);
2648 if (!pageInfo->shown)
2649 continue;
2650
2651 pageOnScreen.x = pageInfo->screenX;
2652 pageOnScreen.y = pageInfo->screenY;
2653 pageOnScreen.dx = pageInfo->bitmapDx;
2654 pageOnScreen.dy = pageInfo->bitmapDy;
2655
2656 if (!RectI_Intersect(&win->selectionRect, &pageOnScreen, &intersect))
2657 continue;
2658
2659 /* selection intersects with a page <pageNo> on the screen */
2660 SelectionOnPage *selOnPage = (SelectionOnPage*)malloc(sizeof(SelectionOnPage));
2661 RectD_FromRectI(&selOnPage->selectionPage, &intersect);
2662
2663 win->dm->rectCvtScreenToUser (&selOnPage->pageNo, &selOnPage->selectionPage);
2664
2665 assert (pageNo == selOnPage->pageNo);
2666
2667 selOnPage->next = win->selectionOnPage;
2668 win->selectionOnPage = selOnPage;
2669 }
2670 }
2671
2672 static void OnMouseLeftButtonDown(WindowInfo *win, int x, int y)
2673 {
2674 assert(win);
2675 if (!win) return;
2676 if (WS_SHOWING_PDF == win->state && win->mouseAction == MA_IDLE) {
2677 assert(win->dm);
2678 if (!win->dm) return;
2679 win->linkOnLastButtonDown = win->dm->linkAtPosition(x, y);
2680 /* dragging mode only starts when we're not on a link */
2681 if (!win->linkOnLastButtonDown) {
2682 SetCapture(win->hwndCanvas);
2683 win->mouseAction = MA_DRAGGING;
2684 win->dragPrevPosX = x;
2685 win->dragPrevPosY = y;
2686 SetCursor(gCursorDrag);
2687 DBG_OUT(" dragging start, x=%d, y=%d\n", x, y);
2688 }
2689 } else if (WS_ABOUT == win->state) {
2690 win->url = AboutGetLink(win, x, y);
2691 }
2692 }
2693
2694 static void OnMouseLeftButtonUp(WindowInfo *win, int x, int y)
2695 {
2696 PdfLink * link;
2697 const char * url;
2698 int dragDx, dragDy;
2699
2700 assert(win);
2701 if (!win) return;
2702
2703 if (WS_ABOUT == win->state) {
2704 url = AboutGetLink(win, x, y);
2705 if (url == win->url)
2706 //LaunchBrowser(url);
2707 MessageBox(NULL,(TCHAR*)url, TEXT("Extern link blocked"), 0); /* @note: work-around to solve linking issue */
2708
2709 win->url = NULL;
2710 return;
2711 }
2712
2713 if (WS_SHOWING_PDF != win->state)
2714 return;
2715
2716 assert(win->dm);
2717 if (!win->dm) return;
2718
2719 if (win->mouseAction == MA_DRAGGING && (GetCapture() == win->hwndCanvas)) {
2720 dragDx = 0; dragDy = 0;
2721 dragDx = x - win->dragPrevPosX;
2722 dragDy = y - win->dragPrevPosY;
2723 DBG_OUT(" dragging ends, x=%d, y=%d, dx=%d, dy=%d\n", x, y, dragDx, dragDy);
2724 assert(!win->linkOnLastButtonDown);
2725 WinMoveDocBy(win, dragDx, -dragDy*2);
2726 win->dragPrevPosX = x;
2727 win->dragPrevPosY = y;
2728 win->mouseAction = MA_IDLE;
2729 SetCursor(gCursorArrow);
2730 ReleaseCapture();
2731 return;
2732 }
2733
2734 if (!win->linkOnLastButtonDown)
2735 return;
2736
2737 link = win->dm->linkAtPosition(x, y);
2738 if (link && (link == win->linkOnLastButtonDown))
2739 win->dm->handleLink(link);
2740 win->linkOnLastButtonDown = NULL;
2741 }
2742
2743 static void OnMouseMove(WindowInfo *win, int x, int y, WPARAM flags)
2744 {
2745 PdfLink * link;
2746 const char * url;
2747 int dragDx, dragDy;
2748
2749 assert(win);
2750 if (!win) return;
2751
2752 if (WS_SHOWING_PDF == win->state) {
2753 assert(win->dm);
2754 if (!win->dm) return;
2755 if (win->mouseAction == MA_SELECTING) {
2756 SetCursor(gCursorArrow);
2757 win->selectionRect.dx = x - win->selectionRect.x;
2758 win->selectionRect.dy = y - win->selectionRect.y;
2759 triggerRepaintDisplayNow(win);
2760 } else {
2761 if (win->mouseAction == MA_DRAGGING) {
2762 dragDx = 0; dragDy = 0;
2763 dragDx = -(x - win->dragPrevPosX);
2764 dragDy = -(y - win->dragPrevPosY);
2765 DBG_OUT(" drag move, x=%d, y=%d, dx=%d, dy=%d\n", x, y, dragDx, dragDy);
2766 WinMoveDocBy(win, dragDx, dragDy*2);
2767 win->dragPrevPosX = x;
2768 win->dragPrevPosY = y;
2769 return;
2770 }
2771 link = win->dm->linkAtPosition(x, y);
2772 if (link)
2773 SetCursor(gCursorHand);
2774 else
2775 SetCursor(gCursorArrow);
2776 }
2777 } else if (WS_ABOUT == win->state) {
2778 url = AboutGetLink(win, x, y);
2779 if (url) {
2780 SetCursor(gCursorHand);
2781 } else {
2782 SetCursor(gCursorArrow);
2783 }
2784 } else {
2785 // TODO: be more efficient, this only needs to be set once (I think)
2786 SetCursor(gCursorArrow);
2787 }
2788 }
2789
2790 static void OnMouseRightButtonDown(WindowInfo *win, int x, int y)
2791 {
2792 //DBG_OUT("Right button clicked on %d %d\n", x, y);
2793 assert (win);
2794 if (!win) return;
2795
2796 if (WS_SHOWING_PDF == win->state && win->mouseAction == MA_IDLE) {
2797 win->documentBlocked = true;
2798 DeleteOldSelectionInfo (win);
2799
2800 win->selectionRect.x = x;
2801 win->selectionRect.y = y;
2802 win->selectionRect.dx = 0;
2803 win->selectionRect.dy = 0;
2804 win->showSelection = true;
2805 win->mouseAction = MA_SELECTING;
2806
2807 triggerRepaintDisplayNow(win);
2808 }
2809 }
2810
2811 static void OnMouseRightButtonUp(WindowInfo *win, int x, int y)
2812 {
2813 assert (win);
2814 if (!win) return;
2815
2816 if (WS_SHOWING_PDF == win->state && win->mouseAction == MA_SELECTING) {
2817 assert (win->dm);
2818 if (!win->dm) return;
2819 win->documentBlocked = false;
2820
2821 win->selectionRect.dx = abs (x - win->selectionRect.x);
2822 win->selectionRect.dy = abs (y - win->selectionRect.y);
2823 win->selectionRect.x = min (win->selectionRect.x, x);
2824 win->selectionRect.y = min (win->selectionRect.y, y);
2825
2826 win->mouseAction = MA_IDLE;
2827 if (win->selectionRect.dx == 0 || win->selectionRect.dy == 0) {
2828 win->showSelection = false;
2829 } else {
2830 ConvertSelectionRectToSelectionOnPage (win);
2831 CopySelectionTextToClipboard (win);
2832 }
2833 triggerRepaintDisplayNow(win);
2834 }
2835 }
2836
2837 #define ABOUT_ANIM_TIMER_ID 15
2838
2839 static void AnimState_AnimStop(AnimState *state)
2840 {
2841 KillTimer(state->hwnd, ABOUT_ANIM_TIMER_ID);
2842 }
2843
2844 static void AnimState_NextFrame(AnimState *state)
2845 {
2846 state->frame += 1;
2847 InvalidateRect(state->hwnd, NULL, FALSE);
2848 UpdateWindow(state->hwnd);
2849 }
2850
2851 static void AnimState_AnimStart(AnimState *state, HWND hwnd, UINT freqInMs)
2852 {
2853 assert(IsWindow(hwnd));
2854 AnimState_AnimStop(state);
2855 state->frame = 0;
2856 state->hwnd = hwnd;
2857 SetTimer(state->hwnd, ABOUT_ANIM_TIMER_ID, freqInMs, NULL);
2858 AnimState_NextFrame(state);
2859 }
2860
2861 #define ANIM_FONT_NAME "Georgia"
2862 #define ANIM_FONT_SIZE_START 20
2863 #define SCROLL_SPEED 3
2864
2865 static void DrawAnim2(WindowInfo *win, HDC hdc, PAINTSTRUCT *ps)
2866 {
2867 AnimState * state = &(win->animState);
2868 DString txt;
2869 RECT rc;
2870 HFONT fontArial24 = NULL;
2871 HFONT origFont = NULL;
2872 int curFontSize;
2873 static int curTxtPosX = -1;
2874 static int curTxtPosY = -1;
2875 static int curDir = SCROLL_SPEED;
2876
2877 GetClientRect(win->hwndCanvas, &rc);
2878
2879 DStringInit(&txt);
2880
2881 if (-1 == curTxtPosX)
2882 curTxtPosX = 40;
2883 if (-1 == curTxtPosY)
2884 curTxtPosY = 25;
2885
2886 int areaDx = rect_dx(&rc);
2887 int areaDy = rect_dy(&rc);
2888
2889 #if 0
2890 if (state->frame % 24 <= 12) {
2891 curFontSize = ANIM_FONT_SIZE_START + (state->frame % 24);
2892 } else {
2893 curFontSize = ANIM_FONT_SIZE_START + 12 - (24 - (state->frame % 24));
2894 }
2895 #else
2896 curFontSize = ANIM_FONT_SIZE_START;
2897 #endif
2898
2899 curTxtPosY += curDir;
2900 if (curTxtPosY < 20)
2901 curDir = SCROLL_SPEED;
2902 else if (curTxtPosY > areaDy - 40)
2903 curDir = -SCROLL_SPEED;
2904
2905 fontArial24 = Win32_Font_GetSimple(hdc, ANIM_FONT_NAME, curFontSize);
2906 assert(fontArial24);
2907
2908 origFont = (HFONT)SelectObject(hdc, fontArial24);
2909
2910 SetBkMode(hdc, TRANSPARENT);
2911 FillRect(hdc, &rc, gBrushBg);
2912 //DStringSprintf(&txt, "Welcome to animation %d", state->frame);
2913 DStringSprintf(&txt, "Welcome to animation");
2914 //DrawText (hdc, txt.pString, -1, &rc, DT_SINGLELINE);
2915 TextOut(hdc, curTxtPosX, curTxtPosY, (TCHAR*)txt.pString, txt.length); /* @note: TCHAR* cast */
2916 WindowInfo_DoubleBuffer_Show(win, hdc);
2917 if (state->frame > 99)
2918 state->frame = 0;
2919
2920 if (origFont)
2921 SelectObject(hdc, origFont);
2922 Win32_Font_Delete(fontArial24);
2923 }
2924
2925 static void WindowInfo_DoubleBuffer_Resize_IfNeeded(WindowInfo *win)
2926 {
2927 WinResizeIfNeeded(win, false);
2928 }
2929
2930 static void OnPaintAbout(HWND hwnd)
2931 {
2932 PAINTSTRUCT ps;
2933 HDC hdc = BeginPaint(hwnd, &ps);
2934 SetBkMode(hdc, TRANSPARENT);
2935 DrawAbout(hwnd, hdc, &ps);
2936 EndPaint(hwnd, &ps);
2937 }
2938
2939 static void OnPaint(WindowInfo *win)
2940 {
2941 PAINTSTRUCT ps;
2942 HDC hdc = BeginPaint(win->hwndCanvas, &ps);
2943
2944 SetBkMode(hdc, TRANSPARENT);
2945 RECT rc;
2946 GetClientRect(win->hwndCanvas, &rc);
2947
2948 if (WS_ABOUT == win->state) {
2949 WindowInfo_DoubleBuffer_Resize_IfNeeded(win);
2950 DrawAbout(win->hwndCanvas, win->hdcToDraw, &ps);
2951 WindowInfo_DoubleBuffer_Show(win, hdc);
2952 } else if (WS_ERROR_LOADING_PDF == win->state) {
2953 HFONT fontRightTxt = Win32_Font_GetSimple(hdc, "Tahoma", 14);
2954 HFONT origFont = (HFONT)SelectObject(hdc, fontRightTxt); /* Just to remember the orig font */
2955 FillRect(hdc, &ps.rcPaint, gBrushBg);
2956 /* @note: translation function and it's related macros need some care */
2957 //DrawText(hdc, _TR("Error loading PDF file."), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
2958 DrawText(hdc, TEXT("Error loading PDF file."), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
2959 if (origFont)
2960 SelectObject(hdc, origFont);
2961 Win32_Font_Delete(fontRightTxt);
2962 } else if (WS_SHOWING_PDF == win->state) {
2963 //TODO: it might cause infinite loop due to showing/hiding scrollbars
2964 WinResizeIfNeeded(win);
2965 WindowInfo_Paint(win, hdc, &ps);
2966 #if 0
2967 if (VS_AMIGA == gVisualStyle)
2968 AmigaCaptionDraw(win);
2969 #endif
2970 WindowInfo_DoubleBuffer_Show(win, hdc);
2971 } else
2972 assert(0);
2973
2974 EndPaint(win->hwndCanvas, &ps);
2975 }
2976
2977 static void OnMenuExit(void)
2978 {
2979 Prefs_Save();
2980 PostQuitMessage(0);
2981 }
2982
2983 /* Close the document associated with window 'hwnd'.
2984 Closes the window unless this is the last window in which
2985 case it switches to empty window and disables the "File\Close"
2986 menu item. */
2987 static void CloseWindow(WindowInfo *win, bool quitIfLast)
2988 {
2989 assert(win);
2990 if (!win) return;
2991
2992 bool lastWindow = false;
2993 if (gRunningDLL) {
2994 lastWindow = true;
2995 } else {
2996 if (1 == WindowInfoList_Len())
2997 lastWindow = true;
2998
2999 if (lastWindow)
3000 Prefs_Save();
3001 else
3002 UpdateCurrentFileDisplayStateForWin(win);
3003 }
3004
3005 win->state = WS_ABOUT;
3006
3007 if (lastWindow && !quitIfLast) {
3008 /* last window - don't delete it */
3009 delete win->dm;
3010 win->dm = NULL;
3011 WindowInfo_RedrawAll(win);
3012 } else {
3013 HWND hwndToDestroy = win->hwndFrame;
3014 WindowInfoList_Remove(win);
3015 WindowInfo_Delete(win);
3016 DragAcceptFiles(hwndToDestroy, FALSE);
3017 DestroyWindow(hwndToDestroy);
3018 }
3019
3020 if (lastWindow && quitIfLast) {
3021 assert(0 == WindowInfoList_Len());
3022 PostQuitMessage(0);
3023 } else {
3024 if (!gRunningDLL)
3025 MenuToolbarUpdateStateForAllWindows();
3026 }
3027 }
3028
3029 /* Zoom document in window 'hwnd' to zoom level 'zoom'.
3030 'zoom' is given as a floating-point number, 1.0 is 100%, 2.0 is 200% etc.
3031 */
3032 static void OnMenuZoom(WindowInfo *win, UINT menuId)
3033 {
3034 if (!win->dm)
3035 return;
3036
3037 double zoom = ZoomMenuItemToZoom(menuId);
3038 win->dm->zoomTo(zoom);
3039 ZoomMenuItemCheck(GetMenu(win->hwndFrame), menuId);
3040 }
3041
3042 static bool CheckPrinterStretchDibSupport(HWND hwndForMsgBox, HDC hdc)
3043 {
3044 // most printers can support stretchdibits,
3045 // whereas a lot of printers do not support bitblt
3046 // quit if printer doesn't support StretchDIBits
3047 int rasterCaps = GetDeviceCaps(hdc, RASTERCAPS);
3048 int supportsStretchDib = rasterCaps & RC_STRETCHDIB;
3049 if (supportsStretchDib)
3050 return true;
3051
3052 MessageBox(hwndForMsgBox, TEXT("This printer doesn't support StretchDIBits function"), TEXT("Printing problem."), MB_ICONEXCLAMATION | MB_OK);
3053 return false;
3054 }
3055
3056 // TODO: make it run in a background thread by constructing new PdfEngine()
3057 // from a file name - this should be thread safe
3058 static void PrintToDevice(DisplayModel *dm, HDC hdc, LPDEVMODE devMode, int fromPage, int toPage) {
3059
3060 assert(toPage >= fromPage);
3061 assert(dm);
3062 if (!dm) return;
3063
3064 PdfEngine *pdfEngine = dm->pdfEngine();
3065 DOCINFO di = {0};
3066 di.cbSize = sizeof (DOCINFO);
3067 di.lpszDocName = (LPCTSTR)pdfEngine->fileName();
3068
3069 if (StartDoc(hdc, &di) <= 0)
3070 return;
3071
3072 // rendering for the same DisplayModel is not thread-safe
3073 // TODO: in fitz, propably rendering anything might not be thread-safe
3074 RenderQueue_RemoveForDisplayModel(dm);
3075 cancelRenderingForDisplayModel(dm);
3076
3077 // print all the pages the user requested unless
3078 // bContinue flags there is a problem.
3079 for (int pageNo = fromPage; pageNo <= toPage; pageNo++) {
3080 int rotation = pdfEngine->pageRotation(pageNo);
3081
3082 DBG_OUT(" printing: drawing bitmap for page %d\n", pageNo);
3083
3084 // render at a big zoom, 250% should be good enough. It's a compromise
3085 // between quality and memory usage. TODO: ideally we would use zoom
3086 // that matches the size of the page in the printer
3087 // TODO: consider using a greater zoom level e.g. 750.0
3088 RenderedBitmap *bmp = pdfEngine->renderBitmap(pageNo, 250.0, rotation, NULL, NULL);
3089 if (!bmp)
3090 goto Error; /* most likely ran out of memory */
3091
3092 StartPage(hdc);
3093 // MM_TEXT: Each logical unit is mapped to one device pixel.
3094 // Positive x is to the right; positive y is down.
3095 SetMapMode(hdc, MM_TEXT);
3096
3097 int pageHeight = GetDeviceCaps(hdc, PHYSICALHEIGHT);
3098 int pageWidth = GetDeviceCaps(hdc, PHYSICALWIDTH);
3099
3100 int topMargin = GetDeviceCaps(hdc, PHYSICALOFFSETY);
3101 int leftMargin = GetDeviceCaps(hdc, PHYSICALOFFSETX);
3102 if (DMORIENT_LANDSCAPE == devMode->dmOrientation)
3103 swap_int(&topMargin, &leftMargin);
3104
3105 bmp->stretchDIBits(hdc, -leftMargin, -topMargin, pageWidth, pageHeight);
3106 delete bmp;
3107 if (EndPage(hdc) <= 0) {
3108 AbortDoc(hdc);
3109 return;
3110 }
3111 }
3112
3113 Error:
3114 EndDoc(hdc);
3115 }
3116
3117 /* Show Print Dialog box to allow user to select the printer
3118 and the pages to print.
3119
3120 Creates a new dummy page for each page with a large zoom factor,
3121 and then uses StretchDIBits to copy this to the printer's dc.
3122
3123 So far have tested printing from XP to
3124 - Acrobat Professional 6 (note that acrobat is usually set to
3125 downgrade the resolution of its bitmaps to 150dpi)
3126 - HP Laserjet 2300d
3127 - HP Deskjet D4160
3128 - Lexmark Z515 inkjet, which should cover most bases.
3129 */
3130 static void OnMenuPrint(WindowInfo *win)
3131 {
3132 PRINTDLG pd;
3133
3134 assert(win);
3135 if (!win) return;
3136
3137 DisplayModel *dm = win->dm;
3138 assert(dm);
3139 if (!dm) return;
3140
3141 /* printing uses the WindowInfo win that is created for the
3142 screen, it may be possible to create a new WindowInfo
3143 for printing to so we don't mess with the screen one,
3144 but the user is not inconvenienced too much, and this
3145 way we only need to concern ourselves with one dm.
3146 TODO: don't re-use WindowInfo, use a different, synchronious
3147 way of creating a bitmap */
3148 ZeroMemory(&pd, sizeof(pd));
3149 pd.lStructSize = sizeof(pd);
3150 pd.hwndOwner = win->hwndFrame;
3151 pd.hDevMode = NULL;
3152 pd.hDevNames = NULL;
3153 pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC;
3154 pd.nCopies = 1;
3155 /* by default print all pages */
3156 pd.nFromPage = 1;
3157 pd.nToPage = dm->pageCount();
3158 pd.nMinPage = 1;
3159 pd.nMaxPage = dm->pageCount();
3160
3161 BOOL pressedOk = PrintDlg(&pd);
3162 if (!pressedOk) {
3163 if (CommDlgExtendedError()) {
3164 /* if PrintDlg was cancelled then
3165 CommDlgExtendedError is zero, otherwise it returns the
3166 error code, which we could look at here if we wanted.
3167 for now just warn the user that printing has stopped
3168 becasue of an error */
3169 MessageBox(win->hwndFrame, TEXT("Cannot initialise printer"), TEXT("Printing problem."), MB_ICONEXCLAMATION | MB_OK);
3170 } /* @note: TEXT() casts */
3171 return;
3172 }
3173
3174 if (CheckPrinterStretchDibSupport(win->hwndFrame, pd.hDC))
3175 PrintToDevice(dm, pd.hDC, (LPDEVMODE)pd.hDevMode, pd.nFromPage, pd.nToPage);
3176
3177 DeleteDC(pd.hDC);
3178 if (pd.hDevNames != NULL) GlobalFree(pd.hDevNames);
3179 if (pd.hDevMode != NULL) GlobalFree(pd.hDevMode);
3180 }
3181
3182 static void OnMenuSaveAs(WindowInfo *win)
3183 {
3184 OPENFILENAME ofn = {0};
3185 char dstFileName[MAX_PATH] = {0};
3186 const char* srcFileName = NULL;
3187
3188 assert(win);
3189 if (!win) return;
3190 assert(win->dm);
3191 if (!win->dm) return;
3192
3193 srcFileName = win->dm->fileName();
3194 assert(srcFileName);
3195 if (!srcFileName) return;
3196
3197 ofn.lStructSize = sizeof(ofn);
3198 ofn.hwndOwner = win->hwndFrame;
3199 ofn.lpstrFile = (TCHAR*)dstFileName;
3200
3201 // Set lpstrFile[0] to '\0' so that GetOpenFileName does not
3202 // use the contents of szFile to initialize itself.
3203 ofn.lpstrFile[0] = '\0';
3204 ofn.nMaxFile = dimof(dstFileName);
3205 ofn.lpstrFilter = TEXT("PDF\0*.pdf\0All\0*.*\0"); /* @note: TEXT() casts */
3206 ofn.nFilterIndex = 1;
3207 ofn.lpstrFileTitle = NULL;
3208 ofn.nMaxFileTitle = 0;
3209 ofn.lpstrInitialDir = NULL;
3210 ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
3211
3212 if (FALSE == GetSaveFileName(&ofn))
3213 return;
3214
3215 char* realDstFileName = dstFileName;
3216 if (!str_endswithi(dstFileName, ".pdf")) {
3217 realDstFileName = str_cat(dstFileName, ".pdf");
3218 }
3219 BOOL cancelled = FALSE;
3220 BOOL ok = CopyFileEx((TCHAR*)srcFileName, (TCHAR*)realDstFileName, NULL, NULL, &cancelled, COPY_FILE_FAIL_IF_EXISTS); /* @note: TCHAR* cast */
3221 if (!ok) {
3222 SeeLastError();
3223 /* @note: translation needs some care */
3224 //MessageBox(win->hwndFrame, _TR("Failed to save a file"), TEXT("Information"), MB_OK);
3225 MessageBox(win->hwndFrame, TEXT("Failed to save a file"), TEXT("Information"), MB_OK);
3226 }
3227 if (realDstFileName != dstFileName)
3228 free(realDstFileName);
3229 }
3230
3231 static void OnMenuOpen(WindowInfo *win)
3232 {
3233 OPENFILENAME ofn = {0};
3234 char fileName[PATH_MAX];
3235
3236 ofn.lStructSize = sizeof(ofn);
3237 ofn.hwndOwner = win->hwndFrame;
3238 ofn.lpstrFile = (TCHAR*)fileName; /* @note: TCHAR* cast */
3239
3240 // Set lpstrFile[0] to '\0' so that GetOpenFileName does not
3241 // use the contents of szFile to initialize itself.
3242 ofn.lpstrFile[0] = '\0';
3243 ofn.nMaxFile = sizeof(fileName);
3244 ofn.lpstrFilter = TEXT("PDF\0*.pdf\0All\0*.*\0");
3245 ofn.nFilterIndex = 1;
3246 ofn.lpstrFileTitle = NULL;
3247 ofn.nMaxFileTitle = 0;
3248 ofn.lpstrInitialDir = NULL;
3249 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
3250
3251 if (FALSE == GetOpenFileName(&ofn))
3252 return;
3253
3254 win = LoadPdf(fileName);
3255 if (!win)
3256 return;
3257 }
3258
3259 static void RotateLeft(WindowInfo *win)
3260 {
3261 assert(win);
3262 if (!win) return;
3263 if (!WindowInfo_PdfLoaded(win))
3264 return;
3265 win->dm->rotateBy(-90);
3266 }
3267
3268 static void RotateRight(WindowInfo *win)
3269 {
3270 assert(win);
3271 if (!win) return;
3272 if (!WindowInfo_PdfLoaded(win))
3273 return;
3274 win->dm->rotateBy(90);
3275 }
3276
3277 static void OnVScroll(WindowInfo *win, WPARAM wParam)
3278 {
3279 if (win->documentBlocked) return;
3280 SCROLLINFO si = {0};
3281 int iVertPos;
3282
3283 si.cbSize = sizeof (si);
3284 si.fMask = SIF_ALL;
3285 GetScrollInfo(win->hwndCanvas, SB_VERT, &si);
3286
3287 iVertPos = si.nPos;
3288
3289 switch (LOWORD(wParam))
3290 {
3291 case SB_TOP:
3292 si.nPos = si.nMin;
3293 break;
3294
3295 case SB_BOTTOM:
3296 si.nPos = si.nMax;
3297 break;
3298
3299 case SB_LINEUP:
3300 si.nPos -= 16;
3301 break;
3302
3303 case SB_LINEDOWN:
3304 si.nPos += 16;
3305 break;
3306
3307 case SB_PAGEUP:
3308 si.nPos -= si.nPage;
3309 break;
3310
3311 case SB_PAGEDOWN:
3312 si.nPos += si.nPage;
3313 break;
3314
3315 case SB_THUMBTRACK:
3316 si.nPos = si.nTrackPos;
3317 break;
3318
3319 default:
3320 break;
3321 }
3322
3323 // Set the position and then retrieve it. Due to adjustments
3324 // by Windows it may not be the same as the value set.
3325 si.fMask = SIF_POS;
3326 SetScrollInfo(win->hwndCanvas, SB_VERT, &si, TRUE);
3327 GetScrollInfo(win->hwndCanvas, SB_VERT, &si);
3328
3329 // If the position has changed, scroll the window and update it
3330 if (win->dm && (si.nPos != iVertPos))
3331 win->dm->scrollYTo(si.nPos);
3332 }
3333
3334 static void OnHScroll(WindowInfo *win, WPARAM wParam)
3335 {
3336 if (win->documentBlocked) return;
3337 SCROLLINFO si = {0};
3338 int iVertPos;
3339
3340 si.cbSize = sizeof (si);
3341 si.fMask = SIF_ALL;
3342 GetScrollInfo(win->hwndCanvas, SB_HORZ, &si);
3343
3344 iVertPos = si.nPos;
3345
3346 switch (LOWORD(wParam))
3347 {
3348 case SB_TOP:
3349 si.nPos = si.nMin;
3350 break;
3351
3352 case SB_BOTTOM:
3353 si.nPos = si.nMax;
3354 break;
3355
3356 case SB_LINEUP:
3357 si.nPos -= 16;
3358 break;
3359
3360 case SB_LINEDOWN:
3361 si.nPos += 16;
3362 break;
3363
3364 case SB_PAGEUP:
3365 si.nPos -= si.nPage;
3366 break;
3367
3368 case SB_PAGEDOWN:
3369 si.nPos += si.nPage;
3370 break;
3371
3372 case SB_THUMBTRACK:
3373 si.nPos = si.nTrackPos;
3374 break;
3375
3376 default:
3377 break;
3378 }
3379
3380 // Set the position and then retrieve it. Due to adjustments
3381 // by Windows it may not be the same as the value set.
3382 si.fMask = SIF_POS;
3383 SetScrollInfo(win->hwndCanvas, SB_HORZ, &si, TRUE);
3384 GetScrollInfo(win->hwndCanvas, SB_HORZ, &si);
3385
3386 // If the position has changed, scroll the window and update it
3387 if (win->dm && (si.nPos != iVertPos))
3388 win->dm->scrollXTo(si.nPos);
3389 }
3390
3391 static void ViewWithAcrobat(WindowInfo *win)
3392 {
3393 // TODO: write me
3394 }
3395
3396 static void OnMenuViewSinglePage(WindowInfo *win)
3397 {
3398 assert(win);
3399 if (!win) return;
3400 if (!WindowInfo_PdfLoaded(win))
3401 return;
3402 SwitchToDisplayMode(win, DM_SINGLE_PAGE);
3403 }
3404
3405 static void OnMenuViewFacing(WindowInfo *win)
3406 {
3407 assert(win);
3408 if (!win) return;
3409 if (!WindowInfo_PdfLoaded(win))
3410 return;
3411 SwitchToDisplayMode(win, DM_FACING);
3412 }
3413
3414 static void OneMenuMakeDefaultReader(void)
3415 {
3416 AssociateExeWithPdfExtensions();
3417 /* @note: translation need some care */
3418 //MessageBox(NULL, _TR("SumatraPDF is now a default reader for PDF files."), "Information", MB_OK);
3419 MessageBox(NULL, TEXT("SumatraPDF is now a default reader for PDF files."), TEXT("Information"), MB_OK);
3420 }
3421
3422 static void OnSize(WindowInfo *win, int dx, int dy)
3423 {
3424 int rebBarDy = 0;
3425 if (gShowToolbar) {
3426 SetWindowPos(win->hwndReBar, NULL, 0, 0, dx, rebBarDy, SWP_NOZORDER);
3427 rebBarDy = gReBarDy + gReBarDyFrame;
3428 }
3429 SetWindowPos(win->hwndCanvas, NULL, 0, rebBarDy, dx, dy-rebBarDy, SWP_NOZORDER);
3430 }
3431
3432 static void ReloadPdfDocument(WindowInfo *win)
3433 {
3434 if (WS_SHOWING_PDF != win->state)
3435 return;
3436 const char *fileName = NULL;
3437 if (win->dm)
3438 fileName = (const char*)str_dup(win->dm->fileName());
3439 CloseWindow(win, false);
3440 if (fileName) {
3441 LoadPdf(fileName);
3442 free((void*)fileName);
3443 }
3444 }
3445
3446 static void RebuildProgramMenus(void)
3447 {
3448 HMENU m = ForceRebuildMenu();
3449 WindowInfo *win = gWindowList;
3450 while (win) {
3451 SetMenu(win->hwndFrame, m);
3452 MenuUpdateStateForWindow(win);
3453 win = win->next;
3454 }
3455 }
3456
3457 static void LanguageChanged(const char *langName)
3458 {
3459 assert(!str_eq(langName, CurrLangNameGet()));
3460
3461 CurrLangNameSet(langName);
3462
3463 RebuildProgramMenus();
3464 // TODO: recreate tooltips
3465 }
3466
3467 static void OnMenuLanguage(int langId)
3468 {
3469 const char *langName = NULL;
3470 for (int i=0; i < LANGS_COUNT; i++) {
3471 if (g_langs[i]._langId == langId) {
3472 langName = g_langs[i]._langName;
3473 break;
3474 }
3475 }
3476
3477 assert(langName);
3478 if (!langName) return;
3479 if (str_eq(langName, CurrLangNameGet()))
3480 return;
3481 LanguageChanged(langName);
3482 }
3483
3484 static void OnMenuViewUseFitz(WindowInfo *win)
3485 {
3486 assert(win);
3487 DBG_OUT("OnMenuViewUseFitz()\n");
3488 if (gUseFitz)
3489 gUseFitz = FALSE;
3490 else
3491 gUseFitz = TRUE;
3492
3493 ReloadPdfDocument(win);
3494 win = gWindowList;
3495 while (win) {
3496 MenuUpdateUseFitzStateForWindow(win);
3497 win = win->next;
3498 }
3499 }
3500
3501 static void OnMenuViewShowHideToolbar()
3502 {
3503 if (gShowToolbar)
3504 gShowToolbar = FALSE;
3505 else
3506 gShowToolbar = TRUE;
3507
3508 WindowInfo* win = gWindowList;
3509 while (win) {
3510 if (gShowToolbar)
3511 ShowWindow(win->hwndReBar, SW_SHOW);
3512 else
3513 ShowWindow(win->hwndReBar, SW_HIDE);
3514 int dx, dy, x, y;
3515 Win32_Win_GetPos(win->hwndFrame, &x, &y);
3516 Win32_Win_GetSize(win->hwndFrame, &dx, &dy);
3517 // TODO: a hack. I add 1 to dy to cause sending WM_SIZE msg to hwndFrame
3518 // but I shouldn't really change the size. But I don't know how to
3519 // cause sending WM_SIZE otherwise. I tried calling OnSize() directly,
3520 // but it left scrollbar partially hidden
3521 MoveWindow(win->hwndFrame, x, y, dx, dy+1, TRUE);
3522 MenuUpdateShowToolbarStateForWindow(win);
3523 win = win->next;
3524 }
3525 }
3526
3527 static void OnMenuViewContinuous(WindowInfo *win)
3528 {
3529 assert(win);
3530 if (!win) return;
3531 if (!WindowInfo_PdfLoaded(win))
3532 return;
3533 SwitchToDisplayMode(win, DM_CONTINUOUS);
3534 }
3535
3536 static void OnMenuViewContinuousFacing(WindowInfo *win)
3537 {
3538 assert(win);
3539 if (!win) return;
3540 if (!WindowInfo_PdfLoaded(win))
3541 return;
3542 SwitchToDisplayMode(win, DM_CONTINUOUS_FACING);
3543 }
3544
3545 static void OnMenuGoToNextPage(WindowInfo *win)
3546 {
3547 assert(win);
3548 if (!win) return;
3549 if (!WindowInfo_PdfLoaded(win))
3550 return;
3551 win->dm->goToNextPage(0);
3552 }
3553
3554 static void OnMenuGoToPrevPage(WindowInfo *win)
3555 {
3556 assert(win);
3557 if (!win) return;
3558 if (!WindowInfo_PdfLoaded(win))
3559 return;
3560 win->dm->goToPrevPage(0);
3561 }
3562
3563 static void OnMenuGoToLastPage(WindowInfo *win)
3564 {
3565 assert(win);
3566 if (!win) return;
3567 if (!WindowInfo_PdfLoaded(win))
3568 return;
3569 win->dm->goToLastPage();
3570 }
3571
3572 static void OnMenuGoToFirstPage(WindowInfo *win)
3573 {
3574 assert(win);
3575 if (!win) return;
3576 if (!WindowInfo_PdfLoaded(win))
3577 return;
3578 win->dm->goToFirstPage();
3579 }
3580
3581 static void OnMenuGoToPage(WindowInfo *win)
3582 {
3583 assert(win);
3584 if (!win) return;
3585 if (!WindowInfo_PdfLoaded(win))
3586 return;
3587
3588 int newPageNo = Dialog_GoToPage(win);
3589 if (win->dm->validPageNo(newPageNo))
3590 win->dm->goToPage(newPageNo, 0);
3591 }
3592
3593 static void OnMenuViewRotateLeft(WindowInfo *win)
3594 {
3595 RotateLeft(win);
3596 }
3597
3598 static void OnMenuViewRotateRight(WindowInfo *win)
3599 {
3600 RotateRight(win);
3601 }
3602
3603 #define KEY_PRESSED_MASK 0x8000
3604 static bool WasKeyDown(int virtKey)
3605 {
3606 SHORT state = GetKeyState(virtKey);
3607 if (KEY_PRESSED_MASK & state)
3608 return true;
3609 return false;
3610 }
3611
3612 static bool WasShiftPressed()
3613 {
3614 return WasKeyDown(VK_LSHIFT) || WasKeyDown(VK_RSHIFT);
3615 }
3616
3617 static bool WasCtrlPressed()
3618 {
3619 return WasKeyDown(VK_LCONTROL) || WasKeyDown(VK_RCONTROL);
3620 }
3621
3622 static void OnKeydown(WindowInfo *win, int key, LPARAM lparam)
3623 {
3624 if (!win->dm)
3625 return;
3626 if (win->documentBlocked)
3627 return;
3628
3629 bool shiftPressed = WasShiftPressed();
3630 bool ctrlPressed = WasCtrlPressed();
3631 //DBG_OUT("key=%d,%c,shift=%d,ctrl=%d\n", key, (char)key, (int)shiftPressed, (int)ctrlPressed);
3632
3633 if (VK_PRIOR == key) {
3634 /* TODO: more intelligence (see VK_NEXT comment). Also, probably
3635 it's exactly the same as 'n' so the code should be factored out */
3636 win->dm->goToPrevPage(0);
3637 /* SendMessage (win->hwnd, WM_VSCROLL, SB_PAGEUP, 0); */
3638 } else if (VK_NEXT == key) {
3639 /* TODO: this probably should be more intelligent (scroll if not yet at the bottom,
3640 go to next page if at the bottom, and something entirely different in continuous mode */
3641 win->dm->goToNextPage(0);
3642 /* SendMessage (win->hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); */
3643 } else if (VK_UP == key) {
3644 SendMessage (win->hwndCanvas, WM_VSCROLL, SB_LINEUP, 0);
3645 } else if (VK_DOWN == key) {
3646 SendMessage (win->hwndCanvas, WM_VSCROLL, SB_LINEDOWN, 0);
3647 } else if (VK_LEFT == key) {
3648 SendMessage (win->hwndCanvas, WM_HSCROLL, SB_PAGEUP, 0);
3649 } else if (VK_RIGHT == key) {
3650 SendMessage (win->hwndCanvas, WM_HSCROLL, SB_PAGEDOWN, 0);
3651 } else if (VK_HOME == key) {
3652 win->dm->goToFirstPage();
3653 } else if (VK_END == key) {
3654 win->dm->goToLastPage();
3655 #if 0 // we do it via accelerators
3656 } else if ('G' == key) {
3657 if (ctrlPressed)
3658 OnMenuGoToPage(win);
3659 #endif
3660 } else if (VK_OEM_PLUS == key) {
3661 // Emulate acrobat: "Shift Ctrl +" is rotate clockwise
3662 if (shiftPressed & ctrlPressed)
3663 RotateRight(win);
3664 } else if (VK_OEM_MINUS == key) {
3665 // Emulate acrobat: "Shift Ctrl -" is rotate counter-clockwise
3666 if (shiftPressed & ctrlPressed)
3667 RotateLeft(win);
3668 }
3669 }
3670
3671 static void OnChar(WindowInfo *win, int key)
3672 {
3673 if (!win->dm)
3674 return;
3675 if (win->documentBlocked)
3676 return;
3677
3678 // DBG_OUT("char=%d,%c\n", key, (char)key);
3679
3680 if (VK_SPACE == key) {
3681 win->dm->scrollYByAreaDy(true, true);
3682 } else if (VK_BACK == key) {
3683 win->dm->scrollYByAreaDy(false, true);
3684 } else if ('g' == key) {
3685 OnMenuGoToPage(win);
3686 } else if ('k' == key) {
3687 SendMessage(win->hwndCanvas, WM_VSCROLL, SB_LINEDOWN, 0);
3688 } else if ('j' == key) {
3689 SendMessage(win->hwndCanvas, WM_VSCROLL, SB_LINEUP, 0);
3690 } else if ('n' == key) {
3691 win->dm->goToNextPage(0);
3692 } else if ('c' == key) {
3693 // TODO: probably should preserve facing vs. non-facing
3694 win->dm->changeDisplayMode(DM_CONTINUOUS);
3695 } else if ('p' == key) {
3696 win->dm->goToPrevPage(0);
3697 } else if ('z' == key) {
3698 WindowInfo_ToggleZoom(win);
3699 } else if ('q' == key) {
3700 DestroyWindow(win->hwndFrame);
3701 } else if ('+' == key) {
3702 win->dm->zoomBy(ZOOM_IN_FACTOR);
3703 } else if ('-' == key) {
3704 win->dm->zoomBy(ZOOM_OUT_FACTOR);
3705 } else if ('r' == key) {
3706 ReloadPdfDocument(win);
3707 }
3708 }
3709
3710 static inline bool IsEnumPrintersArg(const char *txt)
3711 {
3712 if (str_ieq(txt, ENUM_PRINTERS_ARG_TXT))
3713 return true;
3714 return false;
3715 }
3716
3717 static inline bool IsDontRegisterExtArg(const char *txt)
3718 {
3719 if (str_ieq(txt, NO_REGISTER_EXT_ARG_TXT))
3720 return true;
3721 return false;
3722 }
3723
3724 static inline bool IsPrintToArg(const char *txt)
3725 {
3726 if (str_ieq(txt, PRINT_TO_ARG_TXT))
3727 return true;
3728 return false;
3729 }
3730
3731 static inline bool IsPrintToDefaultArg(const char *txt)
3732 {
3733 if (str_ieq(txt, PRINT_TO_ARG_TXT))
3734 return true;
3735 return false;
3736 }
3737
3738 static inline bool IsExitOnPrintArg(const char *txt)
3739 {
3740 if (str_ieq(txt, EXIT_ON_PRINT_ARG_TXT))
3741 return true;
3742 return false;
3743 }
3744
3745 static inline bool IsBenchArg(const char *txt)
3746 {
3747 if (str_ieq(txt, BENCH_ARG_TXT))
3748 return true;
3749 return false;
3750 }
3751
3752 static bool IsBenchMode(void)
3753 {
3754 if (NULL != gBenchFileName)
3755 return true;
3756 return false;
3757 }
3758
3759 /* Find a file in a file history list that has a given 'menuId'.
3760 Return a copy of filename or NULL if couldn't be found.
3761 It's used to figure out if a menu item selected by the user
3762 is one of the "recent files" menu items in File menu.
3763 Caller needs to free() the memory.
3764 */
3765 static const char *RecentFileNameFromMenuItemId(UINT menuId) {
3766 FileHistoryList* curr = gFileHistoryRoot;
3767 while (curr) {
3768 if (curr->menuId == menuId)
3769 return str_dup(curr->state.filePath);
3770 curr = curr->next;
3771 }
3772 return NULL;
3773 }
3774
3775 #define FRAMES_PER_SECS 60
3776 #define ANIM_FREQ_IN_MS 1000 / FRAMES_PER_SECS
3777
3778 static void OnMenuAbout() {
3779 if (gHwndAbout) {
3780 SetActiveWindow(gHwndAbout);
3781 return;
3782 }
3783 gHwndAbout = CreateWindow(
3784 ABOUT_CLASS_NAME, (TCHAR*)ABOUT_WIN_TITLE,
3785 WS_OVERLAPPEDWINDOW,
3786 CW_USEDEFAULT, CW_USEDEFAULT,
3787 ABOUT_WIN_DX, ABOUT_WIN_DY,
3788 NULL, NULL,
3789 ghinst, NULL); /* @note: TCHAR* cast */
3790 if (!gHwndAbout)
3791 return;
3792 ShowWindow(gHwndAbout, SW_SHOW);
3793 }
3794
3795 BOOL PrivateIsAppThemed() {
3796 BOOL isThemed = FALSE;
3797 HMODULE hDll = LoadLibrary(TEXT("uxtheme.dll")); /* @note: TEXT() cast */
3798 if (!hDll) return FALSE;
3799
3800 FARPROC fp = GetProcAddress(hDll, "IsAppThemed");
3801 if (fp)
3802 isThemed = fp();
3803
3804 FreeLibrary(hDll);
3805 return isThemed;
3806 }
3807
3808 static TBBUTTON TbButtonFromButtonInfo(int i) {
3809 TBBUTTON tbButton = {0};
3810 if (IDB_SEPARATOR == gToolbarButtons[i].cmdId) {
3811 tbButton.fsStyle = TBSTYLE_SEP;
3812 } else {
3813 tbButton.iBitmap = gToolbarButtons[i].index;
3814 tbButton.idCommand = gToolbarButtons[i].cmdId;
3815 tbButton.fsState = TBSTATE_ENABLED;
3816 tbButton.fsStyle = TBSTYLE_BUTTON;
3817 tbButton.iString = (INT_PTR)gToolbarButtons[i].toolTip;
3818 }
3819 return tbButton;
3820 }
3821
3822 #define WS_TOOLBAR (WS_CHILD | WS_CLIPSIBLINGS | \
3823 TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | \
3824 TBSTYLE_LIST | CCS_NODIVIDER | CCS_NOPARENTALIGN )
3825
3826 static void CreateToolbar(WindowInfo *win, HINSTANCE hInst) {
3827 BOOL bIsAppThemed = PrivateIsAppThemed();
3828
3829 HWND hwndOwner = win->hwndFrame;
3830 HWND hwndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_TOOLBAR,
3831 0,0,0,0, hwndOwner,(HMENU)IDC_TOOLBAR, hInst,NULL);
3832 win->hwndToolbar = hwndToolbar;
3833 LRESULT lres = SendMessage(hwndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
3834
3835 ShowWindow(hwndToolbar, SW_SHOW);
3836 HIMAGELIST himl = 0;
3837 TBBUTTON tbButtons[TOOLBAR_BUTTONS_COUNT];
3838 for (int i=0; i < TOOLBAR_BUTTONS_COUNT; i++) {
3839 if (IDB_SEPARATOR != gToolbarButtons[i].bitmapResourceId) {
3840 HBITMAP hbmp = LoadBitmap(hInst, MAKEINTRESOURCE(gToolbarButtons[i].bitmapResourceId));
3841 if (!himl) {
3842 BITMAP bmp;
3843 GetObject(hbmp, sizeof(BITMAP), &bmp);
3844 int dx = bmp.bmWidth;
3845 int dy = bmp.bmHeight;
3846 himl = ImageList_Create(dx, dy, ILC_COLORDDB | ILC_MASK, 0, 0);
3847 }
3848 int index = ImageList_AddMasked(himl, hbmp, RGB(255,0,255));
3849 DeleteObject(hbmp);
3850 gToolbarButtons[i].index = index;
3851 }
3852 tbButtons[i] = TbButtonFromButtonInfo(i);
3853 }
3854 lres = SendMessage(hwndToolbar, TB_SETIMAGELIST, 0, (LPARAM)himl);
3855
3856 // TODO: construct disabled image list as well?
3857 //SendMessage(hwndToolbar, TB_SETDISABLEDIMAGELIST, 0, (LPARAM)himl);
3858
3859 LRESULT exstyle = SendMessage(hwndToolbar, TB_GETEXTENDEDSTYLE, 0, 0);
3860 exstyle |= TBSTYLE_EX_MIXEDBUTTONS;
3861 lres = SendMessage(hwndToolbar, TB_SETEXTENDEDSTYLE, 0, exstyle);
3862
3863 lres = SendMessage(hwndToolbar, TB_ADDBUTTONS, TOOLBAR_BUTTONS_COUNT, (LPARAM)tbButtons);
3864 RECT rc;
3865 lres = SendMessage(hwndToolbar, TB_GETITEMRECT, 0, (LPARAM)&rc);
3866
3867 DWORD reBarStyle = WS_REBAR | WS_VISIBLE;
3868 win->hwndReBar = CreateWindowEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, NULL, reBarStyle,
3869 0,0,0,0, hwndOwner, (HMENU)IDC_REBAR, hInst, NULL);
3870 if (!win->hwndReBar)
3871 SeeLastError();
3872
3873 REBARINFO rbi;
3874 rbi.cbSize = sizeof(REBARINFO);
3875 rbi.fMask = 0;
3876 rbi.himl = (HIMAGELIST)NULL;
3877 lres = SendMessage(win->hwndReBar, RB_SETBARINFO, 0, (LPARAM)&rbi);
3878
3879 REBARBANDINFO rbBand;
3880 rbBand.cbSize = sizeof(REBARBANDINFO);
3881 rbBand.fMask = /*RBBIM_COLORS | RBBIM_TEXT | RBBIM_BACKGROUND | */
3882 RBBIM_STYLE | RBBIM_CHILD | RBBIM_CHILDSIZE /*| RBBIM_SIZE*/;
3883 rbBand.fStyle = /*RBBS_CHILDEDGE |*//* RBBS_BREAK |*/ RBBS_FIXEDSIZE /*| RBBS_GRIPPERALWAYS*/;
3884 if (bIsAppThemed)
3885 rbBand.fStyle |= RBBS_CHILDEDGE;
3886 rbBand.hbmBack = NULL;
3887 rbBand.lpText = TEXT("Toolbar");
3888 rbBand.hwndChild = hwndToolbar;
3889 rbBand.cxMinChild = (rc.right - rc.left) * TOOLBAR_BUTTONS_COUNT;
3890 rbBand.cyMinChild = (rc.bottom - rc.top) + 2 * rc.top;
3891 rbBand.cx = 0;
3892 lres = SendMessage(win->hwndReBar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
3893
3894 SetWindowPos(win->hwndReBar, NULL, 0, 0, 0, 0, SWP_NOZORDER);
3895 GetWindowRect(win->hwndReBar, &rc);
3896 gReBarDy = rc.bottom - rc.top;
3897 //TODO: this was inherited but doesn't seem to be right (makes toolbar
3898 // partially unpainted if using classic scheme on xp or vista
3899 //gReBarDyFrame = bIsAppThemed ? 0 : 2;
3900 gReBarDyFrame = 0;
3901 }
3902
3903 static LRESULT CALLBACK WndProcAbout(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3904 {
3905 switch (message)
3906 {
3907 case WM_CREATE:
3908 assert(!gHwndAbout);
3909 break;
3910
3911 case WM_ERASEBKGND:
3912 // do nothing, helps to avoid flicker
3913 return TRUE;
3914
3915 case WM_PAINT:
3916 OnPaintAbout(hwnd);
3917 break;
3918
3919 case WM_DESTROY:
3920 assert(gHwndAbout);
3921 gHwndAbout = NULL;
3922 break;
3923
3924 default:
3925 return DefWindowProc(hwnd, message, wParam, lParam);
3926 }
3927 return 0;
3928 }
3929
3930 /* TODO: gAccumDelta must be per WindowInfo */
3931 static int gDeltaPerLine, gAccumDelta; // for mouse wheel logic
3932
3933 static LRESULT CALLBACK WndProcCanvas(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3934 {
3935 WindowInfo * win;
3936 win = WindowInfo_FindByHwnd(hwnd);
3937 switch (message)
3938 {
3939 case WM_APP_REPAINT_DELAYED:
3940 if (win)
3941 SetTimer(win->hwndCanvas, REPAINT_TIMER_ID, REPAINT_DELAY_IN_MS, NULL);
3942 break;
3943
3944 case WM_APP_REPAINT_NOW:
3945 if (win)
3946 WindowInfo_RedrawAll(win);
3947 break;
3948
3949 case WM_VSCROLL:
3950 OnVScroll(win, wParam);
3951 return WM_VSCROLL_HANDLED;
3952
3953 case WM_HSCROLL:
3954 OnHScroll(win, wParam);
3955 return WM_HSCROLL_HANDLED;
3956
3957 case WM_MOUSEMOVE:
3958 if (win)
3959 OnMouseMove(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), wParam);
3960 break;
3961
3962 case WM_LBUTTONDOWN:
3963 if (win)
3964 OnMouseLeftButtonDown(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
3965 break;
3966
3967 case WM_LBUTTONUP:
3968 if (win)
3969 OnMouseLeftButtonUp(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
3970 break;
3971
3972 case WM_RBUTTONDOWN:
3973 if (win)
3974 OnMouseRightButtonDown(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
3975 break;
3976
3977 case WM_RBUTTONUP:
3978 if (win)
3979 OnMouseRightButtonUp(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
3980 break;
3981
3982 case WM_SETCURSOR:
3983 if (win && win->mouseAction == MA_DRAGGING) {
3984 SetCursor(gCursorDrag);
3985 return TRUE;
3986 }
3987 break;
3988
3989 case WM_TIMER:
3990 assert(win);
3991 if (win) {
3992 if (REPAINT_TIMER_ID == wParam)
3993 WindowInfo_RedrawAll(win);
3994 else
3995 AnimState_NextFrame(&win->animState);
3996 }
3997 break;
3998
3999 case WM_DROPFILES:
4000 if (win)
4001 OnDropFiles(win, (HDROP)wParam);
4002 break;
4003
4004 case WM_ERASEBKGND:
4005 // do nothing, helps to avoid flicker
4006 return TRUE;
4007
4008 case WM_PAINT:
4009 /* it might happen that we get WM_PAINT after destroying a window */
4010 if (win) {
4011 /* blindly kill the timer, just in case it's there */
4012 KillTimer(win->hwndCanvas, REPAINT_TIMER_ID);
4013 OnPaint(win);
4014 }
4015 break;
4016
4017 default:
4018 return DefWindowProc(hwnd, message, wParam, lParam);
4019 }
4020 return 0;
4021 }
4022
4023 static LRESULT CALLBACK WndProcFrame(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4024 {
4025 int wmId, wmEvent;
4026 WindowInfo * win;
4027 ULONG ulScrollLines; // for mouse wheel logic
4028 const char * fileName;
4029
4030 win = WindowInfo_FindByHwnd(hwnd);
4031
4032 switch (message)
4033 {
4034 case WM_CREATE:
4035 // do nothing
4036 goto InitMouseWheelInfo;
4037
4038 case WM_SIZE:
4039 if (win) {
4040 int dx = LOWORD(lParam);
4041 int dy = HIWORD(lParam);
4042 OnSize(win, dx, dy);
4043 }
4044 break;
4045
4046 case WM_COMMAND:
4047 wmId = LOWORD(wParam);
4048 wmEvent = HIWORD(wParam);
4049
4050 fileName = RecentFileNameFromMenuItemId(wmId);
4051 if (fileName) {
4052 LoadPdf(fileName);
4053 free((void*)fileName);
4054 break;
4055 }
4056
4057 switch (wmId)
4058 {
4059 case IDM_OPEN:
4060 case IDT_FILE_OPEN:
4061 OnMenuOpen(win);
4062 break;
4063 case IDM_SAVEAS:
4064 OnMenuSaveAs(win);
4065 break;
4066
4067 case IDT_FILE_PRINT:
4068 case IDM_PRINT:
4069 OnMenuPrint(win);
4070 break;
4071
4072 case IDM_MAKE_DEFAULT_READER:
4073 OneMenuMakeDefaultReader();
4074 break;
4075
4076 case IDT_FILE_EXIT:
4077 case IDM_CLOSE:
4078 CloseWindow(win, FALSE);
4079 break;
4080
4081 case IDM_EXIT:
4082 OnMenuExit();
4083 break;
4084
4085 case IDT_VIEW_ZOOMIN:
4086 if (win->dm)
4087 win->dm->zoomBy(ZOOM_IN_FACTOR);
4088 break;
4089
4090 case IDT_VIEW_ZOOMOUT:
4091 if (win->dm)
4092 win->dm->zoomBy(ZOOM_OUT_FACTOR);
4093 break;
4094
4095 case IDM_ZOOM_6400:
4096 case IDM_ZOOM_3200:
4097 case IDM_ZOOM_1600:
4098 case IDM_ZOOM_800:
4099 case IDM_ZOOM_400:
4100 case IDM_ZOOM_200:
4101 case IDM_ZOOM_150:
4102 case IDM_ZOOM_125:
4103 case IDM_ZOOM_100:
4104 case IDM_ZOOM_50:
4105 case IDM_ZOOM_25:
4106 case IDM_ZOOM_12_5:
4107 case IDM_ZOOM_8_33:
4108 case IDM_ZOOM_FIT_PAGE:
4109 case IDM_ZOOM_FIT_WIDTH:
4110 case IDM_ZOOM_ACTUAL_SIZE:
4111 OnMenuZoom(win, (UINT)wmId);
4112 break;
4113
4114 case IDM_ZOOM_FIT_VISIBLE:
4115 /* TODO: implement me */
4116 break;
4117
4118 case IDM_VIEW_SINGLE_PAGE:
4119 OnMenuViewSinglePage(win);
4120 break;
4121
4122 case IDM_VIEW_FACING:
4123 OnMenuViewFacing(win);
4124 break;
4125
4126 case IDM_VIEW_CONTINUOUS:
4127 OnMenuViewContinuous(win);
4128 break;
4129
4130 case IDM_VIEW_SHOW_HIDE_TOOLBAR:
4131 OnMenuViewShowHideToolbar();
4132 break;
4133
4134 case IDM_VIEW_USE_FITZ:
4135 OnMenuViewUseFitz(win);
4136 break;
4137
4138 case IDM_GOTO_NEXT_PAGE:
4139 OnMenuGoToNextPage(win);
4140 break;
4141
4142 case IDM_GOTO_PREV_PAGE:
4143 OnMenuGoToPrevPage(win);
4144 break;
4145
4146 case IDM_GOTO_FIRST_PAGE:
4147 OnMenuGoToFirstPage(win);
4148 break;
4149
4150 case IDM_GOTO_LAST_PAGE:
4151 OnMenuGoToLastPage(win);
4152 break;
4153
4154 case IDM_GOTO_PAGE:
4155 OnMenuGoToPage(win);
4156 break;
4157
4158 case IDM_VIEW_CONTINUOUS_FACING:
4159 OnMenuViewContinuousFacing(win);
4160 break;
4161
4162 case IDM_VIEW_ROTATE_LEFT:
4163 OnMenuViewRotateLeft(win);
4164 break;
4165
4166 case IDM_VIEW_ROTATE_RIGHT:
4167 OnMenuViewRotateRight(win);
4168 break;
4169
4170 case IDM_VISIT_WEBSITE:
4171 //LaunchBrowser(_T("http://blog.kowalczyk.info/software/sumatrapdf/"));
4172 MessageBox(NULL, TEXT("http://www.reactos.org/"), TEXT("Extern link blocked"), 0); /* @note: work-around to solve linking issue */
4173 break;
4174
4175 case IDM_LANG_EN:
4176 case IDM_LANG_PL:
4177 case IDM_LANG_FR:
4178 case IDM_LANG_DE:
4179 OnMenuLanguage((int)wmId);
4180 break;
4181
4182 case IDM_ABOUT:
4183 OnMenuAbout();
4184 break;
4185 default:
4186 return DefWindowProc(hwnd, message, wParam, lParam);
4187 }
4188 break;
4189
4190 case WM_CHAR:
4191 if (win)
4192 OnChar(win, wParam);
4193 break;
4194
4195 case WM_KEYDOWN:
4196 if (win)
4197 OnKeydown(win, wParam, lParam);
4198 break;
4199
4200 case WM_SETTINGCHANGE:
4201 InitMouseWheelInfo:
4202 SystemParametersInfo (SPI_GETWHEELSCROLLLINES, 0, &ulScrollLines, 0);
4203 // ulScrollLines usually equals 3 or 0 (for no scrolling)
4204 // WHEEL_DELTA equals 120, so iDeltaPerLine will be 40
4205 if (ulScrollLines)
4206 gDeltaPerLine = WHEEL_DELTA / ulScrollLines;
4207 else
4208 gDeltaPerLine = 0;
4209 return 0;
4210
4211 // TODO: I don't understand why WndProcCanvas() doesn't receive this message
4212 case WM_MOUSEWHEEL:
4213 if (!win || !win->dm) /* TODO: check for pdfDoc as well ? */
4214 break;
4215
4216 if (gDeltaPerLine == 0)
4217 break;
4218
4219 gAccumDelta += (short) HIWORD (wParam); // 120 or -120
4220
4221 while (gAccumDelta >= gDeltaPerLine)
4222 {
4223 SendMessage(win->hwndCanvas, WM_VSCROLL, SB_LINEUP, 0);
4224 gAccumDelta -= gDeltaPerLine;
4225 }
4226
4227 while (gAccumDelta <= -gDeltaPerLine)
4228 {
4229 SendMessage(win->hwndCanvas, WM_VSCROLL, SB_LINEDOWN, 0);
4230 gAccumDelta += gDeltaPerLine;
4231 }
4232 return 0;
4233
4234 case WM_DROPFILES:
4235 if (win)
4236 OnDropFiles(win, (HDROP)wParam);
4237 break;
4238
4239 case WM_DESTROY:
4240 /* WM_DESTROY might be sent as a result of File\Close, in which case CloseWindow() has already been called */
4241 if (win)
4242 CloseWindow(win, TRUE);
4243 break;
4244
4245 case IDM_VIEW_WITH_ACROBAT:
4246 if (win)
4247 ViewWithAcrobat(win);
4248 break;
4249
4250 case MSG_BENCH_NEXT_ACTION:
4251 if (win)
4252 OnBenchNextAction(win);
4253 break;
4254
4255 default:
4256 return DefWindowProc(hwnd, message, wParam, lParam);
4257 }
4258 return 0;
4259 }
4260
4261 static BOOL RegisterWinClass(HINSTANCE hInstance)
4262 {
4263 WNDCLASSEX wcex;
4264 ATOM atom;
4265
4266 wcex.cbSize = sizeof(WNDCLASSEX);
4267 wcex.style = CS_HREDRAW | CS_VREDRAW;
4268 wcex.lpfnWndProc = WndProcFrame;
4269 wcex.cbClsExtra = 0;
4270 wcex.cbWndExtra = 0;
4271 wcex.hInstance = hInstance;
4272 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SUMATRAPDF));
4273 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
4274 wcex.hbrBackground = NULL;
4275 wcex.lpszMenuName = NULL;
4276 wcex.lpszClassName = FRAME_CLASS_NAME;
4277 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
4278 atom = RegisterClassEx(&wcex);
4279 if (!atom)
4280 return FALSE;
4281
4282 wcex.cbSize = sizeof(WNDCLASSEX);
4283 wcex.style = CS_HREDRAW | CS_VREDRAW;
4284 wcex.lpfnWndProc = WndProcCanvas;
4285 wcex.cbClsExtra = 0;
4286 wcex.cbWndExtra = 0;
4287 wcex.hInstance = hInstance;
4288 wcex.hIcon = 0;
4289 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
4290 wcex.hbrBackground = NULL;
4291 wcex.lpszMenuName = NULL;
4292 wcex.lpszClassName = CANVAS_CLASS_NAME;
4293 wcex.hIconSm = 0;
4294 atom = RegisterClassEx(&wcex);
4295 if (!atom)
4296 return FALSE;
4297
4298 wcex.cbSize = sizeof(WNDCLASSEX);
4299 wcex.style = CS_HREDRAW | CS_VREDRAW;
4300 wcex.lpfnWndProc = WndProcAbout;
4301 wcex.cbClsExtra = 0;
4302 wcex.cbWndExtra = 0;
4303 wcex.hInstance = hInstance;
4304 wcex.hIcon = 0;
4305 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
4306 wcex.hbrBackground = NULL;
4307 wcex.lpszMenuName = NULL;
4308 wcex.lpszClassName = ABOUT_CLASS_NAME;
4309 wcex.hIconSm = 0;
4310 atom = RegisterClassEx(&wcex);
4311 if (!atom)
4312 return FALSE;
4313
4314 return TRUE;
4315 }
4316
4317 #define IDC_HAND MAKEINTRESOURCE(32649)
4318 static BOOL InstanceInit(HINSTANCE hInstance, int nCmdShow)
4319 {
4320 ghinst = hInstance;
4321
4322 globalParams = new GlobalParams("");
4323 if (!globalParams)
4324 return FALSE;
4325
4326 SplashColorsInit();
4327 gCursorArrow = LoadCursor(NULL, IDC_ARROW);
4328 gCursorHand = LoadCursor(NULL, IDC_HAND); // apparently only available if WINVER >= 0x0500
4329 if (!gCursorHand)
4330 gCursorHand = LoadCursor(ghinst, MAKEINTRESOURCE(IDC_CURSORDRAG));
4331 gCursorDrag = LoadCursor(ghinst, MAKEINTRESOURCE(IDC_CURSORDRAG));
4332 gBrushBg = CreateSolidBrush(COL_WINDOW_BG);
4333 gBrushWhite = CreateSolidBrush(COL_WHITE);
4334 gBrushShadow = CreateSolidBrush(COL_WINDOW_SHADOW);
4335 gBrushLinkDebug = CreateSolidBrush(RGB(0x00,0x00,0xff));
4336 return TRUE;
4337 }
4338
4339 static void StrList_Reverse(StrList **strListRoot)
4340 {
4341 StrList *newRoot = NULL;
4342 StrList *cur, *next;
4343 if (!strListRoot)
4344 return;
4345 cur = *strListRoot;
4346 while (cur) {
4347 next = cur->next;
4348 cur->next = newRoot;
4349 newRoot = cur;
4350 cur = next;
4351 }
4352 *strListRoot = newRoot;
4353 }
4354
4355 static BOOL StrList_InsertAndOwn(StrList **root, char *txt)
4356 {
4357 StrList * el;
4358 assert(root && txt);
4359 if (!root || !txt)
4360 return FALSE;
4361
4362 el = (StrList*)malloc(sizeof(StrList));
4363 if (!el)
4364 return FALSE;
4365 el->str = txt;
4366 el->next = *root;
4367 *root = el;
4368 return TRUE;
4369 }
4370
4371 static void StrList_Destroy(StrList **root)
4372 {
4373 StrList * cur;
4374 StrList * next;
4375
4376 if (!root)
4377 return;
4378 cur = *root;
4379 while (cur) {
4380 next = cur->next;
4381 free((void*)cur->str);
4382 free((void*)cur);
4383 cur = next;
4384 }
4385 *root = NULL;
4386 }
4387
4388 static StrList *StrList_FromCmdLine(char *cmdLine)
4389 {
4390 char * exePath;
4391 StrList * strList = NULL;
4392 char * txt;
4393
4394 assert(cmdLine);
4395
4396 if (!cmdLine)
4397 return NULL;
4398
4399 exePath = ExePathGet();
4400 if (!exePath)
4401 return NULL;
4402 if (!StrList_InsertAndOwn(&strList, exePath)) {
4403 free((void*)exePath);
4404 return NULL;
4405 }
4406
4407 for (;;) {
4408 txt = str_parse_possibly_quoted(&cmdLine);
4409 if (!txt)
4410 break;
4411 if (!StrList_InsertAndOwn(&strList, txt)) {
4412 free((void*)txt);
4413 break;
4414 }
4415 }
4416 StrList_Reverse(&strList);
4417 return strList;
4418 }
4419
4420 static void u_DoAllTests(void)
4421 {
4422 #ifdef DEBUG
4423 printf("Running tests\n");
4424 u_RectI_Intersect();
4425 #else
4426 printf("Not running tests\n");
4427 #endif
4428 }
4429
4430 #define CONSERVE_MEMORY 1
4431
4432 static DWORD WINAPI PageRenderThread(PVOID data)
4433 {
4434 PageRenderRequest req;
4435 RenderedBitmap * bmp;
4436
4437 DBG_OUT("PageRenderThread() started\n");
4438 while (1) {
4439 //DBG_OUT("Worker: wait\n");
4440 LockCache();
4441 gCurPageRenderReq = NULL;
4442 int count = gPageRenderRequestsCount;
4443 UnlockCache();
4444 if (0 == count) {
4445 DWORD waitResult = WaitForSingleObject(gPageRenderSem, INFINITE);
4446 if (WAIT_OBJECT_0 != waitResult) {
4447 DBG_OUT(" WaitForSingleObject() failed\n");
4448 continue;
4449 }
4450 }
4451 if (0 == gPageRenderRequestsCount) {
4452 continue;
4453 }
4454 LockCache();
4455 RenderQueue_Pop(&req);
4456 gCurPageRenderReq = &req;
4457 UnlockCache();
4458 DBG_OUT("PageRenderThread(): dequeued %d\n", req.pageNo);
4459 if (!req.dm->pageVisibleNearby(req.pageNo)) {
4460 DBG_OUT("PageRenderThread(): not rendering because not visible\n");
4461 continue;
4462 }
4463 assert(!req.abort);
4464 MsTimer renderTimer;
4465 bmp = req.dm->renderBitmap(req.pageNo, req.zoomLevel, req.rotation, pageRenderAbortCb, (void*)&req);
4466 renderTimer.stop();
4467 LockCache();
4468 gCurPageRenderReq = NULL;
4469 UnlockCache();
4470 if (req.abort) {
4471 delete bmp;
4472 continue;
4473 }
4474 if (bmp)
4475 DBG_OUT("PageRenderThread(): finished rendering %d\n", req.pageNo);
4476 else
4477 DBG_OUT("PageRenderThread(): failed to render a bitmap of page %d\n", req.pageNo);
4478 double renderTime = renderTimer.timeInMs();
4479 BitmapCache_Add(req.dm, req.pageNo, req.zoomLevel, req.rotation, bmp, renderTime);
4480 #ifdef CONSERVE_MEMORY
4481 BitmapCache_FreeNotVisible();
4482 #endif
4483 WindowInfo* win = (WindowInfo*)req.dm->appData();
4484 triggerRepaintDisplayNow(win);
4485 }
4486 DBG_OUT("PageRenderThread() finished\n");
4487 return 0;
4488 }
4489
4490 static void CreatePageRenderThread(void)
4491 {
4492 LONG semMaxCount = 1000; /* don't really know what the limit should be */
4493 DWORD dwThread1ID = 0;
4494 assert(NULL == gPageRenderThreadHandle);
4495
4496 gPageRenderSem = CreateSemaphore(NULL, 0, semMaxCount, NULL);
4497 gPageRenderThreadHandle = CreateThread(NULL, 0, PageRenderThread, (void*)NULL, 0, &dwThread1ID);
4498 assert(NULL != gPageRenderThreadHandle);
4499 }
4500
4501 static void PrintFile(WindowInfo *win, const char *fileName, const char *printerName)
4502 {
4503 char devstring[256]; // array for WIN.INI data
4504 HANDLE printer;
4505 LPDEVMODE devMode = NULL;
4506 DWORD structSize, returnCode;
4507
4508 if (!win->dm->pdfEngine()->printingAllowed()) { /* @note: TCHAR* and TEXT() casts */
4509 MessageBox(win->hwndFrame, TEXT("Cannot print this file"), TEXT("Printing problem."), MB_ICONEXCLAMATION | MB_OK);
4510 return;
4511 }
4512
4513 // Retrieve the printer, printer driver, and
4514 // output-port names from WIN.INI.
4515 GetProfileString(TEXT("Devices"), (TCHAR*)printerName, TEXT(""), (TCHAR*)devstring, sizeof(devstring));
4516
4517 // Parse the string of names, setting ptrs as required
4518 // If the string contains the required names, use them to
4519 // create a device context.
4520 char *driver = strtok (devstring, (const char *) ",");
4521 char *port = strtok((char *) NULL, (const char *) ",");
4522
4523 if (!driver || !port) {
4524 MessageBox(win->hwndFrame, TEXT("Printer with given name doesn't exist"), TEXT("Printing problem."), MB_ICONEXCLAMATION | MB_OK);
4525 return;
4526 }
4527
4528 BOOL fOk = OpenPrinter((TCHAR*)printerName, &printer, NULL); /* @note: neither LPCTSTR nor LPCTSTR work => TCHAR* cast */
4529 if (!fOk) {
4530 /* @note: translation need some care */
4531 //MessageBox(win->hwndFrame, _TR("Could not open Printer"), _TR("Printing problem."), MB_ICONEXCLAMATION | MB_OK);
4532 MessageBox(win->hwndFrame, TEXT("Could not open Printer"), TEXT("Printing problem."), MB_ICONEXCLAMATION | MB_OK);
4533 return;
4534 }
4535
4536 HDC hdcPrint = NULL;
4537 structSize = DocumentProperties(NULL,
4538 printer, /* Handle to our printer. */
4539 (TCHAR*) printerName, /* Name of the printer. */ /* @note: neither LPCTSTR nor LPCTSTR work => TCHAR* cast */
4540 NULL, /* Asking for size, so */
4541 NULL, /* these are not used. */
4542 0); /* Zero returns buffer size. */
4543 devMode = (LPDEVMODE)malloc(structSize);
4544 if (!devMode) {
4545 /* @note: "crosses initialization of [...]" issues */
4546 //goto Exit;
4547 free(devMode);
4548 DeleteDC(hdcPrint);
4549 }
4550
4551 // Get the default DevMode for the printer and modify it for your needs.
4552 returnCode = DocumentProperties(NULL,
4553 printer,
4554 (TCHAR*) printerName, /* Name of the printer. */ /* @note: neither LPCTSTR nor LPCTSTR work => TCHAR* cast */
4555 devMode, /* The address of the buffer to fill. */
4556 NULL, /* Not using the input buffer. */
4557 DM_OUT_BUFFER); /* Have the output buffer filled. */
4558
4559 if (IDOK != returnCode) {
4560 // If failure, inform the user, cleanup and return failure.
4561 MessageBox(win->hwndFrame, TEXT("Could not obtain Printer properties"), TEXT("Printing problem."), MB_ICONEXCLAMATION | MB_OK);
4562 /* @note: "crosses initialization of [...]" issues */
4563 //goto Exit;
4564 free(devMode);
4565 DeleteDC(hdcPrint);
4566 }
4567
4568 PdfPageInfo * pageInfo = pageInfo = win->dm->getPageInfo(1);
4569
4570 if (pageInfo->bitmapDx > pageInfo->bitmapDy) {
4571 devMode->dmOrientation = DMORIENT_LANDSCAPE;
4572 } else {
4573 devMode->dmOrientation = DMORIENT_PORTRAIT;
4574 }
4575
4576 /*
4577 * Merge the new settings with the old.
4578 * This gives the driver an opportunity to update any private
4579 * portions of the DevMode structure.
4580 */
4581 DocumentProperties(NULL,
4582 printer,
4583 (TCHAR*) printerName, /* Name of the printer. */ /* @note: neither LPCTSTR nor LPCTSTR work => TCHAR* cast */
4584 devMode, /* Reuse our buffer for output. */
4585 devMode, /* Pass the driver our changes. */
4586 DM_IN_BUFFER | /* Commands to Merge our changes and */
4587 DM_OUT_BUFFER); /* write the result. */
4588
4589 ClosePrinter(printer);
4590
4591 hdcPrint = CreateDC((TCHAR*)driver, (TCHAR*)printerName, (TCHAR*)port, devMode);
4592 if (!hdcPrint) {
4593 MessageBox(win->hwndFrame, TEXT("Couldn't initialize printer"), TEXT("Printing problem."), MB_ICONEXCLAMATION | MB_OK);
4594 /* @note: "crosses initialization of [...]" issues */
4595 //goto Exit;
4596 free(devMode);
4597 DeleteDC(hdcPrint);
4598 }
4599
4600 if (CheckPrinterStretchDibSupport(win->hwndFrame, hdcPrint))
4601 PrintToDevice(win->dm, hdcPrint, devMode, 1, win->dm->pageCount());
4602 Exit:
4603 free(devMode);
4604 DeleteDC(hdcPrint);
4605 }
4606
4607 static void EnumeratePrinters()
4608 {
4609 PRINTER_INFO_5 *info5Arr = NULL;
4610 DWORD bufSize = 0, printersCount;
4611 BOOL fOk = EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL,
4612 5, (LPBYTE)info5Arr, bufSize, &bufSize, &printersCount);
4613 if (!fOk) {
4614 info5Arr = (PRINTER_INFO_5*)malloc(bufSize);
4615 fOk = EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL,
4616 5, (LPBYTE)info5Arr, bufSize, &bufSize, &printersCount);
4617 }
4618 if (!info5Arr)
4619 return;
4620 assert(fOk);
4621 if (!fOk) return;
4622 printf("Printers: %d\n", printersCount);
4623 for (DWORD i=0; i < printersCount; i++) {
4624 const char *printerName = (char*)info5Arr[i].pPrinterName; /* @note: char* cast */
4625 const char *printerPort = (char*)info5Arr[i].pPortName; /* @note: char* cast */
4626 bool fDefault = false;
4627 if (info5Arr[i].Attributes & PRINTER_ATTRIBUTE_DEFAULT)
4628 fDefault = true;
4629 printf("Name: %s, port: %s, default: %d\n", printerName, printerPort, (int)fDefault);
4630 }
4631 TCHAR buf[512];
4632 bufSize = sizeof(buf);
4633 fOk = GetDefaultPrinter(buf, &bufSize);
4634 if (!fOk) {
4635 if (ERROR_FILE_NOT_FOUND == GetLastError())
4636 printf("No default printer\n");
4637 }
4638 free(info5Arr);
4639 }
4640
4641 /* Get the name of default printer or NULL if not exists.
4642 The caller needs to free() the result */
4643 char *GetDefaultPrinterName()
4644 {
4645 char buf[512];
4646 DWORD bufSize = sizeof(buf);
4647 if (GetDefaultPrinterA(buf, &bufSize))
4648 return str_dup(buf);
4649 return NULL;
4650 }
4651
4652 int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
4653 {
4654 StrList * argListRoot;
4655 StrList * currArg;
4656 char * benchPageNumStr = NULL;
4657 MSG msg = {0};
4658 HACCEL hAccelTable;
4659 WindowInfo* win;
4660 int pdfOpened = 0;
4661 bool exitOnPrint = false;
4662 bool printToDefaultPrinter = false;
4663
4664 UNREFERENCED_PARAMETER(hPrevInstance);
4665
4666 u_DoAllTests();
4667
4668 INITCOMMONCONTROLSEX cex;
4669 cex.dwSize = sizeof(INITCOMMONCONTROLSEX);
4670 cex.dwICC = ICC_WIN95_CLASSES | ICC_DATE_CLASSES | ICC_USEREX_CLASSES | ICC_COOL_CLASSES;
4671 InitCommonControlsEx(&cex);
4672
4673 argListRoot = StrList_FromCmdLine((char*)lpCmdLine); /* @note: char* cast */
4674 assert(argListRoot);
4675 if (!argListRoot)
4676 return 0;
4677
4678 Prefs_Load();
4679 /* parse argument list. If BENCH_ARG_TXT was given, then we're in benchmarking mode. Otherwise
4680 we assume that all arguments are PDF file names.
4681 BENCH_ARG_TXT can be followed by file or directory name. If file, it can additionally be followed by
4682 a number which we interpret as page number */
4683 bool registerForPdfExtentions = true;
4684 currArg = argListRoot->next;
4685 char *printerName = NULL;
4686 while (currArg) {
4687 if (IsEnumPrintersArg(currArg->str)) {
4688 EnumeratePrinters();
4689 /* this is for testing only, exit immediately */
4690 goto Exit;
4691 }
4692
4693 if (IsDontRegisterExtArg(currArg->str)) {
4694 registerForPdfExtentions = false;
4695 currArg = currArg->next;
4696 continue;
4697 }
4698
4699 if (IsBenchArg(currArg->str)) {
4700 currArg = currArg->next;
4701 if (currArg) {
4702 gBenchFileName = currArg->str;
4703 if (currArg->next)
4704 benchPageNumStr = currArg->next->str;
4705 }
4706 break;
4707 }
4708
4709 if (IsExitOnPrintArg(currArg->str)) {
4710 currArg = currArg->next;
4711 exitOnPrint = true;
4712 continue;
4713 }
4714
4715 if (IsPrintToDefaultArg(currArg->str)) {
4716 printToDefaultPrinter = true;
4717 continue;
4718 }
4719
4720 if (IsPrintToArg(currArg->str)) {
4721 currArg = currArg->next;
4722 if (currArg) {
4723 printerName = currArg->str;
4724 currArg = currArg->next;
4725 }
4726 continue;
4727 }
4728
4729 // we assume that switches come first and file names to open later
4730 // TODO: it would probably be better to collect all non-switches
4731 // in a separate list so that file names can be interspersed with
4732 // switches
4733 break;
4734 }
4735
4736 if (benchPageNumStr) {
4737 gBenchPageNum = atoi(benchPageNumStr);
4738 if (gBenchPageNum < 1)
4739 gBenchPageNum = INVALID_PAGE_NO;
4740 }
4741
4742 LoadString(hInstance, IDS_APP_TITLE, windowTitle, MAX_LOADSTRING);
4743 if (!RegisterWinClass(hInstance))
4744 goto Exit;
4745
4746 CaptionPens_Create();
4747 if (!InstanceInit(hInstance, nCmdShow))
4748 goto Exit;
4749
4750 hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SUMATRAPDF));
4751
4752 CreatePageRenderThread();
4753 /* remaining arguments are names of PDF files */
4754 if (NULL != gBenchFileName) {
4755 win = LoadPdf(gBenchFileName);
4756 if (win)
4757 ++pdfOpened;
4758 } else {
4759 while (currArg) {
4760 win = LoadPdf(currArg->str);
4761 if (!win)
4762 goto Exit;
4763
4764 if (exitOnPrint)
4765 ShowWindow(win->hwndFrame, SW_HIDE);
4766
4767 if (printToDefaultPrinter) {
4768 printerName = GetDefaultPrinterName();
4769 if (printerName)
4770 PrintFile(win, currArg->str, printerName);
4771 free(printerName);
4772 } else if (printerName) {
4773 // note: this prints all of PDF files. Another option would be to
4774 // print only the first one
4775 PrintFile(win, currArg->str, printerName);
4776 }
4777 ++pdfOpened;
4778 currArg = currArg->next;
4779 }
4780 }
4781
4782 if (printerName && exitOnPrint)
4783 goto Exit;
4784
4785 if (0 == pdfOpened) {
4786 /* disable benchmark mode if we couldn't open file to benchmark */
4787 gBenchFileName = 0;
4788 #ifdef REOPEN_FILES_AT_STARTUP
4789 FileHistoryList * currFile = gFileHistoryRoot;
4790 while (currFile) {
4791 if (currFile->state.visible) {
4792 win = LoadPdf(currFile->state.filePath, false);
4793 if (win)
4794 ++pdfOpened;
4795 }
4796 currFile = currFile->next;
4797 }
4798 #endif
4799 if (0 == pdfOpened) {
4800 win = WindowInfo_CreateEmpty();
4801 if (!win)
4802 goto Exit;
4803 WindowInfoList_Add(win);
4804
4805 /* TODO: should this be part of WindowInfo_CreateEmpty() ? */
4806 DragAcceptFiles(win->hwndFrame, TRUE);
4807 ShowWindow(win->hwndCanvas, SW_SHOW);
4808 UpdateWindow(win->hwndCanvas);
4809 ShowWindow(win->hwndFrame, SW_SHOW);
4810 UpdateWindow(win->hwndFrame);
4811 }
4812 }
4813
4814 if (IsBenchMode()) {
4815 assert(win);
4816 assert(pdfOpened > 0);
4817 if (win)
4818 PostBenchNextAction(win->hwndFrame);
4819 }
4820
4821 if (0 == pdfOpened)
4822 MenuToolbarUpdateStateForAllWindows();
4823
4824 if (registerForPdfExtentions)
4825 RegisterForPdfExtentions(win ? win->hwndFrame : NULL);
4826
4827 while (GetMessage(&msg, NULL, 0, 0)) {
4828 if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
4829 TranslateMessage(&msg);
4830 DispatchMessage(&msg);
4831 }
4832 }
4833
4834 Exit:
4835 WindowInfoList_DeleteAll();
4836 FileHistoryList_Free(&gFileHistoryRoot);
4837 CaptionPens_Destroy();
4838 DeleteObject(gBrushBg);
4839 DeleteObject(gBrushWhite);
4840 DeleteObject(gBrushShadow);
4841 DeleteObject(gBrushLinkDebug);
4842
4843 delete globalParams;
4844 StrList_Destroy(&argListRoot);
4845 Translations_FreeData();
4846 CurrLangNameFree();
4847 //histDump();
4848 return (int) msg.wParam;
4849 }
4850
4851 // Code for DLL interace
4852 static WindowInfo* CreateEmpty(HWND parentHandle) {
4853 WindowInfo* pdfWin;
4854 HWND hwndCanvas;
4855 pdfWin = WindowInfo_New(parentHandle);
4856 hwndCanvas = CreateWindow(
4857 CANVAS_CLASS_NAME, NULL,
4858 WS_CHILD | WS_HSCROLL | WS_VSCROLL,
4859 CW_USEDEFAULT, CW_USEDEFAULT,
4860 DEF_WIN_DX, DEF_WIN_DY,
4861 parentHandle, NULL,
4862 ghinst, NULL);
4863 if (hwndCanvas)
4864 pdfWin->hwndCanvas = hwndCanvas;
4865 return pdfWin;
4866 }
4867
4868 static void OpenPdf(WindowInfo* pdfWin,const char *fileName, HWND parentHandle)
4869 {
4870 assert(fileName);
4871 if (!fileName) return;
4872 assert(pdfWin);
4873 if (!pdfWin) return;
4874
4875 pdfWin->GetCanvasSize();
4876 SizeI maxCanvasSize = GetMaxCanvasSize(pdfWin);
4877 SizeD totalDrawAreaSize(pdfWin->winSize());
4878 DisplayMode displayMode = DEFAULT_DISPLAY_MODE;
4879 int offsetX = 0;
4880 int offsetY = 0;
4881 int startPage = 1;
4882 int scrollbarYDx = 0;
4883 int scrollbarXDy = 0;
4884
4885 if (gUseFitz) {
4886 pdfWin->dm = DisplayModelFitz_CreateFromFileName(fileName,
4887 totalDrawAreaSize, scrollbarYDx, scrollbarXDy, displayMode, startPage, pdfWin);
4888 }
4889 else {
4890 pdfWin->dm = DisplayModelSplash_CreateFromFileName(fileName,
4891 totalDrawAreaSize, scrollbarYDx, scrollbarXDy, displayMode, startPage, pdfWin);
4892 }
4893
4894 pdfWin->dm->setAppData((void*)pdfWin);
4895 pdfWin->state = WS_SHOWING_PDF;
4896 double zoomVirtual = DEFAULT_ZOOM;
4897 int rotation = DEFAULT_ROTATION;
4898
4899 UINT menuId = MenuIdFromVirtualZoom(zoomVirtual);
4900 ZoomMenuItemCheck(GetMenu(pdfWin->hwndFrame), menuId);
4901
4902 pdfWin->dm->relayout(zoomVirtual, rotation);
4903 if (!pdfWin->dm->validPageNo(startPage))
4904 startPage = 1;
4905 offsetY = 0;
4906 pdfWin->dm->goToPage(startPage, offsetY, offsetX);
4907 WindowInfo_ResizeToPage(pdfWin, startPage);
4908 WindowInfoList_Add(pdfWin);
4909
4910 RECT rect;
4911 if (GetWindowRect(pdfWin->hwndFrame , &rect) != 0)
4912 {
4913 int nWidth = rect_dx(&rect);
4914 int nHeight = rect_dy(&rect);
4915 WinResizeClientArea(pdfWin->hwndCanvas, nWidth, nHeight);
4916 }
4917
4918 ShowWindow(pdfWin->hwndFrame, SW_SHOW);
4919 ShowWindow(pdfWin->hwndCanvas, SW_SHOW);
4920 UpdateWindow(pdfWin->hwndFrame);
4921 UpdateWindow(pdfWin->hwndCanvas);
4922 }
4923
4924 void Sumatra_LoadPDF(WindowInfo* pdfWin, const char *pdfFile)
4925 {
4926 int pdfOpened = 0;
4927 OpenPdf(pdfWin, pdfFile, pdfWin->hwndFrame);
4928 ++pdfOpened;
4929 if (pdfWin)
4930 ShowWindow(pdfWin->hwndFrame, SW_SHOWNORMAL);
4931 }
4932
4933 void Sumatra_PrintPDF(WindowInfo* pdfWin, const char *pdfFile, long showOptionWindow)
4934 {
4935 }
4936
4937 void Sumatra_Print(WindowInfo* pdfWin)
4938 {
4939 if (WindowInfo_PdfLoaded(pdfWin))
4940 OnMenuPrint(pdfWin);
4941 }
4942
4943 void Sumatra_ShowPrintDialog(WindowInfo* pdfWin)
4944 {
4945 if (WindowInfo_PdfLoaded(pdfWin))
4946 OnMenuPrint(pdfWin);
4947 }
4948
4949 void Sumatra_SetDisplayMode(WindowInfo* pdfWin,long displayMode)
4950 {
4951 if (WindowInfo_PdfLoaded(pdfWin))
4952 SwitchToDisplayMode(pdfWin, (DisplayMode)displayMode);
4953 }
4954
4955 long Sumatra_GoToNextPage(WindowInfo* pdfWin)
4956 {
4957 if (!WindowInfo_PdfLoaded(pdfWin))
4958 return 0;
4959 pdfWin->dm->goToNextPage(0);
4960 return pdfWin->dm->currentPageNo();
4961 }
4962
4963 long Sumatra_GoToPreviousPage(WindowInfo* pdfWin)
4964 {
4965 if (!WindowInfo_PdfLoaded(pdfWin))
4966 return 0;
4967 pdfWin->dm->goToPrevPage(0);
4968 return pdfWin->dm->currentPageNo();
4969 }
4970
4971 long Sumatra_GoToFirstPage(WindowInfo* pdfWin)
4972 {
4973 if (!WindowInfo_PdfLoaded(pdfWin))
4974 return 0;
4975 pdfWin->dm->goToFirstPage();
4976 return pdfWin->dm->currentPageNo();
4977 }
4978
4979 long Sumatra_GoToLastPage(WindowInfo* pdfWin)
4980 {
4981 if (!WindowInfo_PdfLoaded(pdfWin))
4982 return 0;
4983 pdfWin->dm->goToLastPage();
4984 return pdfWin->dm->currentPageNo();
4985 }
4986
4987 long Sumatra_GetNumberOfPages(WindowInfo* pdfWin)
4988 {
4989 if (!WindowInfo_PdfLoaded(pdfWin))
4990 return 0;
4991 return pdfWin->dm->pageCount();
4992 }
4993
4994 long Sumatra_GetCurrentPage(WindowInfo* pdfWin)
4995 {
4996 if (!WindowInfo_PdfLoaded(pdfWin))
4997 return 0;
4998 return pdfWin->dm->currentPageNo();
4999 }
5000
5001 long Sumatra_GoToThisPage(WindowInfo* pdfWin,long pageNumber)
5002 {
5003 if (!WindowInfo_PdfLoaded(pdfWin))
5004 return 0;
5005 if (pdfWin->dm->validPageNo(pageNumber))
5006 pdfWin->dm->goToPage(pageNumber, 0);
5007 return pdfWin->dm->currentPageNo();
5008 }
5009
5010 long Sumatra_ZoomIn(WindowInfo* pdfWin)
5011 {
5012 if (WindowInfo_PdfLoaded(pdfWin))
5013 {
5014 long currentZoom = Sumatra_GetCurrentZoom(pdfWin);
5015 if (currentZoom < 500)
5016 Sumatra_SetZoom(pdfWin,currentZoom+10);
5017 }
5018 return Sumatra_GetCurrentZoom(pdfWin);
5019 }
5020
5021 long Sumatra_ZoomOut(WindowInfo* pdfWin)
5022 {
5023 if (WindowInfo_PdfLoaded(pdfWin))
5024 {
5025 long currentZoom = Sumatra_GetCurrentZoom(pdfWin);
5026 if (currentZoom > 10)
5027 Sumatra_SetZoom(pdfWin,currentZoom-10);
5028 }
5029 return Sumatra_GetCurrentZoom(pdfWin);
5030 }
5031
5032 long Sumatra_SetZoom(WindowInfo* pdfWin,long zoomValue)
5033 {
5034 if (WindowInfo_PdfLoaded(pdfWin))
5035 pdfWin->dm->zoomTo((double)zoomValue);
5036 return Sumatra_GetCurrentZoom(pdfWin);
5037 }
5038
5039 long Sumatra_GetCurrentZoom(WindowInfo* pdfWin)
5040 {
5041 double zoomLevel = 0;
5042 if (WindowInfo_PdfLoaded(pdfWin))
5043 zoomLevel = pdfWin->dm->zoomReal();
5044 return (long)zoomLevel;
5045 }
5046
5047 void Sumatra_Resize(WindowInfo* pdfWin)
5048 {
5049 RECT rect;
5050 if (GetWindowRect(pdfWin->hwndFrame , &rect) != 0)
5051 {
5052 int nWidth = rect_dx(&rect);
5053 int nHeight = rect_dy(&rect);
5054 WinResizeClientArea(pdfWin->hwndCanvas, nWidth, nHeight);
5055 }
5056 }
5057
5058 void Sumatra_ClosePdf(WindowInfo* pdfWin)
5059 {
5060 if (WindowInfo_PdfLoaded(pdfWin))
5061 CloseWindow(pdfWin, FALSE);
5062 }
5063
5064 WindowInfo* Sumatra_Init(HWND pHandle)
5065 {
5066 WindowInfo* pdfWin;
5067 gRunningDLL = true;
5068 HINSTANCE hInstance = NULL;
5069 HINSTANCE hPrevInstance = NULL;
5070 int nCmdShow = 0;
5071
5072 StrList * argListRoot = NULL;
5073 StrList * currArg = NULL;
5074 MSG msg = {0};
5075 bool exitOnPrint = false;
5076 bool printToDefaultPrinter = false;
5077 gUseFitz = TRUE;
5078
5079 UNREFERENCED_PARAMETER(hPrevInstance);
5080
5081 u_DoAllTests();
5082
5083 INITCOMMONCONTROLSEX cex;
5084 cex.dwSize = sizeof(INITCOMMONCONTROLSEX);
5085 cex.dwICC = ICC_WIN95_CLASSES | ICC_DATE_CLASSES | ICC_USEREX_CLASSES | ICC_COOL_CLASSES;
5086 InitCommonControlsEx(&cex);
5087 argListRoot = NULL;
5088
5089 LoadString(hInstance, IDS_APP_TITLE, windowTitle, MAX_LOADSTRING);
5090
5091 if (!RegisterWinClass(hInstance))
5092 Sumatra_Exit();
5093
5094 CaptionPens_Create();
5095
5096 if (!InstanceInit(hInstance, nCmdShow))
5097 Sumatra_Exit();
5098
5099 CreatePageRenderThread();
5100
5101 bool reuseExistingWindow = false;
5102
5103 if (pHandle == 0 )
5104 pHandle = NULL;
5105
5106 pdfWin = CreateEmpty(pHandle);
5107
5108 return pdfWin;
5109 }
5110
5111 void Sumatra_Exit()
5112 {
5113 CaptionPens_Destroy();
5114 DeleteObject(gBrushBg);
5115 DeleteObject(gBrushWhite);
5116 DeleteObject(gBrushShadow);
5117 DeleteObject(gBrushLinkDebug);
5118 delete globalParams;
5119 }