Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/platform/x11/win_main.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 // Copyright (C) 2004-2025 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #ifndef UNICODE | |
| 24 #define UNICODE | |
| 25 #endif | |
| 26 #ifndef _UNICODE | |
| 27 #define _UNICODE | |
| 28 #endif | |
| 29 #define WIN32_LEAN_AND_MEAN | |
| 30 #include <windows.h> | |
| 31 #include <commdlg.h> | |
| 32 #include <shellapi.h> | |
| 33 | |
| 34 /* Include pdfapp.h *AFTER* the UNICODE defines */ | |
| 35 #include "pdfapp.h" | |
| 36 | |
| 37 #include <stdio.h> | |
| 38 #include <stdlib.h> | |
| 39 #include <assert.h> | |
| 40 | |
| 41 #ifndef WM_MOUSEWHEEL | |
| 42 #define WM_MOUSEWHEEL 0x020A | |
| 43 #endif | |
| 44 | |
| 45 #define MIN(x,y) ((x) < (y) ? (x) : (y)) | |
| 46 | |
| 47 #define ID_ABOUT 0x1000 | |
| 48 #define ID_DOCINFO 0x1001 | |
| 49 | |
| 50 static HWND hwndframe = NULL; | |
| 51 static HWND hwndview = NULL; | |
| 52 static HDC hdc; | |
| 53 static HBRUSH bgbrush; | |
| 54 static BITMAPINFO *dibinf = NULL; | |
| 55 static HCURSOR arrowcurs, handcurs, waitcurs, caretcurs; | |
| 56 static LRESULT CALLBACK frameproc(HWND, UINT, WPARAM, LPARAM); | |
| 57 static LRESULT CALLBACK viewproc(HWND, UINT, WPARAM, LPARAM); | |
| 58 static int timer_pending = 0; | |
| 59 static char *password = NULL; | |
| 60 | |
| 61 static int justcopied = 0; | |
| 62 | |
| 63 static pdfapp_t gapp; | |
| 64 | |
| 65 static wchar_t wbuf[PATH_MAX]; | |
| 66 static char filename[PATH_MAX]; | |
| 67 | |
| 68 /* | |
| 69 * Create registry keys to associate MuPDF with PDF and XPS files. | |
| 70 */ | |
| 71 | |
| 72 #define OPEN_KEY(parent, name, ptr) \ | |
| 73 RegCreateKeyExA(parent, name, 0, 0, 0, KEY_WRITE, 0, &ptr, 0) | |
| 74 | |
| 75 #define SET_KEY(parent, name, value) \ | |
| 76 RegSetValueExA(parent, name, 0, REG_SZ, (const BYTE *)(value), (DWORD)strlen(value) + 1) | |
| 77 | |
| 78 static void install_app(char *argv0) | |
| 79 { | |
| 80 char buf[512]; | |
| 81 HKEY software, classes, mupdf, dotpdf, dotxps, dotepub, dotfb2; | |
| 82 HKEY shell, open, command, supported_types; | |
| 83 HKEY pdf_progids, xps_progids, epub_progids, fb2_progids; | |
| 84 | |
| 85 OPEN_KEY(HKEY_CURRENT_USER, "Software", software); | |
| 86 OPEN_KEY(software, "Classes", classes); | |
| 87 OPEN_KEY(classes, ".pdf", dotpdf); | |
| 88 OPEN_KEY(dotpdf, "OpenWithProgids", pdf_progids); | |
| 89 OPEN_KEY(classes, ".xps", dotxps); | |
| 90 OPEN_KEY(dotxps, "OpenWithProgids", xps_progids); | |
| 91 OPEN_KEY(classes, ".epub", dotepub); | |
| 92 OPEN_KEY(dotepub, "OpenWithProgids", epub_progids); | |
| 93 OPEN_KEY(classes, ".fb2", dotfb2); | |
| 94 OPEN_KEY(dotfb2, "OpenWithProgids", fb2_progids); | |
| 95 OPEN_KEY(classes, "MuPDF", mupdf); | |
| 96 OPEN_KEY(mupdf, "SupportedTypes", supported_types); | |
| 97 OPEN_KEY(mupdf, "shell", shell); | |
| 98 OPEN_KEY(shell, "open", open); | |
| 99 OPEN_KEY(open, "command", command); | |
| 100 | |
| 101 sprintf(buf, "\"%s\" \"%%1\"", argv0); | |
| 102 | |
| 103 SET_KEY(open, "FriendlyAppName", "MuPDF"); | |
| 104 SET_KEY(command, "", buf); | |
| 105 SET_KEY(supported_types, ".pdf", ""); | |
| 106 SET_KEY(supported_types, ".xps", ""); | |
| 107 SET_KEY(supported_types, ".epub", ""); | |
| 108 SET_KEY(pdf_progids, "MuPDF", ""); | |
| 109 SET_KEY(xps_progids, "MuPDF", ""); | |
| 110 SET_KEY(epub_progids, "MuPDF", ""); | |
| 111 SET_KEY(fb2_progids, "MuPDF", ""); | |
| 112 | |
| 113 RegCloseKey(dotfb2); | |
| 114 RegCloseKey(dotepub); | |
| 115 RegCloseKey(dotxps); | |
| 116 RegCloseKey(dotpdf); | |
| 117 RegCloseKey(mupdf); | |
| 118 RegCloseKey(classes); | |
| 119 RegCloseKey(software); | |
| 120 } | |
| 121 | |
| 122 /* | |
| 123 * Dialog boxes | |
| 124 */ | |
| 125 | |
| 126 void winwarn(pdfapp_t *app, char *msg) | |
| 127 { | |
| 128 MessageBoxA(hwndframe, msg, "MuPDF: Warning", MB_ICONWARNING); | |
| 129 } | |
| 130 | |
| 131 void winerror(pdfapp_t *app, char *msg) | |
| 132 { | |
| 133 MessageBoxA(hwndframe, msg, "MuPDF: Error", MB_ICONERROR); | |
| 134 exit(1); | |
| 135 } | |
| 136 | |
| 137 void winalert(pdfapp_t *app, pdf_alert_event *alert) | |
| 138 { | |
| 139 int buttons = MB_OK; | |
| 140 int icon = MB_ICONWARNING; | |
| 141 int pressed = PDF_ALERT_BUTTON_NONE; | |
| 142 | |
| 143 switch (alert->icon_type) | |
| 144 { | |
| 145 case PDF_ALERT_ICON_ERROR: | |
| 146 icon = MB_ICONERROR; | |
| 147 break; | |
| 148 case PDF_ALERT_ICON_WARNING: | |
| 149 icon = MB_ICONWARNING; | |
| 150 break; | |
| 151 case PDF_ALERT_ICON_QUESTION: | |
| 152 icon = MB_ICONQUESTION; | |
| 153 break; | |
| 154 case PDF_ALERT_ICON_STATUS: | |
| 155 icon = MB_ICONINFORMATION; | |
| 156 break; | |
| 157 } | |
| 158 | |
| 159 switch (alert->button_group_type) | |
| 160 { | |
| 161 case PDF_ALERT_BUTTON_GROUP_OK: | |
| 162 buttons = MB_OK; | |
| 163 break; | |
| 164 case PDF_ALERT_BUTTON_GROUP_OK_CANCEL: | |
| 165 buttons = MB_OKCANCEL; | |
| 166 break; | |
| 167 case PDF_ALERT_BUTTON_GROUP_YES_NO: | |
| 168 buttons = MB_YESNO; | |
| 169 break; | |
| 170 case PDF_ALERT_BUTTON_GROUP_YES_NO_CANCEL: | |
| 171 buttons = MB_YESNOCANCEL; | |
| 172 break; | |
| 173 } | |
| 174 | |
| 175 pressed = MessageBoxA(hwndframe, alert->message, alert->title, icon|buttons); | |
| 176 | |
| 177 switch (pressed) | |
| 178 { | |
| 179 case IDOK: | |
| 180 alert->button_pressed = PDF_ALERT_BUTTON_OK; | |
| 181 break; | |
| 182 case IDCANCEL: | |
| 183 alert->button_pressed = PDF_ALERT_BUTTON_CANCEL; | |
| 184 break; | |
| 185 case IDNO: | |
| 186 alert->button_pressed = PDF_ALERT_BUTTON_NO; | |
| 187 break; | |
| 188 case IDYES: | |
| 189 alert->button_pressed = PDF_ALERT_BUTTON_YES; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 void winprint(pdfapp_t *app) | |
| 194 { | |
| 195 MessageBoxA(hwndframe, "The MuPDF library supports printing, but this application currently does not", "Print document", MB_ICONWARNING); | |
| 196 } | |
| 197 | |
| 198 int winsavequery(pdfapp_t *app) | |
| 199 { | |
| 200 switch(MessageBoxA(hwndframe, "File has unsaved changes. Do you want to save", "MuPDF", MB_YESNOCANCEL)) | |
| 201 { | |
| 202 case IDYES: return SAVE; | |
| 203 case IDNO: return DISCARD; | |
| 204 default: return CANCEL; | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 int winquery(pdfapp_t *app, const char *query) | |
| 209 { | |
| 210 switch(MessageBoxA(hwndframe, query, "MuPDF", MB_YESNOCANCEL)) | |
| 211 { | |
| 212 case IDYES: return QUERY_YES; | |
| 213 case IDNO: | |
| 214 default: return QUERY_NO; | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 static int winfilename(wchar_t *buf, int len) | |
| 219 { | |
| 220 OPENFILENAME ofn; | |
| 221 buf[0] = 0; | |
| 222 memset(&ofn, 0, sizeof(OPENFILENAME)); | |
| 223 ofn.lStructSize = sizeof(OPENFILENAME); | |
| 224 ofn.hwndOwner = hwndframe; | |
| 225 ofn.lpstrFile = buf; | |
| 226 ofn.nMaxFile = len; | |
| 227 ofn.lpstrInitialDir = NULL; | |
| 228 ofn.lpstrTitle = L"MuPDF: Open PDF file"; | |
| 229 ofn.lpstrFilter = L"Documents (*.pdf;*.xps;*.cbz;*.epub;*.fb2;*.zip;*.png;*.jpeg;*.tiff)\0*.zip;*.cbz;*.xps;*.epub;*.fb2;*.pdf;*.jpe;*.jpg;*.jpeg;*.jfif;*.tif;*.tiff\0PDF Files (*.pdf)\0*.pdf\0XPS Files (*.xps)\0*.xps\0CBZ Files (*.cbz;*.zip)\0*.zip;*.cbz\0EPUB Files (*.epub)\0*.epub\0FictionBook 2 Files (*.fb2)\0*.fb2\0Image Files (*.png;*.jpeg;*.tiff)\0*.png;*.jpg;*.jpe;*.jpeg;*.jfif;*.tif;*.tiff\0All Files\0*\0\0"; | |
| 230 ofn.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; | |
| 231 return GetOpenFileNameW(&ofn); | |
| 232 } | |
| 233 | |
| 234 int wingetcertpath(pdfapp_t *app, char *buf, int len) | |
| 235 { | |
| 236 wchar_t twbuf[PATH_MAX] = {0}; | |
| 237 OPENFILENAME ofn; | |
| 238 buf[0] = 0; | |
| 239 memset(&ofn, 0, sizeof(OPENFILENAME)); | |
| 240 ofn.lStructSize = sizeof(OPENFILENAME); | |
| 241 ofn.hwndOwner = hwndframe; | |
| 242 ofn.lpstrFile = twbuf; | |
| 243 ofn.nMaxFile = PATH_MAX; | |
| 244 ofn.lpstrInitialDir = NULL; | |
| 245 ofn.lpstrTitle = L"MuPDF: Select certificate file"; | |
| 246 ofn.lpstrFilter = L"Certificates (*.pfx)\0*.pfx\0All files\0*\0\0"; | |
| 247 ofn.Flags = OFN_FILEMUSTEXIST; | |
| 248 if (GetOpenFileNameW(&ofn)) | |
| 249 { | |
| 250 int code = WideCharToMultiByte(CP_UTF8, 0, twbuf, -1, buf, MIN(PATH_MAX, len), NULL, NULL); | |
| 251 if (code == 0) | |
| 252 { | |
| 253 pdfapp_error(app, "cannot convert filename to utf-8"); | |
| 254 return 0; | |
| 255 } | |
| 256 | |
| 257 return 1; | |
| 258 } | |
| 259 else | |
| 260 { | |
| 261 return 0; | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 int wingetsavepath(pdfapp_t *app, char *buf, int len) | |
| 266 { | |
| 267 wchar_t twbuf[PATH_MAX]; | |
| 268 OPENFILENAME ofn; | |
| 269 | |
| 270 wcscpy(twbuf, wbuf); | |
| 271 memset(&ofn, 0, sizeof(OPENFILENAME)); | |
| 272 ofn.lStructSize = sizeof(OPENFILENAME); | |
| 273 ofn.hwndOwner = hwndframe; | |
| 274 ofn.lpstrFile = twbuf; | |
| 275 ofn.nMaxFile = PATH_MAX; | |
| 276 ofn.lpstrInitialDir = NULL; | |
| 277 ofn.lpstrTitle = L"MuPDF: Save PDF file"; | |
| 278 ofn.lpstrFilter = L"PDF Documents (*.pdf)\0*.pdf\0All Files\0*\0\0"; | |
| 279 ofn.Flags = OFN_HIDEREADONLY; | |
| 280 if (GetSaveFileName(&ofn)) | |
| 281 { | |
| 282 int code = WideCharToMultiByte(CP_UTF8, 0, twbuf, -1, buf, MIN(PATH_MAX, len), NULL, NULL); | |
| 283 if (code == 0) | |
| 284 { | |
| 285 pdfapp_error(app, "cannot convert filename to utf-8"); | |
| 286 return 0; | |
| 287 } | |
| 288 | |
| 289 wcscpy(wbuf, twbuf); | |
| 290 fz_strlcpy(filename, buf, sizeof filename); | |
| 291 return 1; | |
| 292 } | |
| 293 else | |
| 294 { | |
| 295 return 0; | |
| 296 } | |
| 297 } | |
| 298 | |
| 299 void winreplacefile(pdfapp_t *app, char *source, char *target) | |
| 300 { | |
| 301 wchar_t wsource[PATH_MAX]; | |
| 302 wchar_t wtarget[PATH_MAX]; | |
| 303 | |
| 304 int sz = MultiByteToWideChar(CP_UTF8, 0, source, -1, wsource, PATH_MAX); | |
| 305 if (sz == 0) | |
| 306 { | |
| 307 pdfapp_error(app, "cannot convert filename to Unicode"); | |
| 308 return; | |
| 309 } | |
| 310 | |
| 311 sz = MultiByteToWideChar(CP_UTF8, 0, target, -1, wtarget, PATH_MAX); | |
| 312 if (sz == 0) | |
| 313 { | |
| 314 pdfapp_error(app, "cannot convert filename to Unicode"); | |
| 315 return; | |
| 316 } | |
| 317 | |
| 318 #if (_WIN32_WINNT >= 0x0500) | |
| 319 ReplaceFile(wtarget, wsource, NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL); | |
| 320 #else | |
| 321 DeleteFile(wtarget); | |
| 322 MoveFile(wsource, wtarget); | |
| 323 #endif | |
| 324 } | |
| 325 | |
| 326 void wincopyfile(pdfapp_t *app, char *source, char *target) | |
| 327 { | |
| 328 wchar_t wsource[PATH_MAX]; | |
| 329 wchar_t wtarget[PATH_MAX]; | |
| 330 | |
| 331 int sz = MultiByteToWideChar(CP_UTF8, 0, source, -1, wsource, PATH_MAX); | |
| 332 if (sz == 0) | |
| 333 { | |
| 334 pdfapp_error(app, "cannot convert filename to Unicode"); | |
| 335 return; | |
| 336 } | |
| 337 | |
| 338 sz = MultiByteToWideChar(CP_UTF8, 0, target, -1, wtarget, PATH_MAX); | |
| 339 if (sz == 0) | |
| 340 { | |
| 341 pdfapp_error(app, "cannot convert filename to Unicode"); | |
| 342 return; | |
| 343 } | |
| 344 | |
| 345 CopyFile(wsource, wtarget, FALSE); | |
| 346 } | |
| 347 | |
| 348 static char pd_password[256] = ""; | |
| 349 static wchar_t pd_passwordw[256] = {0}; | |
| 350 static char td_textinput[1024] = ""; | |
| 351 static int td_retry = 0; | |
| 352 static int cd_nopts; | |
| 353 static int *cd_nvals; | |
| 354 static const char **cd_opts; | |
| 355 static const char **cd_vals; | |
| 356 static int pd_okay = 0; | |
| 357 | |
| 358 static INT_PTR CALLBACK | |
| 359 dlogpassproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |
| 360 { | |
| 361 switch(message) | |
| 362 { | |
| 363 case WM_INITDIALOG: | |
| 364 SetDlgItemTextA(hwnd, 4, "The file is encrypted."); | |
| 365 return TRUE; | |
| 366 case WM_COMMAND: | |
| 367 switch(wParam) | |
| 368 { | |
| 369 case 1: | |
| 370 pd_okay = 1; | |
| 371 GetDlgItemTextW(hwnd, 3, pd_passwordw, nelem(pd_passwordw)); | |
| 372 EndDialog(hwnd, 1); | |
| 373 WideCharToMultiByte(CP_UTF8, 0, pd_passwordw, -1, pd_password, sizeof pd_password, NULL, NULL); | |
| 374 return TRUE; | |
| 375 case 2: | |
| 376 pd_okay = 0; | |
| 377 EndDialog(hwnd, 1); | |
| 378 return TRUE; | |
| 379 } | |
| 380 break; | |
| 381 } | |
| 382 return FALSE; | |
| 383 } | |
| 384 | |
| 385 static INT_PTR CALLBACK | |
| 386 dlogtextproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |
| 387 { | |
| 388 switch(message) | |
| 389 { | |
| 390 case WM_INITDIALOG: | |
| 391 SetDlgItemTextA(hwnd, 3, td_textinput); | |
| 392 if (!td_retry) | |
| 393 ShowWindow(GetDlgItem(hwnd, 4), SW_HIDE); | |
| 394 return TRUE; | |
| 395 case WM_COMMAND: | |
| 396 switch(wParam) | |
| 397 { | |
| 398 case 1: | |
| 399 pd_okay = 1; | |
| 400 GetDlgItemTextA(hwnd, 3, td_textinput, sizeof td_textinput); | |
| 401 EndDialog(hwnd, 1); | |
| 402 return TRUE; | |
| 403 case 2: | |
| 404 pd_okay = 0; | |
| 405 EndDialog(hwnd, 1); | |
| 406 return TRUE; | |
| 407 } | |
| 408 break; | |
| 409 case WM_CTLCOLORSTATIC: | |
| 410 if ((HWND)lParam == GetDlgItem(hwnd, 4)) | |
| 411 { | |
| 412 SetTextColor((HDC)wParam, RGB(255,0,0)); | |
| 413 SetBkMode((HDC)wParam, TRANSPARENT); | |
| 414 | |
| 415 return (INT_PTR)GetStockObject(NULL_BRUSH); | |
| 416 } | |
| 417 break; | |
| 418 } | |
| 419 return FALSE; | |
| 420 } | |
| 421 | |
| 422 static INT_PTR CALLBACK | |
| 423 dlogchoiceproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |
| 424 { | |
| 425 HWND listbox; | |
| 426 int i; | |
| 427 int item; | |
| 428 int sel; | |
| 429 switch(message) | |
| 430 { | |
| 431 case WM_INITDIALOG: | |
| 432 listbox = GetDlgItem(hwnd, 3); | |
| 433 for (i = 0; i < cd_nopts; i++) | |
| 434 SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)cd_opts[i]); | |
| 435 | |
| 436 /* FIXME: handle multiple select */ | |
| 437 if (*cd_nvals > 0) | |
| 438 { | |
| 439 item = SendMessageA(listbox, LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)cd_vals[0]); | |
| 440 if (item != LB_ERR) | |
| 441 SendMessageA(listbox, LB_SETCURSEL, item, 0); | |
| 442 } | |
| 443 return TRUE; | |
| 444 case WM_COMMAND: | |
| 445 switch(wParam) | |
| 446 { | |
| 447 case 1: | |
| 448 listbox = GetDlgItem(hwnd, 3); | |
| 449 *cd_nvals = 0; | |
| 450 for (i = 0; i < cd_nopts; i++) | |
| 451 { | |
| 452 item = SendMessageA(listbox, LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)cd_opts[i]); | |
| 453 sel = SendMessageA(listbox, LB_GETSEL, item, 0); | |
| 454 if (sel && sel != LB_ERR) | |
| 455 cd_vals[(*cd_nvals)++] = cd_opts[i]; | |
| 456 } | |
| 457 pd_okay = 1; | |
| 458 EndDialog(hwnd, 1); | |
| 459 return TRUE; | |
| 460 case 2: | |
| 461 pd_okay = 0; | |
| 462 EndDialog(hwnd, 1); | |
| 463 return TRUE; | |
| 464 } | |
| 465 break; | |
| 466 } | |
| 467 return FALSE; | |
| 468 } | |
| 469 | |
| 470 char *winpassword(pdfapp_t *app, char *filename) | |
| 471 { | |
| 472 int code; | |
| 473 | |
| 474 if (password) | |
| 475 { | |
| 476 char *p = password; | |
| 477 password = NULL; | |
| 478 return p; | |
| 479 } | |
| 480 | |
| 481 code = DialogBoxW(NULL, L"IDD_DLOGPASS", hwndframe, dlogpassproc); | |
| 482 if (code <= 0) | |
| 483 pdfapp_error(app, "cannot create password dialog"); | |
| 484 if (pd_okay) | |
| 485 return pd_password; | |
| 486 return NULL; | |
| 487 } | |
| 488 | |
| 489 char *wintextinput(pdfapp_t *app, char *inittext, int retry) | |
| 490 { | |
| 491 int code; | |
| 492 td_retry = retry; | |
| 493 fz_strlcpy(td_textinput, inittext ? inittext : "", sizeof td_textinput); | |
| 494 code = DialogBoxW(NULL, L"IDD_DLOGTEXT", hwndframe, dlogtextproc); | |
| 495 if (code <= 0) | |
| 496 pdfapp_error(app, "cannot create text input dialog"); | |
| 497 if (pd_okay) | |
| 498 return td_textinput; | |
| 499 return NULL; | |
| 500 } | |
| 501 | |
| 502 int winchoiceinput(pdfapp_t *app, int nopts, const char *opts[], int *nvals, const char *vals[]) | |
| 503 { | |
| 504 int code; | |
| 505 cd_nopts = nopts; | |
| 506 cd_nvals = nvals; | |
| 507 cd_opts = opts; | |
| 508 cd_vals = vals; | |
| 509 code = DialogBoxW(NULL, L"IDD_DLOGLIST", hwndframe, dlogchoiceproc); | |
| 510 if (code <= 0) | |
| 511 pdfapp_error(app, "cannot create text input dialog"); | |
| 512 return pd_okay; | |
| 513 } | |
| 514 | |
| 515 static INT_PTR CALLBACK | |
| 516 dloginfoproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |
| 517 { | |
| 518 char buf[256]; | |
| 519 wchar_t bufx[256]; | |
| 520 fz_context *ctx = gapp.ctx; | |
| 521 fz_document *doc = gapp.doc; | |
| 522 | |
| 523 switch(message) | |
| 524 { | |
| 525 case WM_INITDIALOG: | |
| 526 | |
| 527 SetDlgItemTextW(hwnd, 0x10, wbuf); | |
| 528 | |
| 529 if (fz_lookup_metadata(ctx, doc, FZ_META_FORMAT, buf, sizeof buf) >= 0) | |
| 530 { | |
| 531 SetDlgItemTextA(hwnd, 0x11, buf); | |
| 532 } | |
| 533 else | |
| 534 { | |
| 535 SetDlgItemTextA(hwnd, 0x11, "Unknown"); | |
| 536 SetDlgItemTextA(hwnd, 0x12, "None"); | |
| 537 SetDlgItemTextA(hwnd, 0x13, "n/a"); | |
| 538 return TRUE; | |
| 539 } | |
| 540 | |
| 541 if (fz_lookup_metadata(ctx, doc, FZ_META_ENCRYPTION, buf, sizeof buf) >= 0) | |
| 542 { | |
| 543 SetDlgItemTextA(hwnd, 0x12, buf); | |
| 544 } | |
| 545 else | |
| 546 { | |
| 547 SetDlgItemTextA(hwnd, 0x12, "None"); | |
| 548 } | |
| 549 | |
| 550 buf[0] = 0; | |
| 551 if (fz_has_permission(ctx, doc, FZ_PERMISSION_PRINT)) | |
| 552 strcat(buf, "print, "); | |
| 553 if (fz_has_permission(ctx, doc, FZ_PERMISSION_COPY)) | |
| 554 strcat(buf, "copy, "); | |
| 555 if (fz_has_permission(ctx, doc, FZ_PERMISSION_EDIT)) | |
| 556 strcat(buf, "edit, "); | |
| 557 if (fz_has_permission(ctx, doc, FZ_PERMISSION_ANNOTATE)) | |
| 558 strcat(buf, "annotate, "); | |
| 559 if (strlen(buf) > 2) | |
| 560 buf[strlen(buf)-2] = 0; | |
| 561 else | |
| 562 strcpy(buf, "none"); | |
| 563 SetDlgItemTextA(hwnd, 0x13, buf); | |
| 564 | |
| 565 #define SETUTF8(ID, STRING) \ | |
| 566 if (fz_lookup_metadata(ctx, doc, "info:" STRING, buf, sizeof buf) >= 0) \ | |
| 567 { \ | |
| 568 MultiByteToWideChar(CP_UTF8, 0, buf, -1, bufx, nelem(bufx)); \ | |
| 569 SetDlgItemTextW(hwnd, ID, bufx); \ | |
| 570 } | |
| 571 | |
| 572 SETUTF8(0x20, "Title"); | |
| 573 SETUTF8(0x21, "Author"); | |
| 574 SETUTF8(0x22, "Subject"); | |
| 575 SETUTF8(0x23, "Keywords"); | |
| 576 SETUTF8(0x24, "Creator"); | |
| 577 SETUTF8(0x25, "Producer"); | |
| 578 SETUTF8(0x26, "CreationDate"); | |
| 579 SETUTF8(0x27, "ModDate"); | |
| 580 return TRUE; | |
| 581 | |
| 582 case WM_COMMAND: | |
| 583 EndDialog(hwnd, 1); | |
| 584 return TRUE; | |
| 585 } | |
| 586 return FALSE; | |
| 587 } | |
| 588 | |
| 589 static void info() | |
| 590 { | |
| 591 int code = DialogBoxW(NULL, L"IDD_DLOGINFO", hwndframe, dloginfoproc); | |
| 592 if (code <= 0) | |
| 593 pdfapp_error(&gapp, "cannot create info dialog"); | |
| 594 } | |
| 595 | |
| 596 static INT_PTR CALLBACK | |
| 597 dlogaboutproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |
| 598 { | |
| 599 switch(message) | |
| 600 { | |
| 601 case WM_INITDIALOG: | |
| 602 SetDlgItemTextA(hwnd, 2, pdfapp_version(&gapp)); | |
| 603 SetDlgItemTextA(hwnd, 3, pdfapp_usage(&gapp)); | |
| 604 return TRUE; | |
| 605 case WM_COMMAND: | |
| 606 EndDialog(hwnd, 1); | |
| 607 return TRUE; | |
| 608 } | |
| 609 return FALSE; | |
| 610 } | |
| 611 | |
| 612 void winhelp(pdfapp_t*app) | |
| 613 { | |
| 614 int code = DialogBoxW(NULL, L"IDD_DLOGABOUT", hwndframe, dlogaboutproc); | |
| 615 if (code <= 0) | |
| 616 pdfapp_error(&gapp, "cannot create help dialog"); | |
| 617 } | |
| 618 | |
| 619 /* | |
| 620 * Main window | |
| 621 */ | |
| 622 | |
| 623 static void winopen() | |
| 624 { | |
| 625 WNDCLASS wc; | |
| 626 HMENU menu; | |
| 627 RECT r; | |
| 628 ATOM a; | |
| 629 | |
| 630 /* Create and register window frame class */ | |
| 631 memset(&wc, 0, sizeof(wc)); | |
| 632 wc.style = 0; | |
| 633 wc.lpfnWndProc = frameproc; | |
| 634 wc.cbClsExtra = 0; | |
| 635 wc.cbWndExtra = 0; | |
| 636 wc.hInstance = GetModuleHandle(NULL); | |
| 637 wc.hIcon = LoadIconA(wc.hInstance, "IDI_ICONAPP"); | |
| 638 wc.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW); | |
| 639 wc.hbrBackground = NULL; | |
| 640 wc.lpszMenuName = NULL; | |
| 641 wc.lpszClassName = L"FrameWindow"; | |
| 642 a = RegisterClassW(&wc); | |
| 643 if (!a) | |
| 644 pdfapp_error(&gapp, "cannot register frame window class"); | |
| 645 | |
| 646 /* Create and register window view class */ | |
| 647 memset(&wc, 0, sizeof(wc)); | |
| 648 wc.style = CS_HREDRAW | CS_VREDRAW; | |
| 649 wc.lpfnWndProc = viewproc; | |
| 650 wc.cbClsExtra = 0; | |
| 651 wc.cbWndExtra = 0; | |
| 652 wc.hInstance = GetModuleHandle(NULL); | |
| 653 wc.hIcon = NULL; | |
| 654 wc.hCursor = NULL; | |
| 655 wc.hbrBackground = NULL; | |
| 656 wc.lpszMenuName = NULL; | |
| 657 wc.lpszClassName = L"ViewWindow"; | |
| 658 a = RegisterClassW(&wc); | |
| 659 if (!a) | |
| 660 pdfapp_error(&gapp, "cannot register view window class"); | |
| 661 | |
| 662 /* Get screen size */ | |
| 663 SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); | |
| 664 gapp.scrw = r.right - r.left; | |
| 665 gapp.scrh = r.bottom - r.top; | |
| 666 | |
| 667 /* Create cursors */ | |
| 668 arrowcurs = LoadCursor(NULL, IDC_ARROW); | |
| 669 handcurs = LoadCursor(NULL, IDC_HAND); | |
| 670 waitcurs = LoadCursor(NULL, IDC_WAIT); | |
| 671 caretcurs = LoadCursor(NULL, IDC_IBEAM); | |
| 672 | |
| 673 /* And a background color */ | |
| 674 bgbrush = CreateSolidBrush(RGB(0x70,0x70,0x70)); | |
| 675 | |
| 676 /* Init DIB info for buffer */ | |
| 677 dibinf = malloc(sizeof(BITMAPINFO) + 12); | |
| 678 assert(dibinf); | |
| 679 dibinf->bmiHeader.biSize = sizeof(dibinf->bmiHeader); | |
| 680 dibinf->bmiHeader.biPlanes = 1; | |
| 681 dibinf->bmiHeader.biBitCount = 32; | |
| 682 dibinf->bmiHeader.biCompression = BI_RGB; | |
| 683 dibinf->bmiHeader.biXPelsPerMeter = 2834; | |
| 684 dibinf->bmiHeader.biYPelsPerMeter = 2834; | |
| 685 dibinf->bmiHeader.biClrUsed = 0; | |
| 686 dibinf->bmiHeader.biClrImportant = 0; | |
| 687 dibinf->bmiHeader.biClrUsed = 0; | |
| 688 | |
| 689 /* Create window */ | |
| 690 hwndframe = CreateWindowW(L"FrameWindow", // window class name | |
| 691 NULL, // window caption | |
| 692 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, | |
| 693 CW_USEDEFAULT, CW_USEDEFAULT, // initial position | |
| 694 300, // initial x size | |
| 695 300, // initial y size | |
| 696 0, // parent window handle | |
| 697 0, // window menu handle | |
| 698 0, // program instance handle | |
| 699 0); // creation parameters | |
| 700 if (!hwndframe) | |
| 701 pdfapp_error(&gapp, "cannot create frame"); | |
| 702 | |
| 703 hwndview = CreateWindowW(L"ViewWindow", // window class name | |
| 704 NULL, | |
| 705 WS_VISIBLE | WS_CHILD, | |
| 706 CW_USEDEFAULT, CW_USEDEFAULT, | |
| 707 CW_USEDEFAULT, CW_USEDEFAULT, | |
| 708 hwndframe, 0, 0, 0); | |
| 709 if (!hwndview) | |
| 710 pdfapp_error(&gapp, "cannot create view"); | |
| 711 | |
| 712 hdc = NULL; | |
| 713 | |
| 714 SetWindowTextW(hwndframe, L"MuPDF"); | |
| 715 | |
| 716 menu = GetSystemMenu(hwndframe, 0); | |
| 717 AppendMenuW(menu, MF_SEPARATOR, 0, NULL); | |
| 718 AppendMenuW(menu, MF_STRING, ID_ABOUT, L"About MuPDF..."); | |
| 719 AppendMenuW(menu, MF_STRING, ID_DOCINFO, L"Document Properties..."); | |
| 720 | |
| 721 SetCursor(arrowcurs); | |
| 722 } | |
| 723 | |
| 724 static void | |
| 725 do_close(pdfapp_t *app) | |
| 726 { | |
| 727 fz_context *ctx = app->ctx; | |
| 728 pdfapp_close(app); | |
| 729 free(dibinf); | |
| 730 fz_drop_context(ctx); | |
| 731 } | |
| 732 | |
| 733 void winclose(pdfapp_t *app) | |
| 734 { | |
| 735 if (pdfapp_preclose(app)) | |
| 736 { | |
| 737 do_close(app); | |
| 738 exit(0); | |
| 739 } | |
| 740 } | |
| 741 | |
| 742 void wincursor(pdfapp_t *app, int curs) | |
| 743 { | |
| 744 if (curs == ARROW) | |
| 745 SetCursor(arrowcurs); | |
| 746 if (curs == HAND) | |
| 747 SetCursor(handcurs); | |
| 748 if (curs == WAIT) | |
| 749 SetCursor(waitcurs); | |
| 750 if (curs == CARET) | |
| 751 SetCursor(caretcurs); | |
| 752 } | |
| 753 | |
| 754 int winisresolutionacceptable(pdfapp_t *app, fz_matrix ctm) | |
| 755 { | |
| 756 return 1; | |
| 757 } | |
| 758 | |
| 759 void wintitle(pdfapp_t *app, char *title) | |
| 760 { | |
| 761 wchar_t wide[256], *dp; | |
| 762 char *sp; | |
| 763 int rune; | |
| 764 | |
| 765 dp = wide; | |
| 766 sp = title; | |
| 767 while (*sp && dp < wide + 255) | |
| 768 { | |
| 769 sp += fz_chartorune(&rune, sp); | |
| 770 *dp++ = rune; | |
| 771 } | |
| 772 *dp = 0; | |
| 773 | |
| 774 SetWindowTextW(hwndframe, wide); | |
| 775 } | |
| 776 | |
| 777 static void windrawrect(pdfapp_t *app, int x0, int y0, int x1, int y1) | |
| 778 { | |
| 779 RECT r; | |
| 780 r.left = x0; | |
| 781 r.top = y0; | |
| 782 r.right = x1; | |
| 783 r.bottom = y1; | |
| 784 FillRect(hdc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH)); | |
| 785 } | |
| 786 | |
| 787 void windrawstring(pdfapp_t *app, int x, int y, char *s) | |
| 788 { | |
| 789 HFONT font = (HFONT)GetStockObject(ANSI_FIXED_FONT); | |
| 790 SelectObject(hdc, font); | |
| 791 TextOutA(hdc, x, y - 12, s, (int)strlen(s)); | |
| 792 } | |
| 793 | |
| 794 static void winblitsearch() | |
| 795 { | |
| 796 if (gapp.issearching) | |
| 797 { | |
| 798 char buf[sizeof(gapp.search) + 50]; | |
| 799 sprintf(buf, "Search: %s", gapp.search); | |
| 800 windrawrect(&gapp, 0, 0, gapp.winw, 30); | |
| 801 windrawstring(&gapp, 10, 20, buf); | |
| 802 } | |
| 803 } | |
| 804 | |
| 805 static void winblit() | |
| 806 { | |
| 807 int image_w = fz_pixmap_width(gapp.ctx, gapp.image); | |
| 808 int image_h = fz_pixmap_height(gapp.ctx, gapp.image); | |
| 809 int image_n = fz_pixmap_components(gapp.ctx, gapp.image); | |
| 810 unsigned char *samples = fz_pixmap_samples(gapp.ctx, gapp.image); | |
| 811 int x0 = gapp.panx; | |
| 812 int y0 = gapp.pany; | |
| 813 int x1 = gapp.panx + image_w; | |
| 814 int y1 = gapp.pany + image_h; | |
| 815 RECT r; | |
| 816 HBRUSH brush; | |
| 817 | |
| 818 if (gapp.image) | |
| 819 { | |
| 820 if (gapp.iscopying || justcopied) | |
| 821 { | |
| 822 pdfapp_invert(&gapp, gapp.selr); | |
| 823 justcopied = 1; | |
| 824 } | |
| 825 | |
| 826 pdfapp_inverthit(&gapp); | |
| 827 | |
| 828 dibinf->bmiHeader.biWidth = image_w; | |
| 829 dibinf->bmiHeader.biHeight = -image_h; | |
| 830 dibinf->bmiHeader.biSizeImage = image_h * 4; | |
| 831 | |
| 832 if (image_n == 2) | |
| 833 { | |
| 834 size_t i = image_w * (size_t)image_h; | |
| 835 unsigned char *color = malloc(i*4); | |
| 836 unsigned char *s = samples; | |
| 837 unsigned char *d = color; | |
| 838 for (; i > 0 ; i--) | |
| 839 { | |
| 840 d[2] = d[1] = d[0] = *s++; | |
| 841 d[3] = *s++; | |
| 842 d += 4; | |
| 843 } | |
| 844 SetDIBitsToDevice(hdc, | |
| 845 gapp.panx, gapp.pany, image_w, image_h, | |
| 846 0, 0, 0, image_h, color, | |
| 847 dibinf, DIB_RGB_COLORS); | |
| 848 free(color); | |
| 849 } | |
| 850 if (image_n == 4) | |
| 851 { | |
| 852 SetDIBitsToDevice(hdc, | |
| 853 gapp.panx, gapp.pany, image_w, image_h, | |
| 854 0, 0, 0, image_h, samples, | |
| 855 dibinf, DIB_RGB_COLORS); | |
| 856 } | |
| 857 | |
| 858 pdfapp_inverthit(&gapp); | |
| 859 | |
| 860 if (gapp.iscopying || justcopied) | |
| 861 { | |
| 862 pdfapp_invert(&gapp, gapp.selr); | |
| 863 justcopied = 1; | |
| 864 } | |
| 865 } | |
| 866 | |
| 867 if (gapp.invert) | |
| 868 brush = (HBRUSH)GetStockObject(BLACK_BRUSH); | |
| 869 else | |
| 870 brush = bgbrush; | |
| 871 | |
| 872 /* Grey background */ | |
| 873 r.top = 0; r.bottom = gapp.winh; | |
| 874 r.left = 0; r.right = x0; | |
| 875 FillRect(hdc, &r, brush); | |
| 876 r.left = x1; r.right = gapp.winw; | |
| 877 FillRect(hdc, &r, brush); | |
| 878 r.left = 0; r.right = gapp.winw; | |
| 879 r.top = 0; r.bottom = y0; | |
| 880 FillRect(hdc, &r, brush); | |
| 881 r.top = y1; r.bottom = gapp.winh; | |
| 882 FillRect(hdc, &r, brush); | |
| 883 | |
| 884 winblitsearch(); | |
| 885 } | |
| 886 | |
| 887 void winresize(pdfapp_t *app, int w, int h) | |
| 888 { | |
| 889 ShowWindow(hwndframe, SW_SHOWDEFAULT); | |
| 890 w += GetSystemMetrics(SM_CXFRAME) * 2; | |
| 891 h += GetSystemMetrics(SM_CYFRAME) * 2; | |
| 892 h += GetSystemMetrics(SM_CYCAPTION); | |
| 893 SetWindowPos(hwndframe, 0, 0, 0, w, h, SWP_NOZORDER | SWP_NOMOVE); | |
| 894 } | |
| 895 | |
| 896 void winrepaint(pdfapp_t *app) | |
| 897 { | |
| 898 InvalidateRect(hwndview, NULL, 0); | |
| 899 } | |
| 900 | |
| 901 void winrepaintsearch(pdfapp_t *app) | |
| 902 { | |
| 903 // TODO: invalidate only search area and | |
| 904 // call only search redraw routine. | |
| 905 InvalidateRect(hwndview, NULL, 0); | |
| 906 } | |
| 907 | |
| 908 void winfullscreen(pdfapp_t *app, int state) | |
| 909 { | |
| 910 static WINDOWPLACEMENT savedplace; | |
| 911 static int isfullscreen = 0; | |
| 912 if (state && !isfullscreen) | |
| 913 { | |
| 914 GetWindowPlacement(hwndframe, &savedplace); | |
| 915 SetWindowLong(hwndframe, GWL_STYLE, WS_POPUP | WS_VISIBLE); | |
| 916 SetWindowPos(hwndframe, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); | |
| 917 ShowWindow(hwndframe, SW_SHOWMAXIMIZED); | |
| 918 isfullscreen = 1; | |
| 919 } | |
| 920 if (!state && isfullscreen) | |
| 921 { | |
| 922 SetWindowLong(hwndframe, GWL_STYLE, WS_OVERLAPPEDWINDOW); | |
| 923 SetWindowPos(hwndframe, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); | |
| 924 SetWindowPlacement(hwndframe, &savedplace); | |
| 925 isfullscreen = 0; | |
| 926 } | |
| 927 } | |
| 928 | |
| 929 /* | |
| 930 * Event handling | |
| 931 */ | |
| 932 | |
| 933 void windocopy(pdfapp_t *app) | |
| 934 { | |
| 935 HGLOBAL handle; | |
| 936 unsigned short *ucsbuf; | |
| 937 | |
| 938 if (!OpenClipboard(hwndframe)) | |
| 939 return; | |
| 940 EmptyClipboard(); | |
| 941 | |
| 942 handle = GlobalAlloc(GMEM_MOVEABLE, 4096 * sizeof(unsigned short)); | |
| 943 if (!handle) | |
| 944 { | |
| 945 CloseClipboard(); | |
| 946 return; | |
| 947 } | |
| 948 | |
| 949 ucsbuf = GlobalLock(handle); | |
| 950 pdfapp_oncopy(&gapp, ucsbuf, 4096); | |
| 951 GlobalUnlock(handle); | |
| 952 | |
| 953 SetClipboardData(CF_UNICODETEXT, handle); | |
| 954 CloseClipboard(); | |
| 955 | |
| 956 justcopied = 1; /* keep inversion around for a while... */ | |
| 957 } | |
| 958 | |
| 959 void winreloadpage(pdfapp_t *app) | |
| 960 { | |
| 961 SendMessage(hwndview, WM_APP, 0, 0); | |
| 962 } | |
| 963 | |
| 964 void winopenuri(pdfapp_t *app, char *buf) | |
| 965 { | |
| 966 ShellExecuteA(hwndframe, "open", buf, 0, 0, SW_SHOWNORMAL); | |
| 967 } | |
| 968 | |
| 969 #define OUR_TIMER_ID 1 | |
| 970 | |
| 971 void winadvancetimer(pdfapp_t *app, float delay) | |
| 972 { | |
| 973 timer_pending = 1; | |
| 974 SetTimer(hwndview, OUR_TIMER_ID, (unsigned int)(1000*delay), NULL); | |
| 975 } | |
| 976 | |
| 977 static void killtimer(pdfapp_t *app) | |
| 978 { | |
| 979 timer_pending = 0; | |
| 980 } | |
| 981 | |
| 982 static void handlekey(int c) | |
| 983 { | |
| 984 int modifier = (GetAsyncKeyState(VK_SHIFT) < 0); | |
| 985 modifier |= ((GetAsyncKeyState(VK_CONTROL) < 0)<<2); | |
| 986 modifier |= ((GetAsyncKeyState(VK_MENU) < 0)<<3); | |
| 987 | |
| 988 if (timer_pending) | |
| 989 killtimer(&gapp); | |
| 990 | |
| 991 if (GetCapture() == hwndview) | |
| 992 return; | |
| 993 | |
| 994 if (justcopied) | |
| 995 { | |
| 996 justcopied = 0; | |
| 997 winrepaint(&gapp); | |
| 998 } | |
| 999 | |
| 1000 /* translate VK into ASCII equivalents */ | |
| 1001 if (c > 256) | |
| 1002 { | |
| 1003 switch (c - 256) | |
| 1004 { | |
| 1005 case VK_ESCAPE: c = '\033'; break; | |
| 1006 case VK_DOWN: c = 'j'; break; | |
| 1007 case VK_UP: c = 'k'; break; | |
| 1008 case VK_LEFT: c = 'h'; break; | |
| 1009 case VK_RIGHT: c = 'l'; break; | |
| 1010 case VK_PRIOR: c = ','; break; | |
| 1011 case VK_NEXT: c = '.'; break; | |
| 1012 } | |
| 1013 } | |
| 1014 | |
| 1015 pdfapp_onkey(&gapp, c, modifier); | |
| 1016 winrepaint(&gapp); | |
| 1017 } | |
| 1018 | |
| 1019 static void handlemouse(int x, int y, int btn, int state) | |
| 1020 { | |
| 1021 int modifier = (GetAsyncKeyState(VK_SHIFT) < 0); | |
| 1022 modifier |= ((GetAsyncKeyState(VK_CONTROL) < 0)<<2); | |
| 1023 | |
| 1024 if (state != 0 && timer_pending) | |
| 1025 killtimer(&gapp); | |
| 1026 | |
| 1027 if (state != 0 && justcopied) | |
| 1028 { | |
| 1029 justcopied = 0; | |
| 1030 winrepaint(&gapp); | |
| 1031 } | |
| 1032 | |
| 1033 if (state == 1) | |
| 1034 SetCapture(hwndview); | |
| 1035 if (state == -1) | |
| 1036 ReleaseCapture(); | |
| 1037 | |
| 1038 pdfapp_onmouse(&gapp, x, y, btn, modifier, state); | |
| 1039 } | |
| 1040 | |
| 1041 static LRESULT CALLBACK | |
| 1042 frameproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |
| 1043 { | |
| 1044 switch(message) | |
| 1045 { | |
| 1046 case WM_SETFOCUS: | |
| 1047 PostMessage(hwnd, WM_APP+5, 0, 0); | |
| 1048 return 0; | |
| 1049 case WM_APP+5: | |
| 1050 SetFocus(hwndview); | |
| 1051 return 0; | |
| 1052 | |
| 1053 case WM_DESTROY: | |
| 1054 PostQuitMessage(0); | |
| 1055 return 0; | |
| 1056 | |
| 1057 case WM_SYSCOMMAND: | |
| 1058 if (wParam == ID_ABOUT) | |
| 1059 { | |
| 1060 winhelp(&gapp); | |
| 1061 return 0; | |
| 1062 } | |
| 1063 if (wParam == ID_DOCINFO) | |
| 1064 { | |
| 1065 info(); | |
| 1066 return 0; | |
| 1067 } | |
| 1068 break; | |
| 1069 | |
| 1070 case WM_SIZE: | |
| 1071 { | |
| 1072 // More generally, you should use GetEffectiveClientRect | |
| 1073 // if you have a toolbar etc. | |
| 1074 RECT rect; | |
| 1075 GetClientRect(hwnd, &rect); | |
| 1076 MoveWindow(hwndview, rect.left, rect.top, | |
| 1077 rect.right-rect.left, rect.bottom-rect.top, TRUE); | |
| 1078 if (wParam == SIZE_MAXIMIZED) | |
| 1079 gapp.shrinkwrap = 0; | |
| 1080 return 0; | |
| 1081 } | |
| 1082 | |
| 1083 case WM_SIZING: | |
| 1084 gapp.shrinkwrap = 0; | |
| 1085 break; | |
| 1086 | |
| 1087 case WM_NOTIFY: | |
| 1088 case WM_COMMAND: | |
| 1089 return SendMessage(hwndview, message, wParam, lParam); | |
| 1090 | |
| 1091 case WM_CLOSE: | |
| 1092 if (!pdfapp_preclose(&gapp)) | |
| 1093 return 0; | |
| 1094 } | |
| 1095 | |
| 1096 return DefWindowProc(hwnd, message, wParam, lParam); | |
| 1097 } | |
| 1098 | |
| 1099 static LRESULT CALLBACK | |
| 1100 viewproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |
| 1101 { | |
| 1102 static int oldx = 0; | |
| 1103 static int oldy = 0; | |
| 1104 int x = (signed short) LOWORD(lParam); | |
| 1105 int y = (signed short) HIWORD(lParam); | |
| 1106 | |
| 1107 switch (message) | |
| 1108 { | |
| 1109 case WM_SIZE: | |
| 1110 if (wParam == SIZE_MINIMIZED) | |
| 1111 return 0; | |
| 1112 if (wParam == SIZE_MAXIMIZED) | |
| 1113 gapp.shrinkwrap = 0; | |
| 1114 pdfapp_onresize(&gapp, LOWORD(lParam), HIWORD(lParam)); | |
| 1115 break; | |
| 1116 | |
| 1117 /* Paint events are low priority and automagically catenated | |
| 1118 * so we don't need to do any fancy waiting to defer repainting. | |
| 1119 */ | |
| 1120 case WM_PAINT: | |
| 1121 { | |
| 1122 //puts("WM_PAINT"); | |
| 1123 PAINTSTRUCT ps; | |
| 1124 hdc = BeginPaint(hwnd, &ps); | |
| 1125 winblit(); | |
| 1126 hdc = NULL; | |
| 1127 EndPaint(hwnd, &ps); | |
| 1128 pdfapp_postblit(&gapp); | |
| 1129 return 0; | |
| 1130 } | |
| 1131 | |
| 1132 case WM_ERASEBKGND: | |
| 1133 return 1; // well, we don't need to erase to redraw cleanly | |
| 1134 | |
| 1135 /* Mouse events */ | |
| 1136 | |
| 1137 case WM_LBUTTONDOWN: | |
| 1138 SetFocus(hwndview); | |
| 1139 oldx = x; oldy = y; | |
| 1140 handlemouse(x, y, 1, 1); | |
| 1141 return 0; | |
| 1142 case WM_MBUTTONDOWN: | |
| 1143 SetFocus(hwndview); | |
| 1144 oldx = x; oldy = y; | |
| 1145 handlemouse(x, y, 2, 1); | |
| 1146 return 0; | |
| 1147 case WM_RBUTTONDOWN: | |
| 1148 SetFocus(hwndview); | |
| 1149 oldx = x; oldy = y; | |
| 1150 handlemouse(x, y, 3, 1); | |
| 1151 return 0; | |
| 1152 | |
| 1153 case WM_LBUTTONUP: | |
| 1154 oldx = x; oldy = y; | |
| 1155 handlemouse(x, y, 1, -1); | |
| 1156 return 0; | |
| 1157 case WM_MBUTTONUP: | |
| 1158 oldx = x; oldy = y; | |
| 1159 handlemouse(x, y, 2, -1); | |
| 1160 return 0; | |
| 1161 case WM_RBUTTONUP: | |
| 1162 oldx = x; oldy = y; | |
| 1163 handlemouse(x, y, 3, -1); | |
| 1164 return 0; | |
| 1165 | |
| 1166 case WM_MOUSEMOVE: | |
| 1167 oldx = x; oldy = y; | |
| 1168 handlemouse(x, y, 0, 0); | |
| 1169 return 0; | |
| 1170 | |
| 1171 /* Mouse wheel */ | |
| 1172 | |
| 1173 case WM_MOUSEWHEEL: | |
| 1174 if ((signed short)HIWORD(wParam) <= 0) | |
| 1175 { | |
| 1176 handlemouse(oldx, oldy, 5, 1); | |
| 1177 handlemouse(oldx, oldy, 5, -1); | |
| 1178 } | |
| 1179 else | |
| 1180 { | |
| 1181 handlemouse(oldx, oldy, 4, 1); | |
| 1182 handlemouse(oldx, oldy, 4, -1); | |
| 1183 } | |
| 1184 return 0; | |
| 1185 | |
| 1186 /* Timer */ | |
| 1187 case WM_TIMER: | |
| 1188 if (wParam == OUR_TIMER_ID && timer_pending && gapp.presentation_mode) | |
| 1189 { | |
| 1190 timer_pending = 0; | |
| 1191 handlekey(VK_RIGHT + 256); | |
| 1192 handlemouse(oldx, oldy, 0, 0); /* update cursor */ | |
| 1193 return 0; | |
| 1194 } | |
| 1195 break; | |
| 1196 | |
| 1197 /* Keyboard events */ | |
| 1198 | |
| 1199 case WM_KEYDOWN: | |
| 1200 /* only handle special keys */ | |
| 1201 switch (wParam) | |
| 1202 { | |
| 1203 case VK_F1: | |
| 1204 winhelp(&gapp); | |
| 1205 return 0; | |
| 1206 case VK_LEFT: | |
| 1207 case VK_UP: | |
| 1208 case VK_PRIOR: | |
| 1209 case VK_RIGHT: | |
| 1210 case VK_DOWN: | |
| 1211 case VK_NEXT: | |
| 1212 case VK_ESCAPE: | |
| 1213 handlekey(wParam + 256); | |
| 1214 handlemouse(oldx, oldy, 0, 0); /* update cursor */ | |
| 1215 return 0; | |
| 1216 } | |
| 1217 return 1; | |
| 1218 | |
| 1219 case WM_SYSKEYDOWN: | |
| 1220 /* alt keys */ | |
| 1221 switch (wParam) | |
| 1222 { | |
| 1223 case VK_LEFT: | |
| 1224 case VK_RIGHT: | |
| 1225 handlekey(wParam + 256); | |
| 1226 handlemouse(oldx, oldy, 0, 0); /* update cursor */ | |
| 1227 return 0; | |
| 1228 } | |
| 1229 break; | |
| 1230 | |
| 1231 /* unicode encoded chars, including escape, backspace etc... */ | |
| 1232 case WM_CHAR: | |
| 1233 if (wParam < 256) | |
| 1234 { | |
| 1235 handlekey(wParam); | |
| 1236 handlemouse(oldx, oldy, 0, 0); /* update cursor */ | |
| 1237 } | |
| 1238 return 0; | |
| 1239 | |
| 1240 /* We use WM_APP to trigger a reload and repaint of a page */ | |
| 1241 case WM_APP: | |
| 1242 pdfapp_reloadpage(&gapp); | |
| 1243 break; | |
| 1244 } | |
| 1245 | |
| 1246 fflush(stdout); | |
| 1247 | |
| 1248 /* Pass on unhandled events to Windows */ | |
| 1249 return DefWindowProc(hwnd, message, wParam, lParam); | |
| 1250 } | |
| 1251 | |
| 1252 typedef BOOL (SetProcessDPIAwareFn)(void); | |
| 1253 | |
| 1254 static int | |
| 1255 get_system_dpi(void) | |
| 1256 { | |
| 1257 HMODULE hUser32 = LoadLibrary(TEXT("user32.dll")); | |
| 1258 SetProcessDPIAwareFn *ptr; | |
| 1259 int hdpi, vdpi; | |
| 1260 HDC desktopDC; | |
| 1261 | |
| 1262 ptr = (SetProcessDPIAwareFn *)GetProcAddress(hUser32, "SetProcessDPIAware"); | |
| 1263 if (ptr != NULL) | |
| 1264 ptr(); | |
| 1265 FreeLibrary(hUser32); | |
| 1266 | |
| 1267 desktopDC = GetDC(NULL); | |
| 1268 hdpi = GetDeviceCaps(desktopDC, LOGPIXELSX); | |
| 1269 vdpi = GetDeviceCaps(desktopDC, LOGPIXELSY); | |
| 1270 /* hdpi,vdpi = 100 means 96dpi. */ | |
| 1271 return ((hdpi + vdpi) * 96 + 0.5f) / 200; | |
| 1272 } | |
| 1273 | |
| 1274 static void usage(const char *argv0) | |
| 1275 { | |
| 1276 const char *msg = | |
| 1277 "usage: mupdf [options] file.pdf [page]\n" | |
| 1278 "\t-p -\tpassword\n" | |
| 1279 "\t-r -\tresolution\n" | |
| 1280 "\t-A -\tset anti-aliasing quality in bits (0=off, 8=best)\n" | |
| 1281 "\t-C -\tRRGGBB (tint color in hexadecimal syntax)\n" | |
| 1282 "\t-W -\tpage width for EPUB layout\n" | |
| 1283 "\t-H -\tpage height for EPUB layout\n" | |
| 1284 "\t-I -\tinvert colors\n" | |
| 1285 "\t-S -\tfont size for EPUB layout\n" | |
| 1286 "\t-U -\tuser style sheet for EPUB layout\n" | |
| 1287 "\t-X\tdisable document styles for EPUB layout\n"; | |
| 1288 MessageBoxA(NULL, msg, "MuPDF: Usage", MB_OK); | |
| 1289 exit(1); | |
| 1290 } | |
| 1291 | |
| 1292 int WINAPI | |
| 1293 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) | |
| 1294 { | |
| 1295 int argc; | |
| 1296 LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc); | |
| 1297 char **argv; | |
| 1298 char argv0[256]; | |
| 1299 MSG msg; | |
| 1300 int code; | |
| 1301 fz_context *ctx; | |
| 1302 char *profile_name = NULL; | |
| 1303 int kbps = 0; | |
| 1304 int displayRes = get_system_dpi(); | |
| 1305 int c; | |
| 1306 | |
| 1307 ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT); | |
| 1308 if (!ctx) | |
| 1309 { | |
| 1310 MessageBoxA(NULL, "Cannot initialize MuPDF context.", "MuPDF: Error", MB_OK); | |
| 1311 exit(1); | |
| 1312 } | |
| 1313 | |
| 1314 /* stderr goes nowhere. Get us a debug stream we have a chance | |
| 1315 * of seeing. */ | |
| 1316 fz_set_stddbg(ctx, fz_stdods(ctx)); | |
| 1317 | |
| 1318 pdfapp_init(ctx, &gapp); | |
| 1319 | |
| 1320 argv = fz_argv_from_wargv(argc, wargv); | |
| 1321 | |
| 1322 while ((c = fz_getopt(argc, argv, "Ip:r:A:C:W:H:S:U:Xb:c:")) != -1) | |
| 1323 { | |
| 1324 switch (c) | |
| 1325 { | |
| 1326 case 'C': | |
| 1327 c = strtol(fz_optarg, NULL, 16); | |
| 1328 gapp.tint = 1; | |
| 1329 gapp.tint_white = c; | |
| 1330 break; | |
| 1331 case 'p': password = fz_optarg; break; | |
| 1332 case 'r': displayRes = fz_atoi(fz_optarg); break; | |
| 1333 case 'I': gapp.invert = 1; break; | |
| 1334 case 'A': fz_set_aa_level(ctx, fz_atoi(fz_optarg)); break; | |
| 1335 case 'c': profile_name = fz_optarg; break; | |
| 1336 case 'W': gapp.layout_w = fz_atoi(fz_optarg); break; | |
| 1337 case 'H': gapp.layout_h = fz_atoi(fz_optarg); break; | |
| 1338 case 'S': gapp.layout_em = fz_atoi(fz_optarg); break; | |
| 1339 case 'b': kbps = fz_atoi(fz_optarg); break; | |
| 1340 case 'U': gapp.layout_css = fz_optarg; break; | |
| 1341 case 'X': gapp.layout_use_doc_css = 0; break; | |
| 1342 default: usage(argv[0]); | |
| 1343 } | |
| 1344 } | |
| 1345 | |
| 1346 pdfapp_setresolution(&gapp, displayRes); | |
| 1347 | |
| 1348 GetModuleFileNameA(NULL, argv0, sizeof argv0); | |
| 1349 install_app(argv0); | |
| 1350 | |
| 1351 winopen(); | |
| 1352 | |
| 1353 if (fz_optind < argc) | |
| 1354 { | |
| 1355 fz_strlcpy(filename, argv[fz_optind++], sizeof filename); | |
| 1356 } | |
| 1357 else | |
| 1358 { | |
| 1359 if (!winfilename(wbuf, nelem(wbuf))) | |
| 1360 exit(0); | |
| 1361 code = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, filename, sizeof filename, NULL, NULL); | |
| 1362 if (code == 0) | |
| 1363 pdfapp_error(&gapp, "cannot convert filename to utf-8"); | |
| 1364 } | |
| 1365 | |
| 1366 if (fz_optind < argc) | |
| 1367 gapp.pageno = atoi(argv[fz_optind++]); | |
| 1368 | |
| 1369 if (profile_name) | |
| 1370 pdfapp_load_profile(&gapp, profile_name); | |
| 1371 | |
| 1372 if (kbps) | |
| 1373 pdfapp_open_progressive(&gapp, filename, 0, kbps); | |
| 1374 else | |
| 1375 pdfapp_open(&gapp, filename, 0); | |
| 1376 | |
| 1377 while (GetMessage(&msg, NULL, 0, 0)) | |
| 1378 { | |
| 1379 TranslateMessage(&msg); | |
| 1380 DispatchMessage(&msg); | |
| 1381 } | |
| 1382 | |
| 1383 fz_free_argv(argc, argv); | |
| 1384 | |
| 1385 do_close(&gapp); | |
| 1386 | |
| 1387 return 0; | |
| 1388 } |
