1 /* Copyright Krzysztof Kowalczyk 2006-2007
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 :-\
12 #include "SumatraPDF.h"
15 #include "file_util.h"
16 #include "geom_util.h"
18 #include "translations.h"
20 #include "SumatraDialogs.h"
21 #include "FileHistory.h"
23 #include "DisplayModelSplash.h"
25 /* TODO: this and StandardSecurityHandler::getAuthData() and new GlobalParams
26 should be moved to another file (PopplerInit(), PopplerDeinit() */
28 #include "SecurityHandler.h"
29 #include "GlobalParams.h"
35 #include <direct.h> /* for _mkdir() */
40 #include "str_strsafe.h"
44 // some stupid things are in headers of MinGW 5.1.3 :-\
45 // why we have to define these constants & prototypes again (?!)
48 #define VK_OEM_PLUS 0xBB
51 #define VK_OEM_MINUS 0xBD
55 extern BOOL WINAPI
GetDefaultPrinterA(LPSTR
,LPDWORD
);
56 extern BOOL WINAPI
GetDefaultPrinterW(LPWSTR
,LPDWORD
);
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='*'\"")
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
70 /* Next action for the benchmark mode */
71 #define MSG_BENCH_NEXT_ACTION WM_USER + 1
73 #define ZOOM_IN_FACTOR 1.2
74 #define ZOOM_OUT_FACTOR 1.0 / ZOOM_IN_FACTOR
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. */
80 static BOOL gDebugShowLinks
= TRUE
;
82 static BOOL gDebugShowLinks
= FALSE
;
85 /* default UI settings */
86 #define DEFAULT_DISPLAY_MODE DM_CONTINUOUS
88 //#define DEFAULT_ZOOM ZOOM_FIT_WIDTH
89 #define DEFAULT_ZOOM ZOOM_FIT_PAGE
90 #define DEFAULT_ROTATION 0
92 //#define START_WITH_ABOUT 1
94 /* define if want to use double-buffering for rendering the PDF. Takes more memory!. */
95 #define DOUBLE_BUFFER 1
97 #define DRAGQUERY_NUMFILES 0xFFFFFFFF
99 #define MAX_LOADSTRING 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
107 #define ABOUT_WIN_DX 440
108 #define ABOUT_WIN_DY 300
110 #define WM_APP_REPAINT_DELAYED (WM_APP + 10)
111 #define WM_APP_REPAINT_NOW (WM_APP + 11)
113 /* A caption is 4 white/blue 2 pixel line and a 3 pixel white line */
114 #define CAPTION_DY 2*(2*4)+3
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)
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")
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"
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
142 #define REPAINT_TIMER_ID 1
143 #define REPAINT_DELAY_IN_MS 400
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
149 #define WS_REBAR (WS_CHILD | WS_CLIPCHILDREN | WS_BORDER | RBS_VARHEIGHT | \
150 RBS_BANDBORDERS | CCS_NODIVIDER | CCS_NOPARENTALIGN)
152 #define MAX_RECENT_FILES_IN_MENU 15
154 typedef struct StrList
{
155 struct StrList
* next
;
159 static FileHistoryList
* gFileHistoryRoot
= NULL
;
161 static HINSTANCE ghinst
= NULL
;
162 TCHAR windowTitle
[MAX_LOADSTRING
];
164 static WindowInfo
* gWindowList
;
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
;
174 static HPEN ghpenWhite
;
175 static HPEN ghpenBlue
;
178 static bool gRunningDLL
= true;
180 static bool gRunningDLL
= false;
182 //static AppVisualStyle gVisualStyle = VS_WINDOWS;
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
192 BOOL gPdfAssociateShouldAssociate
= TRUE
;
194 static bool gUseDoubleBuffer
= true;
196 static bool gUseDoubleBuffer
= false;
199 #define MAX_PAGE_REQUESTS 8
200 static PageRenderRequest gPageRenderRequests
[MAX_PAGE_REQUESTS
];
201 static int gPageRenderRequestsCount
= 0;
203 static HANDLE gPageRenderThreadHandle
;
204 static HANDLE gPageRenderSem
;
205 static PageRenderRequest
* gCurPageRenderReq
;
208 static int gReBarDyFrame
;
209 static HWND gHwndAbout
;
211 typedef struct ToolbarButtonInfo
{
212 /* information provided at compile time */
213 int bitmapResourceId
;
217 /* information calculated at runtime */
221 #define IDB_SEPARATOR -1
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 */
233 #define DEFAULT_LANGUAGE "en"
235 #define TOOLBAR_BUTTONS_COUNT dimof(gToolbarButtons)
237 static const char *g_currLangName
;
240 const char* _langName
;
249 #define LANGS_COUNT dimof(g_langs)
251 static void WindowInfo_ResizeToPage(WindowInfo
*win
, int pageNo
);
252 static void CreateToolbar(WindowInfo
*win
, HINSTANCE hInst
);
253 static void RebuildProgramMenus(void);
255 const char* CurrLangNameGet() {
257 return DEFAULT_LANGUAGE
;
258 return g_currLangName
;
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
)) {
270 if (!validLang
) return false;
271 free((void*)g_currLangName
);
272 g_currLangName
= str_dup(langName
);
274 bool ok
= Translations_SetCurrentLanguage(langName
);
280 void CurrLangNameFree() {
281 free((void*)g_currLangName
);
282 g_currLangName
= NULL
;
285 /*void LaunchBrowser(const TCHAR *url)
290 static BOOL
pageRenderAbortCb(void *data
)
292 PageRenderRequest
*req
= (PageRenderRequest
*)data
;
294 DBG_OUT("Rendering of page %d aborted\n", req
->pageNo
);
301 void RenderQueue_RemoveForDisplayModel(DisplayModel
*dm
) {
303 int reqCount
= gPageRenderRequestsCount
;
305 for(int i
= 0; i
< reqCount
; i
++) {
306 PageRenderRequest
*req
= &(gPageRenderRequests
[i
]);
307 bool shouldRemove
= (req
->dm
== dm
);
309 gPageRenderRequests
[curPos
] = gPageRenderRequests
[i
];
311 --gPageRenderRequestsCount
;
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
) {
323 DBG_OUT("cancelRenderingForDisplayModel()\n");
324 bool renderingFinished
= false;;
327 if (!gCurPageRenderReq
|| (gCurPageRenderReq
->dm
!= dm
))
328 renderingFinished
= true;
330 gCurPageRenderReq
->abort
= TRUE
;
332 if (renderingFinished
)
334 /* TODO: busy loop is not good, but I don't have a better idea */
335 sleep_milliseconds(500);
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
);
343 if (!dm
) return; //goto Exit; /* @note: because of crosses initialization of [...] */
346 PdfPageInfo
*pageInfo
= dm
->getPageInfo(pageNo
);
347 int rotation
= dm
->rotation();
348 normalizeRotation(&rotation
);
349 double zoomLevel
= dm
->zoomReal();
351 if (BitmapCache_Exists(dm
, pageNo
, zoomLevel
, rotation
)) {
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
;
363 /* we're already rendering exactly the same page */
364 DBG_OUT(" already rendering this page\n");
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
;
380 DBG_OUT(" already queued\n");
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
;
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]);
401 newRequest
= &(gPageRenderRequests
[gPageRenderRequestsCount
]);
402 gPageRenderRequestsCount
++;
404 assert(gPageRenderRequestsCount
<= MAX_PAGE_REQUESTS
);
406 newRequest
->pageNo
= pageNo
;
407 newRequest
->zoomLevel
= zoomLevel
;
408 newRequest
->rotation
= rotation
;
409 newRequest
->abort
= FALSE
;
412 /* tell rendering thread there's a new request to render */
414 ReleaseSemaphore(gPageRenderSem
, 1, &prevCount
);
422 void RenderQueue_Pop(PageRenderRequest
*req
)
425 assert(gPageRenderRequestsCount
> 0);
426 assert(gPageRenderRequestsCount
<= MAX_PAGE_REQUESTS
);
427 --gPageRenderRequestsCount
;
428 *req
= gPageRenderRequests
[gPageRenderRequestsCount
];
429 assert(gPageRenderRequestsCount
>= 0);
433 static HMENU
FindMenuItem(WindowInfo
*win
, UINT id
)
435 HMENU menuMain
= GetMenu(win
->hwndFrame
);
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
);
443 for (int j
= 0; j
< GetMenuItemCount(subMenu
); j
++) {
444 thisId
= GetMenuItemID(menuMain
, j
);
446 return GetSubMenu(subMenu
, j
);
452 static HMENU
GetFileMenu(HWND hwnd
)
454 return GetSubMenu(GetMenu(hwnd
), 0);
457 static void SwitchToDisplayMode(WindowInfo
*win
, DisplayMode displayMode
)
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
);
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
;
480 CheckMenuItem(menuMain
, id
, MF_BYCOMMAND
| MF_CHECKED
);
483 static UINT
AllocNewMenuId(void)
485 static UINT firstId
= 1000;
490 #define SEP_ITEM "-----"
492 typedef struct MenuDef
{
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
},
503 { _TRN("Make SumatraPDF a default PDF reader"), IDM_MAKE_DEFAULT_READER
},
505 { _TRN("E&xit\tCtrl-Q"), IDM_EXIT
}
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
},
514 { _TRN("Rotate left"), IDM_VIEW_ROTATE_LEFT
},
515 { _TRN("Rotate right"), IDM_VIEW_ROTATE_RIGHT
},
517 { _TRN("Show toolbar"), IDM_VIEW_SHOW_HIDE_TOOLBAR
},
519 { _TRN("Use MuPDF rendering engine"), IDM_VIEW_USE_FITZ
},
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
},
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
},
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
},
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
},
557 MenuDef menuDefHelp
[] = {
558 { _TRN("&Visit website"), IDM_VISIT_WEBSITE
},
559 { _TRN("&About"), IDM_ABOUT
}
562 static void AddFileMenuItem(HMENU menuFile
, FileHistoryList
*node
)
567 if (!menuFile
) return;
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
;
577 static HMENU
BuildMenuFromMenuDef(MenuDef menuDefs
[], int menuItems
)
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
;
585 if (str_eq(title
, SEP_ITEM
))
586 AppendMenu(m
, MF_SEPARATOR
, 0, NULL
);
588 const WCHAR
* wtitle
= Translatations_GetTranslationW(title
);
590 AppendMenuW(m
, MF_STRING
, (UINT_PTR
)id
, wtitle
);
596 static HMENU g_currMenu
= NULL
;
598 static void DestroyCurrentMenu()
600 DestroyMenu(g_currMenu
);
604 static void AddRecentFilesToMenu(HMENU m
)
606 if (!gFileHistoryRoot
) return;
608 AppendMenu(m
, MF_SEPARATOR
, 0, NULL
);
611 FileHistoryList
*curr
= gFileHistoryRoot
;
613 assert(curr
->state
.filePath
);
614 if (curr
->state
.filePath
) {
615 AddFileMenuItem(m
, curr
);
616 assert(curr
->menuId
!= INVALID_MENU_ID
);
618 if (itemsAdded
>= MAX_RECENT_FILES_IN_MENU
) {
619 DBG_OUT(" not adding, reached max %d items\n", MAX_RECENT_FILES_IN_MENU
);
627 static HMENU
ForceRebuildMenu()
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"));
648 static HMENU
GetProgramMenu()
650 if (NULL
== g_currMenu
)
656 /* Return the full exe path of my own executable.
657 Caller needs to free() the result. */
658 static char *ExePathGet(void)
660 char *cmdline
= GetCommandLineA();
661 return str_parse_possibly_quoted(&cmdline
);
664 /* Set the client area size of the window 'hwnd' to 'dx'/'dy'. */
665 static void WinResizeClientArea(HWND hwnd
, int dx
, int dy
)
668 GetClientRect(hwnd
, &rc
);
669 if ((rect_dx(&rc
) == dx
) && (rect_dy(&rc
) == dy
))
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
);
678 static void SetCanvasSizeToDxDy(WindowInfo
*win
, int w
, int h
)
681 GetWindowRect(win
->hwndCanvas
, &canvasRect
);
683 GetWindowRect(win
->hwndFrame
, &frameRect
);
684 int dx
= rect_dx(&frameRect
) - rect_dx(&canvasRect
);
686 int dy
= rect_dy(&frameRect
) - rect_dy(&canvasRect
);
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);
692 static void CaptionPens_Create(void)
697 pen
.lopnStyle
= PS_SOLID
;
700 pen
.lopnColor
= COL_WHITE
;
701 ghpenWhite
= CreatePenIndirect(&pen
);
702 pen
.lopnColor
= COL_CAPTION_BLUE
;
703 ghpenBlue
= CreatePenIndirect(&pen
);
706 static void CaptionPens_Destroy(void)
709 DeleteObject(ghpenWhite
);
714 DeleteObject(ghpenBlue
);
719 static void AddFileToHistory(const char *filePath
)
721 FileHistoryList
* node
;
722 uint32_t oldMenuId
= INVALID_MENU_ID
;
725 if (!filePath
) return;
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
);
731 oldMenuId
= node
->menuId
;
732 FileHistoryList_Node_RemoveAndFree(&gFileHistoryRoot
, node
);
734 node
= FileHistoryList_Node_CreateFromFilePath(filePath
);
737 node
->menuId
= oldMenuId
;
738 FileHistoryList_Node_InsertHead(&gFileHistoryRoot
, node
);
741 extern "C" char *GetPasswordForFile(WindowInfo
*win
, const char *fileName
);
743 /* Get password for a given 'fileName', can be NULL if user cancelled the
745 Caller needs to free() the result. */
746 char *GetPasswordForFile(WindowInfo
*win
, const char *fileName
)
748 fileName
= FilePath_GetBaseName(fileName
);
749 return Dialog_GetPassword(win
, fileName
);
752 void *StandardSecurityHandler::getAuthData()
756 StandardAuthData
* authData
;
758 win
= (WindowInfo
*)doc
->getGUIData();
763 pwd
= GetPasswordForFile(win
, doc
->getFileName()->getCString());
767 authData
= new StandardAuthData(new GooString(pwd
), new GooString(pwd
));
769 return (void*)authData
;
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)
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;
782 if (str_startswithi(exePath
, programFilesDir
))
783 fromProgramFiles
= true;
785 // SHGetSpecialFolderPath() might fail on win95/98 so need a different check
786 if (strstr(exePath
, "Program Files"))
787 fromProgramFiles
= true;
790 return fromProgramFiles
;
793 static bool IsRunningInPortableMode(void)
795 return !runningFromProgramFiles();
798 static void AppGetAppDir(DString
* pDs
)
802 SHGetSpecialFolderPath(NULL
, (TCHAR
*)dir
, CSIDL_APPDATA
, TRUE
); /* @note: TCHAR* cast */
803 DStringSprintf(pDs
, "%s/%s", dir
, APP_SUB_DIR
);
804 _mkdir(pDs
->pString
);
807 /* Generate the full path for a filename used by the app in the userdata path. */
808 static void AppGenDataFilename(char* pFilename
, DString
* pDs
)
810 assert(0 == pDs
->length
);
812 if (!pFilename
) return;
816 bool portable
= IsRunningInPortableMode();
818 /* Use the same path as the binary */
819 char *exePath
= ExePathGet();
820 if (!exePath
) return;
821 char *dir
= FilePath_GetDir(exePath
);
823 DStringSprintf(pDs
, "%s", dir
);
824 free((void*)exePath
);
829 if (!str_endswithi(pDs
->pString
, DIR_SEP_STR
) && !(DIR_SEP_CHAR
== pFilename
[0])) {
830 DStringAppend(pDs
, DIR_SEP_STR
, -1);
832 DStringAppend(pDs
, pFilename
, -1);
835 static void Prefs_GetFileName(DString
* pDs
)
837 assert(0 == pDs
->length
);
838 AppGenDataFilename((char*)PREFS_FILE_NAME
, pDs
); /* @note: char* cast */
841 /* Load preferences from the preferences file. */
842 static void Prefs_Load(void)
845 static bool loaded
= false;
846 char * prefsTxt
= NULL
;
851 DBG_OUT("Prefs_Load()\n");
854 Prefs_GetFileName(&path
);
857 prefsTxt
= file_read_all(path
.pString
, &prefsFileLen
);
858 if (str_empty(prefsTxt
)) {
859 DBG_OUT(" no prefs file or is empty\n");
862 DBG_OUT("Prefs file %s:\n%s\n", path
.pString
, prefsTxt
);
864 bool fOk
= Prefs_Deserialize(prefsTxt
, &gFileHistoryRoot
);
868 free((void*)prefsTxt
);
871 static struct idToZoomMap
{
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 }
893 static UINT
MenuIdFromVirtualZoom(double virtualZoom
)
895 for (int i
=0; i
< dimof(gZoomMenuItemsId
); i
++) {
896 if (virtualZoom
== gZoomMenuItemsId
[i
].zoom
)
897 return gZoomMenuItemsId
[i
].id
;
899 return IDM_ZOOM_ACTUAL_SIZE
;
902 static void ZoomMenuItemCheck(HMENU hmenu
, UINT menuItemId
)
906 for (int i
=0; i
<dimof(gZoomMenuItemsId
); i
++) {
907 UINT checkState
= MF_BYCOMMAND
| MF_UNCHECKED
;
908 if (menuItemId
== gZoomMenuItemsId
[i
].id
) {
911 checkState
= MF_BYCOMMAND
| MF_CHECKED
;
913 CheckMenuItem(hmenu
, gZoomMenuItemsId
[i
].id
, checkState
);
918 static double ZoomMenuItemToZoom(UINT menuItemId
)
920 for (int i
=0; i
<dimof(gZoomMenuItemsId
); i
++) {
921 if (menuItemId
== gZoomMenuItemsId
[i
].id
) {
922 return gZoomMenuItemsId
[i
].zoom
;
929 static void Win32_Win_GetSize(HWND hwnd
, int *dxOut
, int *dyOut
)
935 if (GetWindowRect(hwnd
, &r
)) {
936 *dxOut
= (r
.right
- r
.left
);
937 *dyOut
= (r
.bottom
- r
.top
);
941 static void Win32_Win_GetPos(HWND hwnd
, int *xOut
, int *yOut
)
947 if (GetWindowRect(hwnd
, &r
)) {
953 static void Win32_Win_SetPos(HWND hwnd
, int x
, int y
)
955 SetWindowPos(hwnd
, NULL
, x
, y
, 0, 0, SWP_NOACTIVATE
| SWP_NOOWNERZORDER
| SWP_NOSIZE
);
958 static void UpdateDisplayStateWindowPos(WindowInfo
*win
, DisplayState
*ds
)
962 Win32_Win_GetPos(win
->hwndCanvas
, &posX
, &posY
);
968 static void UpdateCurrentFileDisplayStateForWin(WindowInfo
*win
)
971 const char * fileName
= NULL
;
972 FileHistoryList
*node
= NULL
;
976 if (WS_SHOWING_PDF
!= win
->state
)
981 fileName
= win
->dm
->fileName();
988 node
= FileHistoryList_Node_FindByFilePath(&gFileHistoryRoot
, fileName
);
994 DisplayState_Init(&ds
);
995 if (!displayStateFromDisplayModel(&ds
, win
->dm
))
998 UpdateDisplayStateWindowPos(win
, &ds
);
999 DisplayState_Free(&(node
->state
));
1001 node
->state
.visible
= TRUE
;
1004 static void UpdateCurrentFileDisplayState(void)
1006 WindowInfo
* currWin
;
1007 FileHistoryList
* currFile
;
1009 currFile
= gFileHistoryRoot
;
1011 currFile
->state
.visible
= FALSE
;
1012 currFile
= currFile
->next
;
1015 currWin
= gWindowList
;
1017 UpdateCurrentFileDisplayStateForWin(currWin
);
1018 currWin
= currWin
->next
;
1022 static void Prefs_Save(void)
1033 DStringInit(&prefsStr
);
1035 /* mark currently shown files as visible */
1036 UpdateCurrentFileDisplayState();
1038 bool fOk
= Prefs_Serialize(&gFileHistoryRoot
, &prefsStr
);
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 */
1051 DStringFree(&prefsStr
);
1055 static bool WindowInfo_Dib_Init(WindowInfo
*win
) {
1056 assert(NULL
== win
->dibInfo
);
1057 win
->dibInfo
= (BITMAPINFO
*)malloc(sizeof(BITMAPINFO
) + 12);
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;
1071 static void WindowInfo_Dib_Deinit(WindowInfo
*win
) {
1072 free((void*)win
->dibInfo
);
1073 win
->dibInfo
= NULL
;
1076 static void WindowInfo_DoubleBuffer_Delete(WindowInfo
*win
) {
1077 if (win
->bmpDoubleBuffer
) {
1078 DeleteObject(win
->bmpDoubleBuffer
);
1079 win
->bmpDoubleBuffer
= NULL
;
1082 if (win
->hdcDoubleBuffer
) {
1083 DeleteDC(win
->hdcDoubleBuffer
);
1084 win
->hdcDoubleBuffer
= NULL
;
1086 win
->hdcToDraw
= NULL
;
1089 static bool WindowInfo_DoubleBuffer_New(WindowInfo
*win
)
1091 WindowInfo_DoubleBuffer_Delete(win
);
1093 win
->hdc
= GetDC(win
->hwndCanvas
);
1094 win
->hdcToDraw
= win
->hdc
;
1095 win
->GetCanvasSize();
1096 if (!gUseDoubleBuffer
|| (0 == win
->winDx()) || (0 == win
->winDy()))
1099 win
->hdcDoubleBuffer
= CreateCompatibleDC(win
->hdc
);
1100 if (!win
->hdcDoubleBuffer
)
1103 win
->bmpDoubleBuffer
= CreateCompatibleBitmap(win
->hdc
, win
->winDx(), win
->winDy());
1104 if (!win
->bmpDoubleBuffer
) {
1105 WindowInfo_DoubleBuffer_Delete(win
);
1108 /* TODO: do I need this ? */
1109 SelectObject(win
->hdcDoubleBuffer
, win
->bmpDoubleBuffer
);
1110 /* fill out everything with background color */
1112 r
.bottom
= win
->winDy();
1113 r
.right
= win
->winDx();
1114 FillRect(win
->hdcDoubleBuffer
, &r
, gBrushBg
);
1115 win
->hdcToDraw
= win
->hdcDoubleBuffer
;
1119 static void WindowInfo_DoubleBuffer_Show(WindowInfo
*win
, HDC hdc
)
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
);
1127 static void WindowInfo_Delete(WindowInfo
*win
)
1130 RenderQueue_RemoveForDisplayModel(win
->dm
);
1131 cancelRenderingForDisplayModel(win
->dm
);
1135 WindowInfo_Dib_Deinit(win
);
1136 WindowInfo_DoubleBuffer_Delete(win
);
1140 static WindowInfo
* WindowInfo_FindByHwnd(HWND hwnd
)
1142 WindowInfo
*win
= gWindowList
;
1144 if (hwnd
== win
->hwndFrame
)
1146 if (hwnd
== win
->hwndCanvas
)
1148 if (hwnd
== win
->hwndReBar
)
1155 static WindowInfo
*WindowInfo_New(HWND hwndFrame
) {
1156 WindowInfo
* win
= WindowInfo_FindByHwnd(hwndFrame
);
1159 win
= new WindowInfo();;
1163 if (!WindowInfo_Dib_Init(win
))
1166 win
->state
= WS_ABOUT
;
1167 win
->hwndFrame
= hwndFrame
;
1168 win
->mouseAction
= MA_IDLE
;
1171 WindowInfo_Delete(win
);
1175 static void WindowInfoList_Add(WindowInfo
*win
) {
1176 win
->next
= gWindowList
;
1180 static bool WindowInfoList_ExistsWithError(void) {
1181 WindowInfo
*cur
= gWindowList
;
1183 if (WS_ERROR_LOADING_PDF
== cur
->state
)
1190 static void WindowInfoList_Remove(WindowInfo
*to_remove
) {
1194 if (gWindowList
== to_remove
) {
1195 gWindowList
= to_remove
->next
;
1198 WindowInfo
* curr
= gWindowList
;
1200 if (to_remove
== curr
->next
) {
1201 curr
->next
= to_remove
->next
;
1208 static void WindowInfoList_DeleteAll(void) {
1209 WindowInfo
* curr
= gWindowList
;
1211 WindowInfo
* next
= curr
->next
;
1212 WindowInfo_Delete(curr
);
1218 static int WindowInfoList_Len(void) {
1220 WindowInfo
* curr
= gWindowList
;
1228 static void WindowInfo_RedrawAll(WindowInfo
*win
, bool update
=false) {
1229 InvalidateRect(win
->hwndCanvas
, NULL
, false);
1231 UpdateWindow(win
->hwndCanvas
);
1234 static bool FileCloseMenuEnabled(void) {
1235 WindowInfo
* win
= gWindowList
;
1237 if (win
->state
== WS_SHOWING_PDF
)
1244 static void ToolbarUpdateStateForWindow(WindowInfo
*win
) {
1245 LPARAM enable
= (LPARAM
)MAKELONG(1,0);
1246 LPARAM disable
= (LPARAM
)MAKELONG(0,0);
1248 for (int i
=0; i
< TOOLBAR_BUTTONS_COUNT
; i
++) {
1249 int cmdId
= gToolbarButtons
[i
].cmdId
;
1250 if (IDB_SEPARATOR
== cmdId
)
1252 LPARAM buttonState
= enable
;
1253 if (IDM_OPEN
!= cmdId
) {
1254 if (WS_SHOWING_PDF
!= win
->state
)
1255 buttonState
= disable
;
1257 SendMessage(win
->hwndToolbar
, TB_ENABLEBUTTON
, cmdId
, buttonState
);
1261 static void MenuUpdateShowToolbarStateForWindow(WindowInfo
*win
) {
1262 HMENU hmenu
= GetMenu(win
->hwndFrame
);
1264 CheckMenuItem(hmenu
, IDM_VIEW_SHOW_HIDE_TOOLBAR
, MF_BYCOMMAND
| MF_CHECKED
);
1266 CheckMenuItem(hmenu
, IDM_VIEW_SHOW_HIDE_TOOLBAR
, MF_BYCOMMAND
| MF_UNCHECKED
);
1269 static void MenuUpdateUseFitzStateForWindow(WindowInfo
*win
) {
1270 HMENU hmenu
= GetMenu(win
->hwndFrame
);
1272 CheckMenuItem(hmenu
, IDM_VIEW_USE_FITZ
, MF_BYCOMMAND
| MF_CHECKED
);
1274 CheckMenuItem(hmenu
, IDM_VIEW_USE_FITZ
, MF_BYCOMMAND
| MF_UNCHECKED
);
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
);
1286 CheckMenuItem(hmenu
, langMenuId
, MF_BYCOMMAND
| MF_UNCHECKED
);
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
};
1300 bool fileCloseEnabled
= FileCloseMenuEnabled();
1301 HMENU hmenu
= GetMenu(win
->hwndFrame
);
1302 if (fileCloseEnabled
)
1303 EnableMenuItem(hmenu
, IDM_CLOSE
, MF_BYCOMMAND
| MF_ENABLED
);
1305 EnableMenuItem(hmenu
, IDM_CLOSE
, MF_BYCOMMAND
| MF_GRAYED
);
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
);
1313 EnableMenuItem(hmenu
, IDM_PRINT
, MF_BYCOMMAND
| MF_GRAYED
);
1315 MenuUpdateShowToolbarStateForWindow(win
);
1316 MenuUpdateUseFitzStateForWindow(win
);
1317 MenuUpdateLanguage(win
);
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
);
1324 EnableMenuItem(hmenu
, menuId
, MF_BYCOMMAND
| MF_GRAYED
);
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
);
1331 ShowScrollBar(win
->hwndCanvas
, SB_BOTH
, FALSE
);
1332 win_set_text(win
->hwndFrame
, APP_NAME
);
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
;
1341 MenuUpdateStateForWindow(win
);
1342 ToolbarUpdateStateForWindow(win
);
1347 static WindowInfo
* WindowInfo_CreateEmpty(void) {
1348 HWND hwndFrame
, hwndCanvas
;
1352 hwndFrame
= CreateWindowEx(
1353 // WS_EX_TOOLWINDOW,
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
,
1360 CW_USEDEFAULT
, CW_USEDEFAULT
,
1361 DEF_WIN_DX
, DEF_WIN_DY
,
1365 hwndFrame
= CreateWindow(
1366 FRAME_CLASS_NAME
, windowTitle
,
1367 WS_OVERLAPPEDWINDOW
,
1368 CW_USEDEFAULT
, CW_USEDEFAULT
,
1369 DEF_WIN_DX
, DEF_WIN_DY
,
1372 HMENU m
= GetProgramMenu();
1373 SetMenu(hwndFrame
, m
);
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
,
1389 win
->hwndCanvas
= hwndCanvas
;
1390 CreateToolbar(win
, ghinst
);
1394 BOOL
GetDesktopWindowClientRect(RECT
*r
)
1396 HWND hwnd
= GetDesktopWindow();
1397 if (!hwnd
) return FALSE
;
1398 return GetClientRect(hwnd
, r
);
1401 void GetCanvasDxDyDiff(WindowInfo
*win
, int *dxOut
, int *dyOut
)
1404 GetWindowRect(win
->hwndCanvas
, &canvasRect
);
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);
1415 SizeI
GetMaxCanvasSize(WindowInfo
*win
)
1419 GetDesktopWindowClientRect(&r
);
1420 // substract the area of the window not used for canvas
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();
1429 assert(abd
.isVertical());
1430 assert(maxCanvasDy
>= abd
.dy());
1431 maxCanvasDy
-= abd
.dy();
1433 return SizeI(maxCanvasDx
, maxCanvasDy
);
1437 static void RecalcSelectionPosition (WindowInfo
*win
) {
1438 SelectionOnPage
* selOnPage
= win
->selectionOnPage
;
1440 PdfPageInfo
* pageInfo
;
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
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
);
1456 selOnPage
= selOnPage
->next
;
1460 static WindowInfo
* LoadPdf(const char *fileName
, bool ignoreHistorySizePos
= true, bool ignoreHistory
= false)
1463 if (!fileName
) return NULL
;
1465 FileHistoryList
* fileFromHistory
= NULL
;
1467 fileFromHistory
= FileHistoryList_Node_FindByFilePath(&gFileHistoryRoot
, fileName
);
1470 bool reuseExistingWindow
= false;
1471 if ((1 == WindowInfoList_Len()) && (WS_SHOWING_PDF
!= gWindowList
->state
)) {
1473 reuseExistingWindow
= true;
1475 win
= WindowInfo_CreateEmpty();
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
);
1488 #if 0 // not ready yet
1490 IntelligentWindowResize(win
);
1494 /* TODO: make sure it doesn't have a stupid position like
1495 outside of the screen etc. */
1497 if (totalDrawAreaSize
.dxI() > maxCanvasSize
.dx
)
1498 totalDrawAreaSize
.setDx(maxCanvasSize
.dx
);
1499 if (totalDrawAreaSize
.dyI() > maxCanvasSize
.dy
)
1500 totalDrawAreaSize
.setDy(maxCanvasSize
.dy
);
1502 WinResizeClientArea(win
->hwndCanvas
, totalDrawAreaSize
.dxI(), totalDrawAreaSize
.dyI());
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
1511 DisplayMode displayMode
= DEFAULT_DISPLAY_MODE
;
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
;
1525 win
->dm
= DisplayModelFitz_CreateFromFileName(fileName
,
1526 totalDrawAreaSize
, scrollbarYDx
, scrollbarXDy
, displayMode
, startPage
, win
);
1528 win
->dm
= DisplayModelSplash_CreateFromFileName(fileName
,
1529 totalDrawAreaSize
, scrollbarYDx
, scrollbarXDy
, displayMode
, startPage
, win
);
1532 double zoomVirtual
= DEFAULT_ZOOM
;
1533 int rotation
= DEFAULT_ROTATION
;
1535 if (!reuseExistingWindow
&& WindowInfoList_ExistsWithError()) {
1536 /* don't create more than one window with errors */
1537 WindowInfo_Delete(win
);
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();
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
);
1556 win
->dm
->setAppData((void*)win
);
1558 if (!fileFromHistory
) {
1559 AddFileToHistory(fileName
);
1560 RebuildProgramMenus();
1563 /* TODO: if fileFromHistory, set the state based on gFileHistoryList node for
1565 win
->state
= WS_SHOWING_PDF
;
1566 if (fileFromHistory
) {
1567 zoomVirtual
= fileFromHistory
->state
.zoomVirtual
;
1568 rotation
= fileFromHistory
->state
.rotation
;
1571 UINT menuId
= MenuIdFromVirtualZoom(zoomVirtual
);
1572 ZoomMenuItemCheck(GetMenu(win
->hwndFrame
), menuId
);
1574 win
->dm
->relayout(zoomVirtual
, rotation
);
1575 if (!win
->dm
->validPageNo(startPage
))
1577 /* TODO: need to calculate proper offsetY, currently giving large offsetY
1578 remembered for continuous mode breaks things (makes all pages invisible) */
1580 /* TODO: make sure offsetX isn't bogus */
1581 win
->dm
->goToPage(startPage
, offsetY
, offsetX
);
1583 /* only resize the window if it's a newly opened window */
1584 if (!reuseExistingWindow
&& !fileFromHistory
)
1585 WindowInfo_ResizeToPage(win
, startPage
);
1587 if (reuseExistingWindow
)
1588 WindowInfo_RedrawAll(win
);
1591 if (!reuseExistingWindow
)
1592 WindowInfoList_Add(win
);
1593 MenuToolbarUpdateStateForAllWindows();
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
);
1604 static HFONT
Win32_Font_GetSimple(HDC hdc
, char *fontName
, int fontSize
)
1610 font_dc
= (HFONT
)GetStockObject(SYSTEM_FONT
);
1611 if (!GetObject(font_dc
, sizeof(LOGFONT
), &lf
))
1614 lf
.lfHeight
= (LONG
)-fontSize
;
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
);
1631 static void Win32_Font_Delete(HFONT font
)
1636 void DisplayModel::pageChanged(void)
1638 WindowInfo
*win
= (WindowInfo
*)appData();
1643 if (!win
->dmSplash
->pdfDoc
)
1647 int currPageNo
= currentPageNo();
1648 int pageCount
= win
->dm
->pageCount();
1649 const char *baseName
= FilePath_GetBaseName(win
->dm
->fileName());
1651 win_set_text(win
->hwndFrame
, (TCHAR
*)baseName
); /* @note: TCHAR* cast */
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 */
1660 /* Call from non-UI thread to cause repainting of the display */
1661 static void triggerRepaintDisplayPotentiallyDelayed(WindowInfo
*win
, bool delayed
)
1666 PostMessage(win
->hwndCanvas
, WM_APP_REPAINT_DELAYED
, 0, 0);
1668 PostMessage(win
->hwndCanvas
, WM_APP_REPAINT_NOW
, 0, 0);
1671 static void triggerRepaintDisplayNow(WindowInfo
* win
)
1673 triggerRepaintDisplayPotentiallyDelayed(win
, false);
1676 void DisplayModel::repaintDisplay(bool delayed
)
1678 WindowInfo
* win
= (WindowInfo
*)appData();
1679 triggerRepaintDisplayPotentiallyDelayed(win
, delayed
);
1682 void DisplayModel::setScrollbarsState(void)
1684 WindowInfo
*win
= (WindowInfo
*)this->appData();
1688 SCROLLINFO si
= {0};
1689 si
.cbSize
= sizeof(si
);
1692 int canvasDx
= _canvasSize
.dxI();
1693 int canvasDy
= _canvasSize
.dyI();
1694 int drawAreaDx
= drawAreaSize
.dxI();
1695 int drawAreaDy
= drawAreaSize
.dyI();
1697 if (drawAreaDx
>= canvasDx
) {
1703 si
.nPos
= (int)areaOffset
.x
;
1705 si
.nMax
= canvasDx
-1;
1706 si
.nPage
= drawAreaDx
;
1708 SetScrollInfo(win
->hwndCanvas
, SB_HORZ
, &si
, TRUE
);
1710 if (drawAreaDy
>= canvasDy
) {
1716 si
.nPos
= (int)areaOffset
.y
;
1718 si
.nMax
= canvasDy
-1;
1719 si
.nPage
= drawAreaDy
;
1721 SetScrollInfo(win
->hwndCanvas
, SB_VERT
, &si
, TRUE
);
1724 static void WindowInfo_ResizeToWindow(WindowInfo
*win
)
1729 if (!win
->dm
) return;
1731 win
->dm
->changeTotalDrawAreaSize(win
->winSize());
1734 void WindowInfo_ResizeToPage(WindowInfo
*win
, int pageNo
)
1736 bool fullScreen
= false;
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
);
1751 /* TODO: fullscreen not yet supported */
1756 assert(win
->dm
->validPageNo(pageNo
));
1757 if (!win
->dm
->validPageNo(pageNo
))
1759 PdfPageInfo
*pageInfo
= win
->dm
->getPageInfo(pageNo
);
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;
1772 WinResizeClientArea(win
->hwndCanvas
, dx
, dy
);
1775 static void WindowInfo_ToggleZoom(WindowInfo
*win
)
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
);
1792 static BOOL
WindowInfo_PdfLoaded(WindowInfo
*win
)
1795 if (!win
) return FALSE
;
1796 if (!win
->dm
) return FALSE
;
1798 assert(win
->dmSplash
->pdfDoc
);
1799 assert(win
->dmSplash
->pdfDoc
->isOk());
1804 int WindowsVerMajor()
1806 DWORD version
= GetVersion();
1807 return (int)(version
& 0xFF);
1810 int WindowsVerMinor()
1812 DWORD version
= GetVersion();
1813 return (int)((version
& 0xFF00) >> 8);
1816 bool WindowsVer2000OrGreater()
1818 if (WindowsVerMajor() >= 5)
1823 static bool AlreadyRegisteredForPdfExtentions(void)
1825 bool registered
= false;
1827 char nameBuf
[sizeof(APP_NAME
)+8];
1828 DWORD cbNameBuf
= sizeof(nameBuf
);
1831 /* HKEY_CLASSES_ROOT\.pdf */
1832 if (ERROR_SUCCESS
!= RegOpenKeyEx(HKEY_CLASSES_ROOT
, TEXT(".pdf"), 0, KEY_QUERY_VALUE
, &key
)) /* @note: TEXT() cast */
1835 if (ERROR_SUCCESS
!= RegQueryValueEx(key
, NULL
, NULL
, &keyType
, (LPBYTE
)nameBuf
, &cbNameBuf
))
1838 if (REG_SZ
!= keyType
)
1841 if (cbNameBuf
!= sizeof(APP_NAME
))
1844 if (0 == memcmp(APP_NAME
, nameBuf
, sizeof(APP_NAME
)))
1852 static void AssociateExeWithPdfExtensions()
1855 HKEY key
= NULL
, kicon
= NULL
, kshell
= NULL
, kopen
= NULL
, kcmd
= NULL
;
1859 char * exePath
= ExePathGet();
1861 if (!exePath
) return;
1863 HKEY hkeyToUse
= HKEY_CURRENT_USER
;
1864 if (WindowsVer2000OrGreater())
1865 hkeyToUse
= HKEY_LOCAL_MACHINE
;
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
))
1873 if (RegSetValueEx(key
, TEXT(""), 0, REG_SZ
, (const BYTE
*)APP_NAME
, sizeof(APP_NAME
)))
1880 if (RegCreateKeyEx(hkeyToUse
,
1881 APP_NAME
, 0, NULL
, REG_OPTION_NON_VOLATILE
,
1882 KEY_WRITE
, NULL
, &key
, &disp
))
1885 if (RegSetValueEx(key
, TEXT(""), 0, REG_SZ
, (const BYTE
*)PDF_DOC_NAME
, sizeof(PDF_DOC_NAME
)))
1888 /* key\APP_NAME\DefaultIcon */
1889 if (RegCreateKeyEx(key
,
1890 TEXT("DefaultIcon"), 0, NULL
, REG_OPTION_NON_VOLATILE
,
1891 KEY_WRITE
, NULL
, &kicon
, &disp
))
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))
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
))
1908 if (RegCreateKeyEx(kshell
,
1909 TEXT("open"), 0, NULL
, REG_OPTION_NON_VOLATILE
,
1910 KEY_WRITE
, NULL
, &kopen
, &disp
))
1913 if (RegCreateKeyEx(kopen
,
1914 TEXT("command"), 0, NULL
, REG_OPTION_NON_VOLATILE
,
1915 KEY_WRITE
, NULL
, &kcmd
, &disp
))
1918 hr
= StringCchPrintfA(tmp
, dimof(tmp
), "\"%s\" \"%%1\"", exePath
);
1919 if (RegSetValueEx(kcmd
, TEXT(""), 0, REG_SZ
, (const BYTE
*)tmp
, strlen(tmp
)+1))
1922 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
| SHCNF_FLUSHNOWAIT
, 0, 0);
1930 RegCloseKey(kshell
);
1936 static void RegisterForPdfExtentions(HWND hwnd
)
1938 if (AlreadyRegisteredForPdfExtentions())
1941 if (IsRunningInPortableMode())
1944 /* Ask user for permission, unless he previously said he doesn't want to
1946 if (!gPdfAssociateDontAskAgain
) {
1947 int result
= Dialog_PdfAssociate(hwnd
, &gPdfAssociateDontAskAgain
);
1948 if (DIALOG_NO_PRESSED
== result
) {
1949 gPdfAssociateShouldAssociate
= FALSE
;
1951 assert(DIALOG_OK_PRESSED
== result
);
1952 gPdfAssociateShouldAssociate
= TRUE
;
1956 if (gPdfAssociateShouldAssociate
)
1957 AssociateExeWithPdfExtensions();
1960 static void OnDropFiles(WindowInfo
*win
, HDROP hDrop
)
1963 char filename
[MAX_PATH
];
1964 const int files_count
= DragQueryFile(hDrop
, DRAGQUERY_NUMFILES
, 0, 0);
1966 for (i
= 0; i
< files_count
; i
++)
1968 DragQueryFile(hDrop
, i
, (TCHAR
*)filename
, MAX_PATH
); /* @note: TCHAR* cast */
1973 if (files_count
> 0)
1974 WindowInfo_RedrawAll(win
);
1977 static void DrawLineSimple(HDC hdc
, int sx
, int sy
, int ex
, int ey
)
1979 MoveToEx(hdc
, sx
, sy
, NULL
);
1980 LineTo(hdc
, ex
, ey
);
1984 /* Draw caption area for a given window 'win' in the classic AmigaOS style */
1985 static void AmigaCaptionDraw(WindowInfo
*win
)
1990 assert(VS_AMIGA
== gVisualStyle
);
1992 prevPen
= SelectObject(hdc
, ghpenWhite
);
1995 DrawLineSimple(hdc
, 0, 0, win
->winDx
, 0);
1996 DrawLineSimple(hdc
, 0, 1, win
->winDx
, 1);
1999 DrawLineSimple(hdc
, 0, 4, win
->winDx
, 4);
2000 DrawLineSimple(hdc
, 0, 5, win
->winDx
, 5);
2003 DrawLineSimple(hdc
, 0, 8, win
->winDx
, 8);
2004 DrawLineSimple(hdc
, 0, 9, win
->winDx
, 9);
2007 DrawLineSimple(hdc
, 0, 12, win
->winDx
, 12);
2008 DrawLineSimple(hdc
, 0, 13, win
->winDx
, 13);
2011 DrawLineSimple(hdc
, 0, 16, win
->winDx
, 16);
2012 DrawLineSimple(hdc
, 0, 17, win
->winDx
, 17);
2013 DrawLineSimple(hdc
, 0, 18, win
->winDx
, 18);
2015 SelectObject(hdc
, ghpenBlue
);
2018 DrawLineSimple(hdc
, 0, 2, win
->winDx
, 2);
2019 DrawLineSimple(hdc
, 0, 3, win
->winDx
, 3);
2022 DrawLineSimple(hdc
, 0, 6, win
->winDx
, 6);
2023 DrawLineSimple(hdc
, 0, 7, win
->winDx
, 7);
2026 DrawLineSimple(hdc
, 0, 10, win
->winDx
, 10);
2027 DrawLineSimple(hdc
, 0, 11, win
->winDx
, 11);
2030 DrawLineSimple(hdc
, 0, 14, win
->winDx
, 14);
2031 DrawLineSimple(hdc
, 0, 15, win
->winDx
, 15);
2033 SelectObject(hdc
, prevPen
);
2037 static void WinResizeIfNeeded(WindowInfo
*win
, bool resizeWindow
=true)
2040 GetClientRect(win
->hwndCanvas
, &rc
);
2041 int win_dx
= rect_dx(&rc
);
2042 int win_dy
= rect_dy(&rc
);
2044 if (win
->hdcToDraw
&&
2045 (win_dx
== win
->winDx()) &&
2046 (win_dy
== win
->winDy()))
2051 WindowInfo_DoubleBuffer_New(win
);
2053 WindowInfo_ResizeToWindow(win
);
2056 static void PostBenchNextAction(HWND hwnd
)
2058 PostMessage(hwnd
, MSG_BENCH_NEXT_ACTION
, 0, 0);
2061 static void OnBenchNextAction(WindowInfo
*win
)
2066 if (win
->dm
->goToNextPage(0))
2067 PostBenchNextAction(win
->hwndFrame
);
2070 static void DrawCenteredText(HDC hdc
, RECT
*r
, char *txt
)
2072 SetBkMode(hdc
, TRANSPARENT
);
2073 DrawText(hdc
, (TCHAR
*)txt
, strlen(txt
), r
, DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
); /* @note: TCHAR* cast */
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
);
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;
2097 ZeroMemory(&bmi
, sizeof(BITMAPINFO
));
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;
2107 hbitmap
= CreateDIBSection (rectDC
, &bmi
, DIB_RGB_COLORS
, &pvBits
, NULL
, 0x0);
2108 SelectObject(rectDC
, hbitmap
);
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
;
2116 ((UINT32
*)pvBits
)[x
+ y
* rect
->dx
] = selectionColorYellow
;
2119 bf
.BlendOp
= AC_SRC_OVER
;
2121 bf
.SourceConstantAlpha
= 0x5f;
2122 bf
.AlphaFormat
= AC_SRC_ALPHA
;
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
);
2132 static void PaintSelection (WindowInfo
*win
, HDC hdc
) {
2133 if (win
->mouseAction
== MA_SELECTING
) {
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
);
2144 if (selRect
.dx
!= 0 && selRect
.dy
!= 0)
2145 PaintTransparentRectangle (win
, hdc
, &selRect
);
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
;
2159 static void WindowInfo_Paint(WindowInfo
*win
, HDC hdc
, PAINTSTRUCT
*ps
)
2162 RenderedBitmap
* renderedBmp
= NULL
;
2166 DisplayModel
* dm
= win
->dm
;
2169 #if 0 // TODO: write the equivalent dm->isOk() ?
2171 if (!dm
->pdfDoc
) return;
2174 assert(win
->hdcToDraw
);
2175 hdc
= win
->hdcToDraw
;
2177 FillRect(hdc
, &(ps
->rcPaint
), gBrushBg
);
2179 DBG_OUT("WindowInfo_Paint() ");
2180 for (int pageNo
= 1; pageNo
<= dm
->pageCount(); ++pageNo
) {
2181 PdfPageInfo
*pageInfo
= dm
->getPageInfo(pageNo
);
2182 if (!pageInfo
->visible
)
2184 assert(pageInfo
->shown
);
2185 if (!pageInfo
->shown
)
2188 //BitmapCacheEntry *entry = BitmapCache_Find(dm, pageNo, dm->zoomReal(), dm->rotation());
2189 BitmapCacheEntry
*entry
= BitmapCache_Find(dm
, pageNo
);
2191 if ((dm
->rotation() != entry
->rotation
) || (dm
->zoomReal() != entry
->zoomLevel
))
2194 renderedBmp
= entry
->bitmap
;
2198 DBG_OUT(" missing bitmap on visible page %d\n", pageNo
);
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
;
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
;
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
);
2219 SelectObject(hdc
, origFont
);
2220 Win32_Font_Delete(fontRightTxt
);
2224 if (BITMAP_CANNOT_RENDER
== renderedBmp
) {
2225 bounds
.left
= xDest
;
2227 bounds
.right
= xDest
+ bmpDx
;
2228 bounds
.bottom
= yDest
+ bmpDy
;
2229 FillRect(hdc
, &bounds
, gBrushWhite
);
2230 DrawCenteredText(hdc
, &bounds
, "Couldn't render the page");
2234 DBG_OUT("page %d ", pageNo
);
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
);
2244 HDC bmpDC
= CreateCompatibleDC(hdc
);
2246 SelectObject(bmpDC
, hbmp
);
2248 if ((currPageDx
!= renderedBmpDx
) || (currPageDy
!= renderedBmpDy
))
2249 StretchBlt(hdc
, xDest
, yDest
, bmpDx
, bmpDy
, bmpDC
, xSrc
, ySrc
, renderedBmpDx
, renderedBmpDy
, SRCCOPY
);
2252 BitBlt(hdc
, xDest
, yDest
, bmpDx
, bmpDy
, bmpDC
, xSrc
, ySrc
, SRCCOPY
);
2258 if (win
->showSelection
)
2259 PaintSelection(win
, hdc
);
2262 if (!gDebugShowLinks
)
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();
2272 for (int linkNo
= 0; linkNo
< dm
->linkCount(); ++linkNo
) {
2273 PdfLink
*pdfLink
= dm
->link(linkNo
);
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
;
2281 if (RectI_Intersect(&rectLink
, &drawAreaRect
, &intersect
)) {
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
));
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
2308 #define ABOUT_BORDER_COL COL_BLACK
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
2321 #define ABOUT_BG_COLOR RGB(255,242,0)
2322 #define ABOUT_RECT_BG_COLOR RGB(247,148,29)
2324 #define ABOUT_TXT_DY 6
2326 typedef struct AboutLayoutInfoEl
{
2327 /* static data, must be provided */
2328 const char * leftTxt
;
2329 const char * rightTxt
;
2332 /* data calculated by the layout */
2342 } AboutLayoutInfoEl
;
2344 AboutLayoutInfoEl gAboutLayoutInfo
[] = {
2345 { "design", "Krzysztof Kowalczyk", "http://blog.kowalczyk.info",
2346 0, 0, 0, 0, 0, 0, 0, 0 },
2348 { "programming", "Krzysztof Kowalczyk", "http://blog.kowalczyk.info",
2349 0, 0, 0, 0, 0, 0, 0, 0 },
2351 { "pdf rendering 1", "poppler + xpdf", "http://poppler.freedesktop.org/",
2352 0, 0, 0, 0, 0, 0, 0, 0 },
2354 { "pdf rendering 2", "MuPDF", "http://ccxvii.net/apparition/",
2355 0, 0, 0, 0, 0, 0, 0, 0 },
2357 { "license", "GPL v2", "http://www.gnu.org/copyleft/gpl.html",
2358 0, 0, 0, 0, 0, 0, 0, 0 },
2360 { "website", "http://blog.kowalczyk.info/software/sumatra", "http://blog.kowalczyk.info/software/sumatrapdf",
2361 0, 0, 0, 0, 0, 0, 0, 0 },
2363 { "forums", "http://blog.kowalczyk.info/forum_sumatra", "http://blog.kowalczyk.info/forum_sumatra",
2364 0, 0, 0, 0, 0, 0, 0, 0 },
2366 { "program icon", "Goce Mitevski", "http://monsteer.deviantart.com",
2367 0, 0, 0, 0, 0, 0, 0, 0 },
2369 { "toolbar icons", "Mark James", "http://www.famfamfam.com/lab/icons/silk/",
2370 0, 0, 0, 0, 0, 0, 0, 0 },
2373 0, 0, 0, 0, 0, 0, 0, 0 }
2376 static const char *AboutGetLink(WindowInfo
*win
, int x
, int y
)
2378 for (int i
= 0; gAboutLayoutInfo
[i
].leftTxt
; i
++) {
2379 if ((x
< gAboutLayoutInfo
[i
].rightTxtPosX
) ||
2380 (x
> gAboutLayoutInfo
[i
].rightTxtPosX
+ gAboutLayoutInfo
[i
].rightTxtDx
))
2382 if ((y
< gAboutLayoutInfo
[i
].rightTxtPosY
) ||
2383 (y
> gAboutLayoutInfo
[i
].rightTxtPosY
+ gAboutLayoutInfo
[i
].rightTxtDy
))
2385 return gAboutLayoutInfo
[i
].url
;
2390 static void DrawAbout(HWND hwnd
, HDC hdc
, PAINTSTRUCT
*ps
)
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
;
2409 HBRUSH brushBg
= CreateSolidBrush(ABOUT_BG_COLOR
);
2410 HBRUSH brushRectBg
= CreateSolidBrush(ABOUT_RECT_BG_COLOR
);
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
);
2417 GetClientRect(hwnd
, &rc
);
2419 int areaDx
= rect_dx(&rc
);
2420 int areaDy
= rect_dy(&rc
);
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
);
2427 HFONT origFont
= (HFONT
)SelectObject(hdc
, fontSumatraTxt
); /* Just to remember the orig font */
2429 SetBkMode(hdc
, TRANSPARENT
);
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
;
2437 boxDy
= sumatraPdfTxtDy
+ ABOUT_BOX_MARGIN_DY
* 2;
2439 GetTextExtentPoint32(hdc
, (TCHAR
*)txt
, strlen(txt
), &txtSize
); /* @note: TCHAR* cast */
2440 betaTxtDx
= txtSize
.cx
;
2441 betaTxtDy
= txtSize
.cy
;
2443 (HFONT
)SelectObject(hdc
, fontLeftTxt
);
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
;
2452 leftDy
= gAboutLayoutInfo
[i
].leftTxtDy
;
2454 assert(leftDy
== gAboutLayoutInfo
[i
].leftTxtDy
);
2455 if (leftLargestDx
< gAboutLayoutInfo
[i
].leftTxtDx
)
2456 leftLargestDx
= gAboutLayoutInfo
[i
].leftTxtDx
;
2459 (HFONT
)SelectObject(hdc
, fontRightTxt
);
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
;
2468 rightDy
= gAboutLayoutInfo
[i
].rightTxtDy
;
2470 assert(rightDy
== gAboutLayoutInfo
[i
].rightTxtDy
);
2471 if (rightLargestDx
< gAboutLayoutInfo
[i
].rightTxtDx
)
2472 rightLargestDx
= gAboutLayoutInfo
[i
].rightTxtDx
;
2475 fontDyDiff
= (rightDy
- leftDy
) / 2;
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
;
2484 totalDy
+= ABOUT_LINE_OUTER_SIZE
;
2485 totalDy
+= (dimof(gAboutLayoutInfo
)-1) * (rightDy
+ ABOUT_TXT_DY
);
2486 totalDy
+= ABOUT_LINE_OUTER_SIZE
+ 4;
2488 offX
= (areaDx
- totalDx
) / 2;
2489 offY
= (areaDy
- totalDy
) / 2;
2493 rcTmp
.right
= totalDx
+ offX
;
2494 rcTmp
.bottom
= totalDy
+ offY
;
2496 FillRect(hdc
, &rc
, brushBg
);
2498 SelectObject(hdc
, brushBg
);
2499 SelectObject(hdc
, penBorder
);
2501 Rectangle(hdc
, offX
, offY
+ ABOUT_LINE_OUTER_SIZE
, offX
+ totalDx
, offY
+ boxDy
+ ABOUT_LINE_OUTER_SIZE
);
2503 SetTextColor(hdc
, ABOUT_BORDER_COL
);
2504 (HFONT
)SelectObject(hdc
, fontSumatraTxt
);
2505 x
= offX
+ (totalDx
- sumatraPdfTxtDx
) / 2;
2506 y
= offY
+ (boxDy
- sumatraPdfTxtDy
) / 2;
2508 TextOut(hdc
, x
, y
, (TCHAR
*)txt
, strlen(txt
)); /* @note: TCHAR* cast */
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;
2516 TextOut(hdc
, x
, y
, (TCHAR
*)txt
, strlen(txt
)); /* @note: TCHAR* cast */
2517 SetTextColor(hdc
, ABOUT_BORDER_COL
);
2520 Rectangle(hdc
, offX
, offY
, offX
+ totalDx
, offY
+ totalDy
- boxDy
);
2522 linePosX
= ABOUT_LINE_OUTER_SIZE
+ ABOUT_MARGIN_DX
+ leftLargestDx
+ ABOUT_LEFT_RIGHT_SPACE_DX
;
2524 lineDy
= (dimof(gAboutLayoutInfo
)-1) * (rightDy
+ ABOUT_TXT_DY
);
2526 /* render text on the left*/
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
;
2539 /* render text on the rigth */
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
;
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
;
2552 SelectObject(hdc
, penDivideLine
);
2553 MoveToEx(hdc
, linePosX
+ offX
, linePosY
+ offY
, NULL
);
2554 LineTo(hdc
, linePosX
+ offX
, linePosY
+ lineDy
+ offY
);
2557 SelectObject(hdc
, origFont
);
2559 Win32_Font_Delete(fontSumatraTxt
);
2560 Win32_Font_Delete(fontBetaTxt
);
2561 Win32_Font_Delete(fontLeftTxt
);
2562 Win32_Font_Delete(fontRightTxt
);
2564 DeleteObject(brushBg
);
2565 DeleteObject(brushRectBg
);
2566 DeleteObject(penBorder
);
2567 DeleteObject(penDivideLine
);
2568 DeleteObject(penRectBorder
);
2571 static void WinMoveDocBy(WindowInfo
*win
, int dx
, int dy
)
2575 assert (WS_SHOWING_PDF
== win
->state
);
2576 if (WS_SHOWING_PDF
!= win
->state
) return;
2578 if (!win
->dm
) return;
2579 assert(!win
->linkOnLastButtonDown
);
2580 if (win
->linkOnLastButtonDown
) return;
2582 win
->dm
->scrollXBy(dx
);
2584 win
->dm
->scrollYBy(dy
, FALSE
);
2587 static void CopySelectionTextToClipboard(WindowInfo
*win
)
2589 SelectionOnPage
* selOnPage
;
2594 if (!win
->selectionOnPage
) return;
2597 unsigned short *ucsbuf
;
2598 int ucsbuflen
= 4096;
2600 if (!OpenClipboard(NULL
)) return;
2604 handle
= GlobalAlloc(GMEM_MOVEABLE
, ucsbuflen
* sizeof(unsigned short));
2609 ucsbuf
= (unsigned short *) GlobalLock(handle
);
2611 selOnPage
= win
->selectionOnPage
;
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)
2620 selOnPage
= selOnPage
->next
;
2624 GlobalUnlock(handle
);
2626 SetClipboardData(CF_UNICODETEXT
, handle
);
2630 static void DeleteOldSelectionInfo (WindowInfo
*win
) {
2631 SelectionOnPage
*selOnPage
= win
->selectionOnPage
;
2632 while (selOnPage
!= NULL
) {
2633 SelectionOnPage
*tmp
= selOnPage
->next
;
2637 win
->selectionOnPage
= NULL
;
2640 static void ConvertSelectionRectToSelectionOnPage (WindowInfo
*win
) {
2641 RectI pageOnScreen
, intersect
;
2643 for (int pageNo
= win
->dm
->pageCount(); pageNo
>= 1; --pageNo
) {
2644 PdfPageInfo
*pageInfo
= win
->dm
->getPageInfo(pageNo
);
2645 if (!pageInfo
->visible
)
2647 assert(pageInfo
->shown
);
2648 if (!pageInfo
->shown
)
2651 pageOnScreen
.x
= pageInfo
->screenX
;
2652 pageOnScreen
.y
= pageInfo
->screenY
;
2653 pageOnScreen
.dx
= pageInfo
->bitmapDx
;
2654 pageOnScreen
.dy
= pageInfo
->bitmapDy
;
2656 if (!RectI_Intersect(&win
->selectionRect
, &pageOnScreen
, &intersect
))
2659 /* selection intersects with a page <pageNo> on the screen */
2660 SelectionOnPage
*selOnPage
= (SelectionOnPage
*)malloc(sizeof(SelectionOnPage
));
2661 RectD_FromRectI(&selOnPage
->selectionPage
, &intersect
);
2663 win
->dm
->rectCvtScreenToUser (&selOnPage
->pageNo
, &selOnPage
->selectionPage
);
2665 assert (pageNo
== selOnPage
->pageNo
);
2667 selOnPage
->next
= win
->selectionOnPage
;
2668 win
->selectionOnPage
= selOnPage
;
2672 static void OnMouseLeftButtonDown(WindowInfo
*win
, int x
, int y
)
2676 if (WS_SHOWING_PDF
== win
->state
&& win
->mouseAction
== MA_IDLE
) {
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
);
2689 } else if (WS_ABOUT
== win
->state
) {
2690 win
->url
= AboutGetLink(win
, x
, y
);
2694 static void OnMouseLeftButtonUp(WindowInfo
*win
, int x
, int y
)
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 */
2713 if (WS_SHOWING_PDF
!= win
->state
)
2717 if (!win
->dm
) return;
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
);
2734 if (!win
->linkOnLastButtonDown
)
2737 link
= win
->dm
->linkAtPosition(x
, y
);
2738 if (link
&& (link
== win
->linkOnLastButtonDown
))
2739 win
->dm
->handleLink(link
);
2740 win
->linkOnLastButtonDown
= NULL
;
2743 static void OnMouseMove(WindowInfo
*win
, int x
, int y
, WPARAM flags
)
2752 if (WS_SHOWING_PDF
== win
->state
) {
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
);
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
;
2771 link
= win
->dm
->linkAtPosition(x
, y
);
2773 SetCursor(gCursorHand
);
2775 SetCursor(gCursorArrow
);
2777 } else if (WS_ABOUT
== win
->state
) {
2778 url
= AboutGetLink(win
, x
, y
);
2780 SetCursor(gCursorHand
);
2782 SetCursor(gCursorArrow
);
2785 // TODO: be more efficient, this only needs to be set once (I think)
2786 SetCursor(gCursorArrow
);
2790 static void OnMouseRightButtonDown(WindowInfo
*win
, int x
, int y
)
2792 //DBG_OUT("Right button clicked on %d %d\n", x, y);
2796 if (WS_SHOWING_PDF
== win
->state
&& win
->mouseAction
== MA_IDLE
) {
2797 win
->documentBlocked
= true;
2798 DeleteOldSelectionInfo (win
);
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
;
2807 triggerRepaintDisplayNow(win
);
2811 static void OnMouseRightButtonUp(WindowInfo
*win
, int x
, int y
)
2816 if (WS_SHOWING_PDF
== win
->state
&& win
->mouseAction
== MA_SELECTING
) {
2818 if (!win
->dm
) return;
2819 win
->documentBlocked
= false;
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
);
2826 win
->mouseAction
= MA_IDLE
;
2827 if (win
->selectionRect
.dx
== 0 || win
->selectionRect
.dy
== 0) {
2828 win
->showSelection
= false;
2830 ConvertSelectionRectToSelectionOnPage (win
);
2831 CopySelectionTextToClipboard (win
);
2833 triggerRepaintDisplayNow(win
);
2837 #define ABOUT_ANIM_TIMER_ID 15
2839 static void AnimState_AnimStop(AnimState
*state
)
2841 KillTimer(state
->hwnd
, ABOUT_ANIM_TIMER_ID
);
2844 static void AnimState_NextFrame(AnimState
*state
)
2847 InvalidateRect(state
->hwnd
, NULL
, FALSE
);
2848 UpdateWindow(state
->hwnd
);
2851 static void AnimState_AnimStart(AnimState
*state
, HWND hwnd
, UINT freqInMs
)
2853 assert(IsWindow(hwnd
));
2854 AnimState_AnimStop(state
);
2857 SetTimer(state
->hwnd
, ABOUT_ANIM_TIMER_ID
, freqInMs
, NULL
);
2858 AnimState_NextFrame(state
);
2861 #define ANIM_FONT_NAME "Georgia"
2862 #define ANIM_FONT_SIZE_START 20
2863 #define SCROLL_SPEED 3
2865 static void DrawAnim2(WindowInfo
*win
, HDC hdc
, PAINTSTRUCT
*ps
)
2867 AnimState
* state
= &(win
->animState
);
2870 HFONT fontArial24
= NULL
;
2871 HFONT origFont
= NULL
;
2873 static int curTxtPosX
= -1;
2874 static int curTxtPosY
= -1;
2875 static int curDir
= SCROLL_SPEED
;
2877 GetClientRect(win
->hwndCanvas
, &rc
);
2881 if (-1 == curTxtPosX
)
2883 if (-1 == curTxtPosY
)
2886 int areaDx
= rect_dx(&rc
);
2887 int areaDy
= rect_dy(&rc
);
2890 if (state
->frame
% 24 <= 12) {
2891 curFontSize
= ANIM_FONT_SIZE_START
+ (state
->frame
% 24);
2893 curFontSize
= ANIM_FONT_SIZE_START
+ 12 - (24 - (state
->frame
% 24));
2896 curFontSize
= ANIM_FONT_SIZE_START
;
2899 curTxtPosY
+= curDir
;
2900 if (curTxtPosY
< 20)
2901 curDir
= SCROLL_SPEED
;
2902 else if (curTxtPosY
> areaDy
- 40)
2903 curDir
= -SCROLL_SPEED
;
2905 fontArial24
= Win32_Font_GetSimple(hdc
, ANIM_FONT_NAME
, curFontSize
);
2906 assert(fontArial24
);
2908 origFont
= (HFONT
)SelectObject(hdc
, fontArial24
);
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)
2921 SelectObject(hdc
, origFont
);
2922 Win32_Font_Delete(fontArial24
);
2925 static void WindowInfo_DoubleBuffer_Resize_IfNeeded(WindowInfo
*win
)
2927 WinResizeIfNeeded(win
, false);
2930 static void OnPaintAbout(HWND hwnd
)
2933 HDC hdc
= BeginPaint(hwnd
, &ps
);
2934 SetBkMode(hdc
, TRANSPARENT
);
2935 DrawAbout(hwnd
, hdc
, &ps
);
2936 EndPaint(hwnd
, &ps
);
2939 static void OnPaint(WindowInfo
*win
)
2942 HDC hdc
= BeginPaint(win
->hwndCanvas
, &ps
);
2944 SetBkMode(hdc
, TRANSPARENT
);
2946 GetClientRect(win
->hwndCanvas
, &rc
);
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
) ;
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
);
2967 if (VS_AMIGA
== gVisualStyle
)
2968 AmigaCaptionDraw(win
);
2970 WindowInfo_DoubleBuffer_Show(win
, hdc
);
2974 EndPaint(win
->hwndCanvas
, &ps
);
2977 static void OnMenuExit(void)
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"
2987 static void CloseWindow(WindowInfo
*win
, bool quitIfLast
)
2992 bool lastWindow
= false;
2996 if (1 == WindowInfoList_Len())
3002 UpdateCurrentFileDisplayStateForWin(win
);
3005 win
->state
= WS_ABOUT
;
3007 if (lastWindow
&& !quitIfLast
) {
3008 /* last window - don't delete it */
3011 WindowInfo_RedrawAll(win
);
3013 HWND hwndToDestroy
= win
->hwndFrame
;
3014 WindowInfoList_Remove(win
);
3015 WindowInfo_Delete(win
);
3016 DragAcceptFiles(hwndToDestroy
, FALSE
);
3017 DestroyWindow(hwndToDestroy
);
3020 if (lastWindow
&& quitIfLast
) {
3021 assert(0 == WindowInfoList_Len());
3025 MenuToolbarUpdateStateForAllWindows();
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.
3032 static void OnMenuZoom(WindowInfo
*win
, UINT menuId
)
3037 double zoom
= ZoomMenuItemToZoom(menuId
);
3038 win
->dm
->zoomTo(zoom
);
3039 ZoomMenuItemCheck(GetMenu(win
->hwndFrame
), menuId
);
3042 static bool CheckPrinterStretchDibSupport(HWND hwndForMsgBox
, HDC hdc
)
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
)
3052 MessageBox(hwndForMsgBox
, TEXT("This printer doesn't support StretchDIBits function"), TEXT("Printing problem."), MB_ICONEXCLAMATION
| MB_OK
);
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
) {
3060 assert(toPage
>= fromPage
);
3064 PdfEngine
*pdfEngine
= dm
->pdfEngine();
3066 di
.cbSize
= sizeof (DOCINFO
);
3067 di
.lpszDocName
= (LPCTSTR
)pdfEngine
->fileName();
3069 if (StartDoc(hdc
, &di
) <= 0)
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
);
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
);
3082 DBG_OUT(" printing: drawing bitmap for page %d\n", pageNo
);
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
);
3090 goto Error
; /* most likely ran out of memory */
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
);
3097 int pageHeight
= GetDeviceCaps(hdc
, PHYSICALHEIGHT
);
3098 int pageWidth
= GetDeviceCaps(hdc
, PHYSICALWIDTH
);
3100 int topMargin
= GetDeviceCaps(hdc
, PHYSICALOFFSETY
);
3101 int leftMargin
= GetDeviceCaps(hdc
, PHYSICALOFFSETX
);
3102 if (DMORIENT_LANDSCAPE
== devMode
->dmOrientation
)
3103 swap_int(&topMargin
, &leftMargin
);
3105 bmp
->stretchDIBits(hdc
, -leftMargin
, -topMargin
, pageWidth
, pageHeight
);
3107 if (EndPage(hdc
) <= 0) {
3117 /* Show Print Dialog box to allow user to select the printer
3118 and the pages to print.
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.
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)
3128 - Lexmark Z515 inkjet, which should cover most bases.
3130 static void OnMenuPrint(WindowInfo
*win
)
3137 DisplayModel
*dm
= win
->dm
;
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
;
3152 pd
.hDevNames
= NULL
;
3153 pd
.Flags
= PD_USEDEVMODECOPIESANDCOLLATE
| PD_RETURNDC
;
3155 /* by default print all pages */
3157 pd
.nToPage
= dm
->pageCount();
3159 pd
.nMaxPage
= dm
->pageCount();
3161 BOOL pressedOk
= PrintDlg(&pd
);
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 */
3174 if (CheckPrinterStretchDibSupport(win
->hwndFrame
, pd
.hDC
))
3175 PrintToDevice(dm
, pd
.hDC
, (LPDEVMODE
)pd
.hDevMode
, pd
.nFromPage
, pd
.nToPage
);
3178 if (pd
.hDevNames
!= NULL
) GlobalFree(pd
.hDevNames
);
3179 if (pd
.hDevMode
!= NULL
) GlobalFree(pd
.hDevMode
);
3182 static void OnMenuSaveAs(WindowInfo
*win
)
3184 OPENFILENAME ofn
= {0};
3185 char dstFileName
[MAX_PATH
] = {0};
3186 const char* srcFileName
= NULL
;
3191 if (!win
->dm
) return;
3193 srcFileName
= win
->dm
->fileName();
3194 assert(srcFileName
);
3195 if (!srcFileName
) return;
3197 ofn
.lStructSize
= sizeof(ofn
);
3198 ofn
.hwndOwner
= win
->hwndFrame
;
3199 ofn
.lpstrFile
= (TCHAR
*)dstFileName
;
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
;
3212 if (FALSE
== GetSaveFileName(&ofn
))
3215 char* realDstFileName
= dstFileName
;
3216 if (!str_endswithi(dstFileName
, ".pdf")) {
3217 realDstFileName
= str_cat(dstFileName
, ".pdf");
3219 BOOL cancelled
= FALSE
;
3220 BOOL ok
= CopyFileEx((TCHAR
*)srcFileName
, (TCHAR
*)realDstFileName
, NULL
, NULL
, &cancelled
, COPY_FILE_FAIL_IF_EXISTS
); /* @note: TCHAR* cast */
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
);
3227 if (realDstFileName
!= dstFileName
)
3228 free(realDstFileName
);
3231 static void OnMenuOpen(WindowInfo
*win
)
3233 OPENFILENAME ofn
= {0};
3234 char fileName
[PATH_MAX
];
3236 ofn
.lStructSize
= sizeof(ofn
);
3237 ofn
.hwndOwner
= win
->hwndFrame
;
3238 ofn
.lpstrFile
= (TCHAR
*)fileName
; /* @note: TCHAR* cast */
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
;
3251 if (FALSE
== GetOpenFileName(&ofn
))
3254 win
= LoadPdf(fileName
);
3259 static void RotateLeft(WindowInfo
*win
)
3263 if (!WindowInfo_PdfLoaded(win
))
3265 win
->dm
->rotateBy(-90);
3268 static void RotateRight(WindowInfo
*win
)
3272 if (!WindowInfo_PdfLoaded(win
))
3274 win
->dm
->rotateBy(90);
3277 static void OnVScroll(WindowInfo
*win
, WPARAM wParam
)
3279 if (win
->documentBlocked
) return;
3280 SCROLLINFO si
= {0};
3283 si
.cbSize
= sizeof (si
);
3285 GetScrollInfo(win
->hwndCanvas
, SB_VERT
, &si
);
3289 switch (LOWORD(wParam
))
3308 si
.nPos
-= si
.nPage
;
3312 si
.nPos
+= si
.nPage
;
3316 si
.nPos
= si
.nTrackPos
;
3323 // Set the position and then retrieve it. Due to adjustments
3324 // by Windows it may not be the same as the value set.
3326 SetScrollInfo(win
->hwndCanvas
, SB_VERT
, &si
, TRUE
);
3327 GetScrollInfo(win
->hwndCanvas
, SB_VERT
, &si
);
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
);
3334 static void OnHScroll(WindowInfo
*win
, WPARAM wParam
)
3336 if (win
->documentBlocked
) return;
3337 SCROLLINFO si
= {0};
3340 si
.cbSize
= sizeof (si
);
3342 GetScrollInfo(win
->hwndCanvas
, SB_HORZ
, &si
);
3346 switch (LOWORD(wParam
))
3365 si
.nPos
-= si
.nPage
;
3369 si
.nPos
+= si
.nPage
;
3373 si
.nPos
= si
.nTrackPos
;
3380 // Set the position and then retrieve it. Due to adjustments
3381 // by Windows it may not be the same as the value set.
3383 SetScrollInfo(win
->hwndCanvas
, SB_HORZ
, &si
, TRUE
);
3384 GetScrollInfo(win
->hwndCanvas
, SB_HORZ
, &si
);
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
);
3391 static void ViewWithAcrobat(WindowInfo
*win
)
3396 static void OnMenuViewSinglePage(WindowInfo
*win
)
3400 if (!WindowInfo_PdfLoaded(win
))
3402 SwitchToDisplayMode(win
, DM_SINGLE_PAGE
);
3405 static void OnMenuViewFacing(WindowInfo
*win
)
3409 if (!WindowInfo_PdfLoaded(win
))
3411 SwitchToDisplayMode(win
, DM_FACING
);
3414 static void OneMenuMakeDefaultReader(void)
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
);
3422 static void OnSize(WindowInfo
*win
, int dx
, int dy
)
3426 SetWindowPos(win
->hwndReBar
, NULL
, 0, 0, dx
, rebBarDy
, SWP_NOZORDER
);
3427 rebBarDy
= gReBarDy
+ gReBarDyFrame
;
3429 SetWindowPos(win
->hwndCanvas
, NULL
, 0, rebBarDy
, dx
, dy
-rebBarDy
, SWP_NOZORDER
);
3432 static void ReloadPdfDocument(WindowInfo
*win
)
3434 if (WS_SHOWING_PDF
!= win
->state
)
3436 const char *fileName
= NULL
;
3438 fileName
= (const char*)str_dup(win
->dm
->fileName());
3439 CloseWindow(win
, false);
3442 free((void*)fileName
);
3446 static void RebuildProgramMenus(void)
3448 HMENU m
= ForceRebuildMenu();
3449 WindowInfo
*win
= gWindowList
;
3451 SetMenu(win
->hwndFrame
, m
);
3452 MenuUpdateStateForWindow(win
);
3457 static void LanguageChanged(const char *langName
)
3459 assert(!str_eq(langName
, CurrLangNameGet()));
3461 CurrLangNameSet(langName
);
3463 RebuildProgramMenus();
3464 // TODO: recreate tooltips
3467 static void OnMenuLanguage(int langId
)
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
;
3478 if (!langName
) return;
3479 if (str_eq(langName
, CurrLangNameGet()))
3481 LanguageChanged(langName
);
3484 static void OnMenuViewUseFitz(WindowInfo
*win
)
3487 DBG_OUT("OnMenuViewUseFitz()\n");
3493 ReloadPdfDocument(win
);
3496 MenuUpdateUseFitzStateForWindow(win
);
3501 static void OnMenuViewShowHideToolbar()
3504 gShowToolbar
= FALSE
;
3506 gShowToolbar
= TRUE
;
3508 WindowInfo
* win
= gWindowList
;
3511 ShowWindow(win
->hwndReBar
, SW_SHOW
);
3513 ShowWindow(win
->hwndReBar
, SW_HIDE
);
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
);
3527 static void OnMenuViewContinuous(WindowInfo
*win
)
3531 if (!WindowInfo_PdfLoaded(win
))
3533 SwitchToDisplayMode(win
, DM_CONTINUOUS
);
3536 static void OnMenuViewContinuousFacing(WindowInfo
*win
)
3540 if (!WindowInfo_PdfLoaded(win
))
3542 SwitchToDisplayMode(win
, DM_CONTINUOUS_FACING
);
3545 static void OnMenuGoToNextPage(WindowInfo
*win
)
3549 if (!WindowInfo_PdfLoaded(win
))
3551 win
->dm
->goToNextPage(0);
3554 static void OnMenuGoToPrevPage(WindowInfo
*win
)
3558 if (!WindowInfo_PdfLoaded(win
))
3560 win
->dm
->goToPrevPage(0);
3563 static void OnMenuGoToLastPage(WindowInfo
*win
)
3567 if (!WindowInfo_PdfLoaded(win
))
3569 win
->dm
->goToLastPage();
3572 static void OnMenuGoToFirstPage(WindowInfo
*win
)
3576 if (!WindowInfo_PdfLoaded(win
))
3578 win
->dm
->goToFirstPage();
3581 static void OnMenuGoToPage(WindowInfo
*win
)
3585 if (!WindowInfo_PdfLoaded(win
))
3588 int newPageNo
= Dialog_GoToPage(win
);
3589 if (win
->dm
->validPageNo(newPageNo
))
3590 win
->dm
->goToPage(newPageNo
, 0);
3593 static void OnMenuViewRotateLeft(WindowInfo
*win
)
3598 static void OnMenuViewRotateRight(WindowInfo
*win
)
3603 #define KEY_PRESSED_MASK 0x8000
3604 static bool WasKeyDown(int virtKey
)
3606 SHORT state
= GetKeyState(virtKey
);
3607 if (KEY_PRESSED_MASK
& state
)
3612 static bool WasShiftPressed()
3614 return WasKeyDown(VK_LSHIFT
) || WasKeyDown(VK_RSHIFT
);
3617 static bool WasCtrlPressed()
3619 return WasKeyDown(VK_LCONTROL
) || WasKeyDown(VK_RCONTROL
);
3622 static void OnKeydown(WindowInfo
*win
, int key
, LPARAM lparam
)
3626 if (win
->documentBlocked
)
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);
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
) {
3658 OnMenuGoToPage(win
);
3660 } else if (VK_OEM_PLUS
== key
) {
3661 // Emulate acrobat: "Shift Ctrl +" is rotate clockwise
3662 if (shiftPressed
& ctrlPressed
)
3664 } else if (VK_OEM_MINUS
== key
) {
3665 // Emulate acrobat: "Shift Ctrl -" is rotate counter-clockwise
3666 if (shiftPressed
& ctrlPressed
)
3671 static void OnChar(WindowInfo
*win
, int key
)
3675 if (win
->documentBlocked
)
3678 // DBG_OUT("char=%d,%c\n", key, (char)key);
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
);
3710 static inline bool IsEnumPrintersArg(const char *txt
)
3712 if (str_ieq(txt
, ENUM_PRINTERS_ARG_TXT
))
3717 static inline bool IsDontRegisterExtArg(const char *txt
)
3719 if (str_ieq(txt
, NO_REGISTER_EXT_ARG_TXT
))
3724 static inline bool IsPrintToArg(const char *txt
)
3726 if (str_ieq(txt
, PRINT_TO_ARG_TXT
))
3731 static inline bool IsPrintToDefaultArg(const char *txt
)
3733 if (str_ieq(txt
, PRINT_TO_ARG_TXT
))
3738 static inline bool IsExitOnPrintArg(const char *txt
)
3740 if (str_ieq(txt
, EXIT_ON_PRINT_ARG_TXT
))
3745 static inline bool IsBenchArg(const char *txt
)
3747 if (str_ieq(txt
, BENCH_ARG_TXT
))
3752 static bool IsBenchMode(void)
3754 if (NULL
!= gBenchFileName
)
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.
3765 static const char *RecentFileNameFromMenuItemId(UINT menuId
) {
3766 FileHistoryList
* curr
= gFileHistoryRoot
;
3768 if (curr
->menuId
== menuId
)
3769 return str_dup(curr
->state
.filePath
);
3775 #define FRAMES_PER_SECS 60
3776 #define ANIM_FREQ_IN_MS 1000 / FRAMES_PER_SECS
3778 static void OnMenuAbout() {
3780 SetActiveWindow(gHwndAbout
);
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
,
3789 ghinst
, NULL
); /* @note: TCHAR* cast */
3792 ShowWindow(gHwndAbout
, SW_SHOW
);
3795 BOOL
PrivateIsAppThemed() {
3796 BOOL isThemed
= FALSE
;
3797 HMODULE hDll
= LoadLibrary(TEXT("uxtheme.dll")); /* @note: TEXT() cast */
3798 if (!hDll
) return FALSE
;
3800 FARPROC fp
= GetProcAddress(hDll
, "IsAppThemed");
3808 static TBBUTTON
TbButtonFromButtonInfo(int i
) {
3809 TBBUTTON tbButton
= {0};
3810 if (IDB_SEPARATOR
== gToolbarButtons
[i
].cmdId
) {
3811 tbButton
.fsStyle
= TBSTYLE_SEP
;
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
;
3822 #define WS_TOOLBAR (WS_CHILD | WS_CLIPSIBLINGS | \
3823 TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | \
3824 TBSTYLE_LIST | CCS_NODIVIDER | CCS_NOPARENTALIGN )
3826 static void CreateToolbar(WindowInfo
*win
, HINSTANCE hInst
) {
3827 BOOL bIsAppThemed
= PrivateIsAppThemed();
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);
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
));
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);
3848 int index
= ImageList_AddMasked(himl
, hbmp
, RGB(255,0,255));
3850 gToolbarButtons
[i
].index
= index
;
3852 tbButtons
[i
] = TbButtonFromButtonInfo(i
);
3854 lres
= SendMessage(hwndToolbar
, TB_SETIMAGELIST
, 0, (LPARAM
)himl
);
3856 // TODO: construct disabled image list as well?
3857 //SendMessage(hwndToolbar, TB_SETDISABLEDIMAGELIST, 0, (LPARAM)himl);
3859 LRESULT exstyle
= SendMessage(hwndToolbar
, TB_GETEXTENDEDSTYLE
, 0, 0);
3860 exstyle
|= TBSTYLE_EX_MIXEDBUTTONS
;
3861 lres
= SendMessage(hwndToolbar
, TB_SETEXTENDEDSTYLE
, 0, exstyle
);
3863 lres
= SendMessage(hwndToolbar
, TB_ADDBUTTONS
, TOOLBAR_BUTTONS_COUNT
, (LPARAM
)tbButtons
);
3865 lres
= SendMessage(hwndToolbar
, TB_GETITEMRECT
, 0, (LPARAM
)&rc
);
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
)
3874 rbi
.cbSize
= sizeof(REBARINFO
);
3876 rbi
.himl
= (HIMAGELIST
)NULL
;
3877 lres
= SendMessage(win
->hwndReBar
, RB_SETBARINFO
, 0, (LPARAM
)&rbi
);
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*/;
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
;
3892 lres
= SendMessage(win
->hwndReBar
, RB_INSERTBAND
, (WPARAM
)-1, (LPARAM
)&rbBand
);
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;
3903 static LRESULT CALLBACK
WndProcAbout(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3908 assert(!gHwndAbout
);
3912 // do nothing, helps to avoid flicker
3925 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
3930 /* TODO: gAccumDelta must be per WindowInfo */
3931 static int gDeltaPerLine
, gAccumDelta
; // for mouse wheel logic
3933 static LRESULT CALLBACK
WndProcCanvas(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3936 win
= WindowInfo_FindByHwnd(hwnd
);
3939 case WM_APP_REPAINT_DELAYED
:
3941 SetTimer(win
->hwndCanvas
, REPAINT_TIMER_ID
, REPAINT_DELAY_IN_MS
, NULL
);
3944 case WM_APP_REPAINT_NOW
:
3946 WindowInfo_RedrawAll(win
);
3950 OnVScroll(win
, wParam
);
3951 return WM_VSCROLL_HANDLED
;
3954 OnHScroll(win
, wParam
);
3955 return WM_HSCROLL_HANDLED
;
3959 OnMouseMove(win
, GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
), wParam
);
3962 case WM_LBUTTONDOWN
:
3964 OnMouseLeftButtonDown(win
, GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
3969 OnMouseLeftButtonUp(win
, GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
3972 case WM_RBUTTONDOWN
:
3974 OnMouseRightButtonDown(win
, GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
3979 OnMouseRightButtonUp(win
, GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
3983 if (win
&& win
->mouseAction
== MA_DRAGGING
) {
3984 SetCursor(gCursorDrag
);
3992 if (REPAINT_TIMER_ID
== wParam
)
3993 WindowInfo_RedrawAll(win
);
3995 AnimState_NextFrame(&win
->animState
);
4001 OnDropFiles(win
, (HDROP
)wParam
);
4005 // do nothing, helps to avoid flicker
4009 /* it might happen that we get WM_PAINT after destroying a window */
4011 /* blindly kill the timer, just in case it's there */
4012 KillTimer(win
->hwndCanvas
, REPAINT_TIMER_ID
);
4018 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
4023 static LRESULT CALLBACK
WndProcFrame(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
4027 ULONG ulScrollLines
; // for mouse wheel logic
4028 const char * fileName
;
4030 win
= WindowInfo_FindByHwnd(hwnd
);
4036 goto InitMouseWheelInfo
;
4040 int dx
= LOWORD(lParam
);
4041 int dy
= HIWORD(lParam
);
4042 OnSize(win
, dx
, dy
);
4047 wmId
= LOWORD(wParam
);
4048 wmEvent
= HIWORD(wParam
);
4050 fileName
= RecentFileNameFromMenuItemId(wmId
);
4053 free((void*)fileName
);
4067 case IDT_FILE_PRINT
:
4072 case IDM_MAKE_DEFAULT_READER
:
4073 OneMenuMakeDefaultReader();
4078 CloseWindow(win
, FALSE
);
4085 case IDT_VIEW_ZOOMIN
:
4087 win
->dm
->zoomBy(ZOOM_IN_FACTOR
);
4090 case IDT_VIEW_ZOOMOUT
:
4092 win
->dm
->zoomBy(ZOOM_OUT_FACTOR
);
4108 case IDM_ZOOM_FIT_PAGE
:
4109 case IDM_ZOOM_FIT_WIDTH
:
4110 case IDM_ZOOM_ACTUAL_SIZE
:
4111 OnMenuZoom(win
, (UINT
)wmId
);
4114 case IDM_ZOOM_FIT_VISIBLE
:
4115 /* TODO: implement me */
4118 case IDM_VIEW_SINGLE_PAGE
:
4119 OnMenuViewSinglePage(win
);
4122 case IDM_VIEW_FACING
:
4123 OnMenuViewFacing(win
);
4126 case IDM_VIEW_CONTINUOUS
:
4127 OnMenuViewContinuous(win
);
4130 case IDM_VIEW_SHOW_HIDE_TOOLBAR
:
4131 OnMenuViewShowHideToolbar();
4134 case IDM_VIEW_USE_FITZ
:
4135 OnMenuViewUseFitz(win
);
4138 case IDM_GOTO_NEXT_PAGE
:
4139 OnMenuGoToNextPage(win
);
4142 case IDM_GOTO_PREV_PAGE
:
4143 OnMenuGoToPrevPage(win
);
4146 case IDM_GOTO_FIRST_PAGE
:
4147 OnMenuGoToFirstPage(win
);
4150 case IDM_GOTO_LAST_PAGE
:
4151 OnMenuGoToLastPage(win
);
4155 OnMenuGoToPage(win
);
4158 case IDM_VIEW_CONTINUOUS_FACING
:
4159 OnMenuViewContinuousFacing(win
);
4162 case IDM_VIEW_ROTATE_LEFT
:
4163 OnMenuViewRotateLeft(win
);
4166 case IDM_VIEW_ROTATE_RIGHT
:
4167 OnMenuViewRotateRight(win
);
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 */
4179 OnMenuLanguage((int)wmId
);
4186 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
4192 OnChar(win
, wParam
);
4197 OnKeydown(win
, wParam
, lParam
);
4200 case WM_SETTINGCHANGE
:
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
4206 gDeltaPerLine
= WHEEL_DELTA
/ ulScrollLines
;
4211 // TODO: I don't understand why WndProcCanvas() doesn't receive this message
4213 if (!win
|| !win
->dm
) /* TODO: check for pdfDoc as well ? */
4216 if (gDeltaPerLine
== 0)
4219 gAccumDelta
+= (short) HIWORD (wParam
); // 120 or -120
4221 while (gAccumDelta
>= gDeltaPerLine
)
4223 SendMessage(win
->hwndCanvas
, WM_VSCROLL
, SB_LINEUP
, 0);
4224 gAccumDelta
-= gDeltaPerLine
;
4227 while (gAccumDelta
<= -gDeltaPerLine
)
4229 SendMessage(win
->hwndCanvas
, WM_VSCROLL
, SB_LINEDOWN
, 0);
4230 gAccumDelta
+= gDeltaPerLine
;
4236 OnDropFiles(win
, (HDROP
)wParam
);
4240 /* WM_DESTROY might be sent as a result of File\Close, in which case CloseWindow() has already been called */
4242 CloseWindow(win
, TRUE
);
4245 case IDM_VIEW_WITH_ACROBAT
:
4247 ViewWithAcrobat(win
);
4250 case MSG_BENCH_NEXT_ACTION
:
4252 OnBenchNextAction(win
);
4256 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
4261 static BOOL
RegisterWinClass(HINSTANCE hInstance
)
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
);
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
;
4289 wcex
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
4290 wcex
.hbrBackground
= NULL
;
4291 wcex
.lpszMenuName
= NULL
;
4292 wcex
.lpszClassName
= CANVAS_CLASS_NAME
;
4294 atom
= RegisterClassEx(&wcex
);
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
;
4305 wcex
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
4306 wcex
.hbrBackground
= NULL
;
4307 wcex
.lpszMenuName
= NULL
;
4308 wcex
.lpszClassName
= ABOUT_CLASS_NAME
;
4310 atom
= RegisterClassEx(&wcex
);
4317 #define IDC_HAND MAKEINTRESOURCE(32649)
4318 static BOOL
InstanceInit(HINSTANCE hInstance
, int nCmdShow
)
4322 globalParams
= new GlobalParams("");
4327 gCursorArrow
= LoadCursor(NULL
, IDC_ARROW
);
4328 gCursorHand
= LoadCursor(NULL
, IDC_HAND
); // apparently only available if WINVER >= 0x0500
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));
4339 static void StrList_Reverse(StrList
**strListRoot
)
4341 StrList
*newRoot
= NULL
;
4342 StrList
*cur
, *next
;
4348 cur
->next
= newRoot
;
4352 *strListRoot
= newRoot
;
4355 static BOOL
StrList_InsertAndOwn(StrList
**root
, char *txt
)
4358 assert(root
&& txt
);
4362 el
= (StrList
*)malloc(sizeof(StrList
));
4371 static void StrList_Destroy(StrList
**root
)
4381 free((void*)cur
->str
);
4388 static StrList
*StrList_FromCmdLine(char *cmdLine
)
4391 StrList
* strList
= NULL
;
4399 exePath
= ExePathGet();
4402 if (!StrList_InsertAndOwn(&strList
, exePath
)) {
4403 free((void*)exePath
);
4408 txt
= str_parse_possibly_quoted(&cmdLine
);
4411 if (!StrList_InsertAndOwn(&strList
, txt
)) {
4416 StrList_Reverse(&strList
);
4420 static void u_DoAllTests(void)
4423 printf("Running tests\n");
4424 u_RectI_Intersect();
4426 printf("Not running tests\n");
4430 #define CONSERVE_MEMORY 1
4432 static DWORD WINAPI
PageRenderThread(PVOID data
)
4434 PageRenderRequest req
;
4435 RenderedBitmap
* bmp
;
4437 DBG_OUT("PageRenderThread() started\n");
4439 //DBG_OUT("Worker: wait\n");
4441 gCurPageRenderReq
= NULL
;
4442 int count
= gPageRenderRequestsCount
;
4445 DWORD waitResult
= WaitForSingleObject(gPageRenderSem
, INFINITE
);
4446 if (WAIT_OBJECT_0
!= waitResult
) {
4447 DBG_OUT(" WaitForSingleObject() failed\n");
4451 if (0 == gPageRenderRequestsCount
) {
4455 RenderQueue_Pop(&req
);
4456 gCurPageRenderReq
= &req
;
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");
4464 MsTimer renderTimer
;
4465 bmp
= req
.dm
->renderBitmap(req
.pageNo
, req
.zoomLevel
, req
.rotation
, pageRenderAbortCb
, (void*)&req
);
4468 gCurPageRenderReq
= NULL
;
4475 DBG_OUT("PageRenderThread(): finished rendering %d\n", req
.pageNo
);
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();
4483 WindowInfo
* win
= (WindowInfo
*)req
.dm
->appData();
4484 triggerRepaintDisplayNow(win
);
4486 DBG_OUT("PageRenderThread() finished\n");
4490 static void CreatePageRenderThread(void)
4492 LONG semMaxCount
= 1000; /* don't really know what the limit should be */
4493 DWORD dwThread1ID
= 0;
4494 assert(NULL
== gPageRenderThreadHandle
);
4496 gPageRenderSem
= CreateSemaphore(NULL
, 0, semMaxCount
, NULL
);
4497 gPageRenderThreadHandle
= CreateThread(NULL
, 0, PageRenderThread
, (void*)NULL
, 0, &dwThread1ID
);
4498 assert(NULL
!= gPageRenderThreadHandle
);
4501 static void PrintFile(WindowInfo
*win
, const char *fileName
, const char *printerName
)
4503 char devstring
[256]; // array for WIN.INI data
4505 LPDEVMODE devMode
= NULL
;
4506 DWORD structSize
, returnCode
;
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
);
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
));
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 *) ",");
4523 if (!driver
|| !port
) {
4524 MessageBox(win
->hwndFrame
, TEXT("Printer with given name doesn't exist"), TEXT("Printing problem."), MB_ICONEXCLAMATION
| MB_OK
);
4528 BOOL fOk
= OpenPrinter((TCHAR
*)printerName
, &printer
, NULL
); /* @note: neither LPCTSTR nor LPCTSTR work => TCHAR* cast */
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
);
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
);
4545 /* @note: "crosses initialization of [...]" issues */
4551 // Get the default DevMode for the printer and modify it for your needs.
4552 returnCode
= DocumentProperties(NULL
,
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. */
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 */
4568 PdfPageInfo
* pageInfo
= pageInfo
= win
->dm
->getPageInfo(1);
4570 if (pageInfo
->bitmapDx
> pageInfo
->bitmapDy
) {
4571 devMode
->dmOrientation
= DMORIENT_LANDSCAPE
;
4573 devMode
->dmOrientation
= DMORIENT_PORTRAIT
;
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.
4581 DocumentProperties(NULL
,
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. */
4589 ClosePrinter(printer
);
4591 hdcPrint
= CreateDC((TCHAR
*)driver
, (TCHAR
*)printerName
, (TCHAR
*)port
, devMode
);
4593 MessageBox(win
->hwndFrame
, TEXT("Couldn't initialize printer"), TEXT("Printing problem."), MB_ICONEXCLAMATION
| MB_OK
);
4594 /* @note: "crosses initialization of [...]" issues */
4600 if (CheckPrinterStretchDibSupport(win
->hwndFrame
, hdcPrint
))
4601 PrintToDevice(win
->dm
, hdcPrint
, devMode
, 1, win
->dm
->pageCount());
4607 static void EnumeratePrinters()
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
);
4614 info5Arr
= (PRINTER_INFO_5
*)malloc(bufSize
);
4615 fOk
= EnumPrinters(PRINTER_ENUM_LOCAL
| PRINTER_ENUM_CONNECTIONS
, NULL
,
4616 5, (LPBYTE
)info5Arr
, bufSize
, &bufSize
, &printersCount
);
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
)
4629 printf("Name: %s, port: %s, default: %d\n", printerName
, printerPort
, (int)fDefault
);
4632 bufSize
= sizeof(buf
);
4633 fOk
= GetDefaultPrinter(buf
, &bufSize
);
4635 if (ERROR_FILE_NOT_FOUND
== GetLastError())
4636 printf("No default printer\n");
4641 /* Get the name of default printer or NULL if not exists.
4642 The caller needs to free() the result */
4643 char *GetDefaultPrinterName()
4646 DWORD bufSize
= sizeof(buf
);
4647 if (GetDefaultPrinterA(buf
, &bufSize
))
4648 return str_dup(buf
);
4652 int APIENTRY
_tWinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPTSTR lpCmdLine
, int nCmdShow
)
4654 StrList
* argListRoot
;
4656 char * benchPageNumStr
= NULL
;
4661 bool exitOnPrint
= false;
4662 bool printToDefaultPrinter
= false;
4664 UNREFERENCED_PARAMETER(hPrevInstance
);
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
);
4673 argListRoot
= StrList_FromCmdLine((char*)lpCmdLine
); /* @note: char* cast */
4674 assert(argListRoot
);
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
;
4687 if (IsEnumPrintersArg(currArg
->str
)) {
4688 EnumeratePrinters();
4689 /* this is for testing only, exit immediately */
4693 if (IsDontRegisterExtArg(currArg
->str
)) {
4694 registerForPdfExtentions
= false;
4695 currArg
= currArg
->next
;
4699 if (IsBenchArg(currArg
->str
)) {
4700 currArg
= currArg
->next
;
4702 gBenchFileName
= currArg
->str
;
4704 benchPageNumStr
= currArg
->next
->str
;
4709 if (IsExitOnPrintArg(currArg
->str
)) {
4710 currArg
= currArg
->next
;
4715 if (IsPrintToDefaultArg(currArg
->str
)) {
4716 printToDefaultPrinter
= true;
4720 if (IsPrintToArg(currArg
->str
)) {
4721 currArg
= currArg
->next
;
4723 printerName
= currArg
->str
;
4724 currArg
= currArg
->next
;
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
4736 if (benchPageNumStr
) {
4737 gBenchPageNum
= atoi(benchPageNumStr
);
4738 if (gBenchPageNum
< 1)
4739 gBenchPageNum
= INVALID_PAGE_NO
;
4742 LoadString(hInstance
, IDS_APP_TITLE
, windowTitle
, MAX_LOADSTRING
);
4743 if (!RegisterWinClass(hInstance
))
4746 CaptionPens_Create();
4747 if (!InstanceInit(hInstance
, nCmdShow
))
4750 hAccelTable
= LoadAccelerators(hInstance
, MAKEINTRESOURCE(IDC_SUMATRAPDF
));
4752 CreatePageRenderThread();
4753 /* remaining arguments are names of PDF files */
4754 if (NULL
!= gBenchFileName
) {
4755 win
= LoadPdf(gBenchFileName
);
4760 win
= LoadPdf(currArg
->str
);
4765 ShowWindow(win
->hwndFrame
, SW_HIDE
);
4767 if (printToDefaultPrinter
) {
4768 printerName
= GetDefaultPrinterName();
4770 PrintFile(win
, currArg
->str
, 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
);
4778 currArg
= currArg
->next
;
4782 if (printerName
&& exitOnPrint
)
4785 if (0 == pdfOpened
) {
4786 /* disable benchmark mode if we couldn't open file to benchmark */
4788 #ifdef REOPEN_FILES_AT_STARTUP
4789 FileHistoryList
* currFile
= gFileHistoryRoot
;
4791 if (currFile
->state
.visible
) {
4792 win
= LoadPdf(currFile
->state
.filePath
, false);
4796 currFile
= currFile
->next
;
4799 if (0 == pdfOpened
) {
4800 win
= WindowInfo_CreateEmpty();
4803 WindowInfoList_Add(win
);
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
);
4814 if (IsBenchMode()) {
4816 assert(pdfOpened
> 0);
4818 PostBenchNextAction(win
->hwndFrame
);
4822 MenuToolbarUpdateStateForAllWindows();
4824 if (registerForPdfExtentions
)
4825 RegisterForPdfExtentions(win
? win
->hwndFrame
: NULL
);
4827 while (GetMessage(&msg
, NULL
, 0, 0)) {
4828 if (!TranslateAccelerator(msg
.hwnd
, hAccelTable
, &msg
)) {
4829 TranslateMessage(&msg
);
4830 DispatchMessage(&msg
);
4835 WindowInfoList_DeleteAll();
4836 FileHistoryList_Free(&gFileHistoryRoot
);
4837 CaptionPens_Destroy();
4838 DeleteObject(gBrushBg
);
4839 DeleteObject(gBrushWhite
);
4840 DeleteObject(gBrushShadow
);
4841 DeleteObject(gBrushLinkDebug
);
4843 delete globalParams
;
4844 StrList_Destroy(&argListRoot
);
4845 Translations_FreeData();
4848 return (int) msg
.wParam
;
4851 // Code for DLL interace
4852 static WindowInfo
* CreateEmpty(HWND parentHandle
) {
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
,
4864 pdfWin
->hwndCanvas
= hwndCanvas
;
4868 static void OpenPdf(WindowInfo
* pdfWin
,const char *fileName
, HWND parentHandle
)
4871 if (!fileName
) return;
4873 if (!pdfWin
) return;
4875 pdfWin
->GetCanvasSize();
4876 SizeI maxCanvasSize
= GetMaxCanvasSize(pdfWin
);
4877 SizeD
totalDrawAreaSize(pdfWin
->winSize());
4878 DisplayMode displayMode
= DEFAULT_DISPLAY_MODE
;
4882 int scrollbarYDx
= 0;
4883 int scrollbarXDy
= 0;
4886 pdfWin
->dm
= DisplayModelFitz_CreateFromFileName(fileName
,
4887 totalDrawAreaSize
, scrollbarYDx
, scrollbarXDy
, displayMode
, startPage
, pdfWin
);
4890 pdfWin
->dm
= DisplayModelSplash_CreateFromFileName(fileName
,
4891 totalDrawAreaSize
, scrollbarYDx
, scrollbarXDy
, displayMode
, startPage
, pdfWin
);
4894 pdfWin
->dm
->setAppData((void*)pdfWin
);
4895 pdfWin
->state
= WS_SHOWING_PDF
;
4896 double zoomVirtual
= DEFAULT_ZOOM
;
4897 int rotation
= DEFAULT_ROTATION
;
4899 UINT menuId
= MenuIdFromVirtualZoom(zoomVirtual
);
4900 ZoomMenuItemCheck(GetMenu(pdfWin
->hwndFrame
), menuId
);
4902 pdfWin
->dm
->relayout(zoomVirtual
, rotation
);
4903 if (!pdfWin
->dm
->validPageNo(startPage
))
4906 pdfWin
->dm
->goToPage(startPage
, offsetY
, offsetX
);
4907 WindowInfo_ResizeToPage(pdfWin
, startPage
);
4908 WindowInfoList_Add(pdfWin
);
4911 if (GetWindowRect(pdfWin
->hwndFrame
, &rect
) != 0)
4913 int nWidth
= rect_dx(&rect
);
4914 int nHeight
= rect_dy(&rect
);
4915 WinResizeClientArea(pdfWin
->hwndCanvas
, nWidth
, nHeight
);
4918 ShowWindow(pdfWin
->hwndFrame
, SW_SHOW
);
4919 ShowWindow(pdfWin
->hwndCanvas
, SW_SHOW
);
4920 UpdateWindow(pdfWin
->hwndFrame
);
4921 UpdateWindow(pdfWin
->hwndCanvas
);
4924 void Sumatra_LoadPDF(WindowInfo
* pdfWin
, const char *pdfFile
)
4927 OpenPdf(pdfWin
, pdfFile
, pdfWin
->hwndFrame
);
4930 ShowWindow(pdfWin
->hwndFrame
, SW_SHOWNORMAL
);
4933 void Sumatra_PrintPDF(WindowInfo
* pdfWin
, const char *pdfFile
, long showOptionWindow
)
4937 void Sumatra_Print(WindowInfo
* pdfWin
)
4939 if (WindowInfo_PdfLoaded(pdfWin
))
4940 OnMenuPrint(pdfWin
);
4943 void Sumatra_ShowPrintDialog(WindowInfo
* pdfWin
)
4945 if (WindowInfo_PdfLoaded(pdfWin
))
4946 OnMenuPrint(pdfWin
);
4949 void Sumatra_SetDisplayMode(WindowInfo
* pdfWin
,long displayMode
)
4951 if (WindowInfo_PdfLoaded(pdfWin
))
4952 SwitchToDisplayMode(pdfWin
, (DisplayMode
)displayMode
);
4955 long Sumatra_GoToNextPage(WindowInfo
* pdfWin
)
4957 if (!WindowInfo_PdfLoaded(pdfWin
))
4959 pdfWin
->dm
->goToNextPage(0);
4960 return pdfWin
->dm
->currentPageNo();
4963 long Sumatra_GoToPreviousPage(WindowInfo
* pdfWin
)
4965 if (!WindowInfo_PdfLoaded(pdfWin
))
4967 pdfWin
->dm
->goToPrevPage(0);
4968 return pdfWin
->dm
->currentPageNo();
4971 long Sumatra_GoToFirstPage(WindowInfo
* pdfWin
)
4973 if (!WindowInfo_PdfLoaded(pdfWin
))
4975 pdfWin
->dm
->goToFirstPage();
4976 return pdfWin
->dm
->currentPageNo();
4979 long Sumatra_GoToLastPage(WindowInfo
* pdfWin
)
4981 if (!WindowInfo_PdfLoaded(pdfWin
))
4983 pdfWin
->dm
->goToLastPage();
4984 return pdfWin
->dm
->currentPageNo();
4987 long Sumatra_GetNumberOfPages(WindowInfo
* pdfWin
)
4989 if (!WindowInfo_PdfLoaded(pdfWin
))
4991 return pdfWin
->dm
->pageCount();
4994 long Sumatra_GetCurrentPage(WindowInfo
* pdfWin
)
4996 if (!WindowInfo_PdfLoaded(pdfWin
))
4998 return pdfWin
->dm
->currentPageNo();
5001 long Sumatra_GoToThisPage(WindowInfo
* pdfWin
,long pageNumber
)
5003 if (!WindowInfo_PdfLoaded(pdfWin
))
5005 if (pdfWin
->dm
->validPageNo(pageNumber
))
5006 pdfWin
->dm
->goToPage(pageNumber
, 0);
5007 return pdfWin
->dm
->currentPageNo();
5010 long Sumatra_ZoomIn(WindowInfo
* pdfWin
)
5012 if (WindowInfo_PdfLoaded(pdfWin
))
5014 long currentZoom
= Sumatra_GetCurrentZoom(pdfWin
);
5015 if (currentZoom
< 500)
5016 Sumatra_SetZoom(pdfWin
,currentZoom
+10);
5018 return Sumatra_GetCurrentZoom(pdfWin
);
5021 long Sumatra_ZoomOut(WindowInfo
* pdfWin
)
5023 if (WindowInfo_PdfLoaded(pdfWin
))
5025 long currentZoom
= Sumatra_GetCurrentZoom(pdfWin
);
5026 if (currentZoom
> 10)
5027 Sumatra_SetZoom(pdfWin
,currentZoom
-10);
5029 return Sumatra_GetCurrentZoom(pdfWin
);
5032 long Sumatra_SetZoom(WindowInfo
* pdfWin
,long zoomValue
)
5034 if (WindowInfo_PdfLoaded(pdfWin
))
5035 pdfWin
->dm
->zoomTo((double)zoomValue
);
5036 return Sumatra_GetCurrentZoom(pdfWin
);
5039 long Sumatra_GetCurrentZoom(WindowInfo
* pdfWin
)
5041 double zoomLevel
= 0;
5042 if (WindowInfo_PdfLoaded(pdfWin
))
5043 zoomLevel
= pdfWin
->dm
->zoomReal();
5044 return (long)zoomLevel
;
5047 void Sumatra_Resize(WindowInfo
* pdfWin
)
5050 if (GetWindowRect(pdfWin
->hwndFrame
, &rect
) != 0)
5052 int nWidth
= rect_dx(&rect
);
5053 int nHeight
= rect_dy(&rect
);
5054 WinResizeClientArea(pdfWin
->hwndCanvas
, nWidth
, nHeight
);
5058 void Sumatra_ClosePdf(WindowInfo
* pdfWin
)
5060 if (WindowInfo_PdfLoaded(pdfWin
))
5061 CloseWindow(pdfWin
, FALSE
);
5064 WindowInfo
* Sumatra_Init(HWND pHandle
)
5068 HINSTANCE hInstance
= NULL
;
5069 HINSTANCE hPrevInstance
= NULL
;
5072 StrList
* argListRoot
= NULL
;
5073 StrList
* currArg
= NULL
;
5075 bool exitOnPrint
= false;
5076 bool printToDefaultPrinter
= false;
5079 UNREFERENCED_PARAMETER(hPrevInstance
);
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
);
5089 LoadString(hInstance
, IDS_APP_TITLE
, windowTitle
, MAX_LOADSTRING
);
5091 if (!RegisterWinClass(hInstance
))
5094 CaptionPens_Create();
5096 if (!InstanceInit(hInstance
, nCmdShow
))
5099 CreatePageRenderThread();
5101 bool reuseExistingWindow
= false;
5106 pdfWin
= CreateEmpty(pHandle
);
5113 CaptionPens_Destroy();
5114 DeleteObject(gBrushBg
);
5115 DeleteObject(gBrushWhite
);
5116 DeleteObject(gBrushShadow
);
5117 DeleteObject(gBrushLinkDebug
);
5118 delete globalParams
;