Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. #include "fitz.h"
  2. #include "mupdf.h"
  3. #include "muxps.h"
  4. #include "pdfapp.h"
  5.  
  6. #ifndef UNICODE
  7. #define UNICODE
  8. #endif
  9. #ifndef _UNICODE
  10. #define _UNICODE
  11. #endif
  12. #define WIN32_LEAN_AND_MEAN
  13. #include <windows.h>
  14. #include <commdlg.h>
  15. #include <shellapi.h>
  16.  
  17. #ifndef WM_MOUSEWHEEL
  18. #define WM_MOUSEWHEEL 0x020A
  19. #endif
  20.  
  21. #define ID_ABOUT        0x1000
  22. #define ID_DOCINFO      0x1001
  23.  
  24. static HWND hwndframe = NULL;
  25. static HWND hwndview = NULL;
  26. static HDC hdc;
  27. static HBRUSH bgbrush;
  28. static HBRUSH shbrush;
  29. static BITMAPINFO *dibinf;
  30. static HCURSOR arrowcurs, handcurs, waitcurs;
  31. static LRESULT CALLBACK frameproc(HWND, UINT, WPARAM, LPARAM);
  32. static LRESULT CALLBACK viewproc(HWND, UINT, WPARAM, LPARAM);
  33.  
  34. static int justcopied = 0;
  35.  
  36. static pdfapp_t gapp;
  37.  
  38. static wchar_t wbuf[1024];
  39. static char filename[1024];
  40.  
  41. /*
  42.  * Create registry keys to associate MuPDF with PDF and XPS files.
  43.  */
  44.  
  45. #define OPEN_KEY(parent, name, ptr) \
  46.         RegCreateKeyExA(parent, name, 0, 0, 0, KEY_WRITE, 0, &ptr, 0)
  47.  
  48. #define SET_KEY(parent, name, value) \
  49.         RegSetValueExA(parent, name, 0, REG_SZ, value, strlen(value) + 1)
  50.  
  51. void install_app(char *argv0)
  52. {
  53.         char buf[512];
  54.         HKEY software, classes, mupdf, dotpdf, dotxps;
  55.         HKEY shell, open, command, supported_types;
  56.         HKEY pdf_progids, xps_progids;
  57.  
  58.         OPEN_KEY(HKEY_CURRENT_USER, "Software", software);
  59.         OPEN_KEY(software, "Classes", classes);
  60.         OPEN_KEY(classes, ".pdf", dotpdf);
  61.         OPEN_KEY(dotpdf, "OpenWithProgids", pdf_progids);
  62.         OPEN_KEY(classes, ".xps", dotxps);
  63.         OPEN_KEY(dotxps, "OpenWithProgids", xps_progids);
  64.         OPEN_KEY(classes, "MuPDF", mupdf);
  65.         OPEN_KEY(mupdf, "SupportedTypes", supported_types);
  66.         OPEN_KEY(mupdf, "shell", shell);
  67.         OPEN_KEY(shell, "open", open);
  68.         OPEN_KEY(open, "command", command);
  69.  
  70.         sprintf(buf, "\"%s\" \"%%1\"", argv0);
  71.  
  72.         SET_KEY(open, "FriendlyAppName", "MuPDF");
  73.         SET_KEY(command, "", buf);
  74.         SET_KEY(supported_types, ".pdf", "");
  75.         SET_KEY(supported_types, ".xps", "");
  76.         SET_KEY(pdf_progids, "MuPDF", "");
  77.         SET_KEY(xps_progids, "MuPDF", "");
  78.  
  79.         RegCloseKey(dotxps);
  80.         RegCloseKey(dotpdf);
  81.         RegCloseKey(mupdf);
  82.         RegCloseKey(classes);
  83.         RegCloseKey(software);
  84. }
  85.  
  86. /*
  87.  * Dialog boxes
  88.  */
  89.  
  90. void winwarn(pdfapp_t *app, char *msg)
  91. {
  92.         MessageBoxA(hwndframe, msg, "MuPDF: Warning", MB_ICONWARNING);
  93. }
  94.  
  95. void winerror(pdfapp_t *app, fz_error error)
  96. {
  97.         char msgbuf[160 * 30];
  98.         int i;
  99.  
  100.         /* TODO: redirect stderr to a log file and display here */
  101.         fz_catch(error, "displaying error message to user");
  102.  
  103.         fz_strlcpy(msgbuf, "An error has occurred.\n\n", sizeof msgbuf);
  104.         for (i = 0; i < fz_get_error_count(); i++)
  105.         {
  106.                 fz_strlcat(msgbuf, fz_get_error_line(i), sizeof msgbuf);
  107.                 fz_strlcat(msgbuf, "\n", sizeof msgbuf);
  108.         }
  109.  
  110.         MessageBoxA(hwndframe, msgbuf, "MuPDF: Error", MB_ICONERROR);
  111.         exit(1);
  112. }
  113.  
  114. void win32error(char *msg)
  115. {
  116.         LPSTR buf;
  117.         int code = GetLastError();
  118.         FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  119.                 FORMAT_MESSAGE_FROM_SYSTEM |
  120.                 FORMAT_MESSAGE_IGNORE_INSERTS,
  121.                 NULL,
  122.                 code,
  123.                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  124.                 (LPSTR)&buf, 0, NULL);
  125.         winerror(&gapp, fz_throw("%s:\n%s", msg, buf));
  126. }
  127.  
  128. int winfilename(wchar_t *buf, int len)
  129. {
  130.         OPENFILENAME ofn;
  131.         buf[0] = 0;
  132.         memset(&ofn, 0, sizeof(OPENFILENAME));
  133.         ofn.lStructSize = sizeof(OPENFILENAME);
  134.         ofn.hwndOwner = hwndframe;
  135.         ofn.lpstrFile = buf;
  136.         ofn.nMaxFile = len;
  137.         ofn.lpstrInitialDir = NULL;
  138.         ofn.lpstrTitle = L"MuPDF: Open PDF file";
  139.         ofn.lpstrFilter = L"Documents (*.pdf;*.xps)\0*.xps;*.pdf\0PDF Files (*.pdf)\0*.pdf\0XPS Files (*.xps)\0*.xps\0All Files\0*\0\0";
  140.         ofn.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
  141.         return GetOpenFileNameW(&ofn);
  142. }
  143.  
  144. static char pd_filename[256] = "The file is encrypted.";
  145. static char pd_password[256] = "";
  146. static int pd_okay = 0;
  147.  
  148. INT CALLBACK
  149. dlogpassproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  150. {
  151.         switch(message)
  152.         {
  153.         case WM_INITDIALOG:
  154.                 SetDlgItemTextA(hwnd, 4, pd_filename);
  155.                 return TRUE;
  156.         case WM_COMMAND:
  157.                 switch(wParam)
  158.                 {
  159.                 case 1:
  160.                         pd_okay = 1;
  161.                         GetDlgItemTextA(hwnd, 3, pd_password, sizeof pd_password);
  162.                         EndDialog(hwnd, 1);
  163.                         return TRUE;
  164.                 case 2:
  165.                         pd_okay = 0;
  166.                         EndDialog(hwnd, 1);
  167.                         return TRUE;
  168.                 }
  169.                 break;
  170.         }
  171.         return FALSE;
  172. }
  173.  
  174. char *winpassword(pdfapp_t *app, char *filename)
  175. {
  176.         char buf[1024], *s;
  177.         int code;
  178.         strcpy(buf, filename);
  179.         s = buf;
  180.         if (strrchr(s, '\\')) s = strrchr(s, '\\') + 1;
  181.         if (strrchr(s, '/')) s = strrchr(s, '/') + 1;
  182.         if (strlen(s) > 32)
  183.                 strcpy(s + 30, "...");
  184.         sprintf(pd_filename, "The file \"%s\" is encrypted.", s);
  185.         code = DialogBoxW(NULL, L"IDD_DLOGPASS", hwndframe, dlogpassproc);
  186.         if (code <= 0)
  187.                 win32error("cannot create password dialog");
  188.         if (pd_okay)
  189.                 return pd_password;
  190.         return NULL;
  191. }
  192.  
  193. INT CALLBACK
  194. dloginfoproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  195. {
  196.         char buf[256];
  197.         pdf_xref *xref = gapp.xref;
  198.         fz_obj *info, *obj;
  199.  
  200.         switch(message)
  201.         {
  202.         case WM_INITDIALOG:
  203.  
  204.                 SetDlgItemTextW(hwnd, 0x10, wbuf);
  205.  
  206.                 if (!xref)
  207.                 {
  208.                         SetDlgItemTextA(hwnd, 0x11, "XPS");
  209.                         SetDlgItemTextA(hwnd, 0x12, "None");
  210.                         SetDlgItemTextA(hwnd, 0x13, "n/a");
  211.                         return TRUE;
  212.                 }
  213.  
  214.                 sprintf(buf, "PDF %d.%d", xref->version / 10, xref->version % 10);
  215.                 SetDlgItemTextA(hwnd, 0x11, buf);
  216.  
  217.                 if (xref->crypt)
  218.                 {
  219.                         sprintf(buf, "Standard V%d %d-bit %s", pdf_get_crypt_revision(xref),
  220.                                 pdf_get_crypt_length(xref), pdf_get_crypt_method(xref));
  221.                         SetDlgItemTextA(hwnd, 0x12, buf);
  222.                         strcpy(buf, "");
  223.                         if (pdf_has_permission(xref, PDF_PERM_PRINT))
  224.                                 strcat(buf, "print, ");
  225.                         if (pdf_has_permission(xref, PDF_PERM_CHANGE))
  226.                                 strcat(buf, "modify, ");
  227.                         if (pdf_has_permission(xref, PDF_PERM_COPY))
  228.                                 strcat(buf, "copy, ");
  229.                         if (pdf_has_permission(xref, PDF_PERM_NOTES))
  230.                                 strcat(buf, "annotate, ");
  231.                         if (strlen(buf) > 2)
  232.                                 buf[strlen(buf)-2] = 0;
  233.                         else
  234.                                 strcpy(buf, "none");
  235.                         SetDlgItemTextA(hwnd, 0x13, buf);
  236.                 }
  237.                 else
  238.                 {
  239.                         SetDlgItemTextA(hwnd, 0x12, "None");
  240.                         SetDlgItemTextA(hwnd, 0x13, "n/a");
  241.                 }
  242.  
  243.                 info = fz_dict_gets(xref->trailer, "Info");
  244.                 if (!info)
  245.                         return TRUE;
  246.  
  247. #define SETUCS(ID) \
  248.                 { \
  249.                         unsigned short *ucs; \
  250.                         ucs = pdf_to_ucs2(obj); \
  251.                         SetDlgItemTextW(hwnd, ID, ucs); \
  252.                         fz_free(ucs); \
  253.                 }
  254.  
  255.                 if ((obj = fz_dict_gets(info, "Title")))
  256.                         SETUCS(0x20);
  257.                 if ((obj = fz_dict_gets(info, "Author")))
  258.                         SETUCS(0x21);
  259.                 if ((obj = fz_dict_gets(info, "Subject")))
  260.                         SETUCS(0x22);
  261.                 if ((obj = fz_dict_gets(info, "Keywords")))
  262.                         SETUCS(0x23);
  263.                 if ((obj = fz_dict_gets(info, "Creator")))
  264.                         SETUCS(0x24);
  265.                 if ((obj = fz_dict_gets(info, "Producer")))
  266.                         SETUCS(0x25);
  267.                 if ((obj = fz_dict_gets(info, "CreationDate")))
  268.                         SETUCS(0x26);
  269.                 if ((obj = fz_dict_gets(info, "ModDate")))
  270.                         SETUCS(0x27);
  271.                 return TRUE;
  272.  
  273.         case WM_COMMAND:
  274.                 EndDialog(hwnd, 1);
  275.                 return TRUE;
  276.         }
  277.         return FALSE;
  278. }
  279.  
  280. void info()
  281. {
  282.         int code = DialogBoxW(NULL, L"IDD_DLOGINFO", hwndframe, dloginfoproc);
  283.         if (code <= 0)
  284.                 win32error("cannot create info dialog");
  285. }
  286.  
  287. INT CALLBACK
  288. dlogaboutproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  289. {
  290.         switch(message)
  291.         {
  292.         case WM_INITDIALOG:
  293.                 SetDlgItemTextA(hwnd, 2, pdfapp_version(&gapp));
  294.                 SetDlgItemTextA(hwnd, 3, pdfapp_usage(&gapp));
  295.                 return TRUE;
  296.         case WM_COMMAND:
  297.                 EndDialog(hwnd, 1);
  298.                 return TRUE;
  299.         }
  300.         return FALSE;
  301. }
  302.  
  303. void winhelp(pdfapp_t*app)
  304. {
  305.         int code = DialogBoxW(NULL, L"IDD_DLOGABOUT", hwndframe, dlogaboutproc);
  306.         if (code <= 0)
  307.                 win32error("cannot create help dialog");
  308. }
  309.  
  310. /*
  311.  * Main window
  312.  */
  313.  
  314. void winopen()
  315. {
  316.         WNDCLASS wc;
  317.         HMENU menu;
  318.         RECT r;
  319.         ATOM a;
  320.  
  321.         /* Create and register window frame class */
  322.         memset(&wc, 0, sizeof(wc));
  323.         wc.style = 0;
  324.         wc.lpfnWndProc = frameproc;
  325.         wc.cbClsExtra = 0;
  326.         wc.cbWndExtra = 0;
  327.         wc.hInstance = GetModuleHandle(NULL);
  328.         wc.hIcon = LoadIconA(wc.hInstance, "IDI_ICONAPP");
  329.         wc.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW);
  330.         wc.hbrBackground = NULL;
  331.         wc.lpszMenuName = NULL;
  332.         wc.lpszClassName = L"FrameWindow";
  333.         a = RegisterClassW(&wc);
  334.         if (!a)
  335.                 win32error("cannot register frame window class");
  336.  
  337.         /* Create and register window view class */
  338.         memset(&wc, 0, sizeof(wc));
  339.         wc.style = CS_HREDRAW | CS_VREDRAW;
  340.         wc.lpfnWndProc = viewproc;
  341.         wc.cbClsExtra = 0;
  342.         wc.cbWndExtra = 0;
  343.         wc.hInstance = GetModuleHandle(NULL);
  344.         wc.hIcon = NULL;
  345.         wc.hCursor = NULL;
  346.         wc.hbrBackground = NULL;
  347.         wc.lpszMenuName = NULL;
  348.         wc.lpszClassName = L"ViewWindow";
  349.         a = RegisterClassW(&wc);
  350.         if (!a)
  351.                 win32error("cannot register view window class");
  352.  
  353.         /* Get screen size */
  354.         SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
  355.         gapp.scrw = r.right - r.left;
  356.         gapp.scrh = r.bottom - r.top;
  357.  
  358.         /* Create cursors */
  359.         arrowcurs = LoadCursor(NULL, IDC_ARROW);
  360.         handcurs = LoadCursor(NULL, IDC_HAND);
  361.         waitcurs = LoadCursor(NULL, IDC_WAIT);
  362.  
  363.         /* And a background color */
  364.         bgbrush = CreateSolidBrush(RGB(0x70,0x70,0x70));
  365.         shbrush = CreateSolidBrush(RGB(0x40,0x40,0x40));
  366.  
  367.         /* Init DIB info for buffer */
  368.         dibinf = malloc(sizeof(BITMAPINFO) + 12);
  369.         assert(dibinf != NULL);
  370.         dibinf->bmiHeader.biSize = sizeof(dibinf->bmiHeader);
  371.         dibinf->bmiHeader.biPlanes = 1;
  372.         dibinf->bmiHeader.biBitCount = 32;
  373.         dibinf->bmiHeader.biCompression = BI_RGB;
  374.         dibinf->bmiHeader.biXPelsPerMeter = 2834;
  375.         dibinf->bmiHeader.biYPelsPerMeter = 2834;
  376.         dibinf->bmiHeader.biClrUsed = 0;
  377.         dibinf->bmiHeader.biClrImportant = 0;
  378.         dibinf->bmiHeader.biClrUsed = 0;
  379.  
  380.         /* Create window */
  381.         hwndframe = CreateWindowW(L"FrameWindow", // window class name
  382.         NULL, // window caption
  383.         WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  384.         CW_USEDEFAULT, CW_USEDEFAULT, // initial position
  385.         300, // initial x size
  386.         300, // initial y size
  387.         0, // parent window handle
  388.         0, // window menu handle
  389.         0, // program instance handle
  390.         0); // creation parameters
  391.         if (!hwndframe)
  392.                 win32error("cannot create frame: %s");
  393.  
  394.         hwndview = CreateWindowW(L"ViewWindow", // window class name
  395.         NULL,
  396.         WS_VISIBLE | WS_CHILD,
  397.         CW_USEDEFAULT, CW_USEDEFAULT,
  398.         CW_USEDEFAULT, CW_USEDEFAULT,
  399.         hwndframe, 0, 0, 0);
  400.         if (!hwndview)
  401.                 win32error("cannot create view: %s");
  402.  
  403.         hdc = NULL;
  404.  
  405.         SetWindowTextW(hwndframe, L"MuPDF");
  406.  
  407.         menu = GetSystemMenu(hwndframe, 0);
  408.         AppendMenuW(menu, MF_SEPARATOR, 0, NULL);
  409.         AppendMenuW(menu, MF_STRING, ID_ABOUT, L"About MuPDF...");
  410.         AppendMenuW(menu, MF_STRING, ID_DOCINFO, L"Document Properties...");
  411.  
  412.         SetCursor(arrowcurs);
  413. }
  414.  
  415. void winclose(pdfapp_t *app)
  416. {
  417.         pdfapp_close(app);
  418.         exit(0);
  419. }
  420.  
  421. void wincursor(pdfapp_t *app, int curs)
  422. {
  423.         if (curs == ARROW)
  424.                 SetCursor(arrowcurs);
  425.         if (curs == HAND)
  426.                 SetCursor(handcurs);
  427.         if (curs == WAIT)
  428.                 SetCursor(waitcurs);
  429. }
  430.  
  431. void wintitle(pdfapp_t *app, char *title)
  432. {
  433.         wchar_t wide[256], *dp;
  434.         char *sp;
  435.         int rune;
  436.  
  437.         dp = wide;
  438.         sp = title;
  439.         while (*sp && dp < wide + 255)
  440.         {
  441.                 sp += chartorune(&rune, sp);
  442.                 *dp++ = rune;
  443.         }
  444.         *dp = 0;
  445.  
  446.         SetWindowTextW(hwndframe, wide);
  447. }
  448.  
  449. void windrawrect(pdfapp_t *app, int x0, int y0, int x1, int y1)
  450. {
  451.         RECT r;
  452.         r.left = x0;
  453.         r.top = y0;
  454.         r.right = x1;
  455.         r.bottom = y1;
  456.         FillRect(hdc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
  457. }
  458.  
  459. void windrawstring(pdfapp_t *app, int x, int y, char *s)
  460. {
  461.         HFONT font = (HFONT)GetStockObject(ANSI_FIXED_FONT);
  462.         SelectObject(hdc, font);
  463.         TextOutA(hdc, x, y - 12, s, strlen(s));
  464. }
  465.  
  466. void winblitsearch()
  467. {
  468.         if (gapp.isediting)
  469.         {
  470.                 char buf[sizeof(gapp.search) + 50];
  471.                 sprintf(buf, "Search: %s", gapp.search);
  472.                 windrawrect(&gapp, 0, 0, gapp.winw, 30);
  473.                 windrawstring(&gapp, 10, 20, buf);
  474.         }
  475. }
  476.  
  477. void winblit()
  478. {
  479.         int x0 = gapp.panx;
  480.         int y0 = gapp.pany;
  481.         int x1 = gapp.panx + gapp.image->w;
  482.         int y1 = gapp.pany + gapp.image->h;
  483.         RECT r;
  484.  
  485.         if (gapp.image)
  486.         {
  487.                 if (gapp.iscopying || justcopied)
  488.                 {
  489.                         pdfapp_invert(&gapp, gapp.selr);
  490.                         justcopied = 1;
  491.                 }
  492.  
  493.                 pdfapp_inverthit(&gapp);
  494.  
  495.                 dibinf->bmiHeader.biWidth = gapp.image->w;
  496.                 dibinf->bmiHeader.biHeight = -gapp.image->h;
  497.                 dibinf->bmiHeader.biSizeImage = gapp.image->h * 4;
  498.  
  499.                 if (gapp.image->n == 2)
  500.                 {
  501.                         int i = gapp.image->w * gapp.image->h;
  502.                         unsigned char *color = malloc(i*4);
  503.                         unsigned char *s = gapp.image->samples;
  504.                         unsigned char *d = color;
  505.                         for (; i > 0 ; i--)
  506.                         {
  507.                                 d[2] = d[1] = d[0] = *s++;
  508.                                 d[3] = *s++;
  509.                                 d += 4;
  510.                         }
  511.                         SetDIBitsToDevice(hdc,
  512.                                 gapp.panx, gapp.pany, gapp.image->w, gapp.image->h,
  513.                                 0, 0, 0, gapp.image->h, color,
  514.                                 dibinf, DIB_RGB_COLORS);
  515.                         free(color);
  516.                 }
  517.                 if (gapp.image->n == 4)
  518.                 {
  519.                         SetDIBitsToDevice(hdc,
  520.                                 gapp.panx, gapp.pany, gapp.image->w, gapp.image->h,
  521.                                 0, 0, 0, gapp.image->h, gapp.image->samples,
  522.                                 dibinf, DIB_RGB_COLORS);
  523.                 }
  524.  
  525.                 pdfapp_inverthit(&gapp);
  526.  
  527.                 if (gapp.iscopying || justcopied)
  528.                 {
  529.                         pdfapp_invert(&gapp, gapp.selr);
  530.                         justcopied = 1;
  531.                 }
  532.         }
  533.  
  534.         /* Grey background */
  535.         r.top = 0; r.bottom = gapp.winh;
  536.         r.left = 0; r.right = x0;
  537.         FillRect(hdc, &r, bgbrush);
  538.         r.left = x1; r.right = gapp.winw;
  539.         FillRect(hdc, &r, bgbrush);
  540.         r.left = 0; r.right = gapp.winw;
  541.         r.top = 0; r.bottom = y0;
  542.         FillRect(hdc, &r, bgbrush);
  543.         r.top = y1; r.bottom = gapp.winh;
  544.         FillRect(hdc, &r, bgbrush);
  545.  
  546.         /* Drop shadow */
  547.         r.left = x0 + 2;
  548.         r.right = x1 + 2;
  549.         r.top = y1;
  550.         r.bottom = y1 + 2;
  551.         FillRect(hdc, &r, shbrush);
  552.         r.left = x1;
  553.         r.right = x1 + 2;
  554.         r.top = y0 + 2;
  555.         r.bottom = y1;
  556.         FillRect(hdc, &r, shbrush);
  557.  
  558.         winblitsearch();
  559. }
  560.  
  561. void winresize(pdfapp_t *app, int w, int h)
  562. {
  563.         ShowWindow(hwndframe, SW_SHOWDEFAULT);
  564.         w += GetSystemMetrics(SM_CXFRAME) * 2;
  565.         h += GetSystemMetrics(SM_CYFRAME) * 2;
  566.         h += GetSystemMetrics(SM_CYCAPTION);
  567.         SetWindowPos(hwndframe, 0, 0, 0, w, h, SWP_NOZORDER | SWP_NOMOVE);
  568. }
  569.  
  570. void winrepaint(pdfapp_t *app)
  571. {
  572.         InvalidateRect(hwndview, NULL, 0);
  573. }
  574.  
  575. void winrepaintsearch(pdfapp_t *app)
  576. {
  577.         // TODO: invalidate only search area and
  578.         // call only search redraw routine.
  579.         InvalidateRect(hwndview, NULL, 0);
  580. }
  581.  
  582. /*
  583.  * Event handling
  584.  */
  585.  
  586. void windocopy(pdfapp_t *app)
  587. {
  588.         HGLOBAL handle;
  589.         unsigned short *ucsbuf;
  590.  
  591.         if (!OpenClipboard(hwndframe))
  592.                 return;
  593.         EmptyClipboard();
  594.  
  595.         handle = GlobalAlloc(GMEM_MOVEABLE, 4096 * sizeof(unsigned short));
  596.         if (!handle)
  597.         {
  598.                 CloseClipboard();
  599.                 return;
  600.         }
  601.  
  602.         ucsbuf = GlobalLock(handle);
  603.         pdfapp_oncopy(&gapp, ucsbuf, 4096);
  604.         GlobalUnlock(handle);
  605.  
  606.         SetClipboardData(CF_UNICODETEXT, handle);
  607.         CloseClipboard();
  608.  
  609.         justcopied = 1; /* keep inversion around for a while... */
  610. }
  611.  
  612. void winreloadfile(pdfapp_t *app)
  613. {
  614.         int fd;
  615.  
  616.         pdfapp_close(app);
  617.  
  618.         fd = _wopen(wbuf, O_BINARY | O_RDONLY, 0666);
  619.         if (fd < 0)
  620.                 winerror(&gapp, fz_throw("cannot reload file '%s'", filename));
  621.  
  622.         pdfapp_open(app, filename, fd, 1);
  623. }
  624.  
  625. void winopenuri(pdfapp_t *app, char *buf)
  626. {
  627.         ShellExecuteA(hwndframe, "open", buf, 0, 0, SW_SHOWNORMAL);
  628. }
  629.  
  630. void handlekey(int c)
  631. {
  632.         if (GetCapture() == hwndview)
  633.                 return;
  634.  
  635.         if (justcopied)
  636.         {
  637.                 justcopied = 0;
  638.                 winrepaint(&gapp);
  639.         }
  640.  
  641.         /* translate VK into ascii equivalents */
  642.         if (c > 256)
  643.         {
  644.                 switch (c - 256)
  645.                 {
  646.                 case VK_F1: c = '?'; break;
  647.                 case VK_ESCAPE: c = '\033'; break;
  648.                 case VK_DOWN: c = 'j'; break;
  649.                 case VK_UP: c = 'k'; break;
  650.                 case VK_LEFT: c = 'b'; break;
  651.                 case VK_RIGHT: c = ' '; break;
  652.                 case VK_PRIOR: c = ','; break;
  653.                 case VK_NEXT: c = '.'; break;
  654.                 }
  655.         }
  656.  
  657.         pdfapp_onkey(&gapp, c);
  658.         winrepaint(&gapp);
  659. }
  660.  
  661. void handlemouse(int x, int y, int btn, int state)
  662. {
  663.         if (state != 0 && justcopied)
  664.         {
  665.                 justcopied = 0;
  666.                 winrepaint(&gapp);
  667.         }
  668.  
  669.         if (state == 1)
  670.                 SetCapture(hwndview);
  671.         if (state == -1)
  672.                 ReleaseCapture();
  673.  
  674.         pdfapp_onmouse(&gapp, x, y, btn, 0, state);
  675. }
  676.  
  677. LRESULT CALLBACK
  678. frameproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  679. {
  680.         switch(message)
  681.         {
  682.         case WM_SETFOCUS:
  683.                 PostMessage(hwnd, WM_APP+5, 0, 0);
  684.                 return 0;
  685.         case WM_APP+5:
  686.                 SetFocus(hwndview);
  687.                 return 0;
  688.  
  689.         case WM_DESTROY:
  690.                 PostQuitMessage(0);
  691.                 return 0;
  692.  
  693.         case WM_SYSCOMMAND:
  694.                 if (wParam == ID_ABOUT)
  695.                 {
  696.                         winhelp(&gapp);
  697.                         return 0;
  698.                 }
  699.                 if (wParam == ID_DOCINFO)
  700.                 {
  701.                         info();
  702.                         return 0;
  703.                 }
  704.                 if (wParam == SC_MAXIMIZE)
  705.                         gapp.shrinkwrap = 0;
  706.                 break;
  707.  
  708.         case WM_SIZE:
  709.         {
  710.                 // More generally, you should use GetEffectiveClientRect
  711.                 // if you have a toolbar etc.
  712.                 RECT rect;
  713.                 GetClientRect(hwnd, &rect);
  714.                 MoveWindow(hwndview, rect.left, rect.top,
  715.                 rect.right-rect.left, rect.bottom-rect.top, TRUE);
  716.                 return 0;
  717.         }
  718.  
  719.         case WM_SIZING:
  720.                 gapp.shrinkwrap = 0;
  721.                 break;
  722.  
  723.         case WM_NOTIFY:
  724.         case WM_COMMAND:
  725.                 return SendMessage(hwndview, message, wParam, lParam);
  726.         }
  727.  
  728.         return DefWindowProc(hwnd, message, wParam, lParam);
  729. }
  730.  
  731. LRESULT CALLBACK
  732. viewproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  733. {
  734.         static int oldx = 0;
  735.         static int oldy = 0;
  736.         int x = (signed short) LOWORD(lParam);
  737.         int y = (signed short) HIWORD(lParam);
  738.  
  739.         switch (message)
  740.         {
  741.         case WM_SIZE:
  742.                 if (wParam == SIZE_MINIMIZED)
  743.                         return 0;
  744.                 if (wParam == SIZE_MAXIMIZED)
  745.                         gapp.shrinkwrap = 0;
  746.                 pdfapp_onresize(&gapp, LOWORD(lParam), HIWORD(lParam));
  747.                 break;
  748.  
  749.         /* Paint events are low priority and automagically catenated
  750.          * so we don't need to do any fancy waiting to defer repainting.
  751.          */
  752.         case WM_PAINT:
  753.         {
  754.                 //puts("WM_PAINT");
  755.                 PAINTSTRUCT ps;
  756.                 hdc = BeginPaint(hwnd, &ps);
  757.                 winblit();
  758.                 hdc = NULL;
  759.                 EndPaint(hwnd, &ps);
  760.                 return 0;
  761.         }
  762.  
  763.         case WM_ERASEBKGND:
  764.                 return 1; // well, we don't need to erase to redraw cleanly
  765.  
  766.         /* Mouse events */
  767.  
  768.         case WM_LBUTTONDOWN:
  769.                 SetFocus(hwndview);
  770.                 oldx = x; oldy = y;
  771.                 handlemouse(x, y, 1, 1);
  772.                 return 0;
  773.         case WM_MBUTTONDOWN:
  774.                 SetFocus(hwndview);
  775.                 oldx = x; oldy = y;
  776.                 handlemouse(x, y, 2, 1);
  777.                 return 0;
  778.         case WM_RBUTTONDOWN:
  779.                 SetFocus(hwndview);
  780.                 oldx = x; oldy = y;
  781.                 handlemouse(x, y, 3, 1);
  782.                 return 0;
  783.  
  784.         case WM_LBUTTONUP:
  785.                 oldx = x; oldy = y;
  786.                 handlemouse(x, y, 1, -1);
  787.                 return 0;
  788.         case WM_MBUTTONUP:
  789.                 oldx = x; oldy = y;
  790.                 handlemouse(x, y, 2, -1);
  791.                 return 0;
  792.         case WM_RBUTTONUP:
  793.                 oldx = x; oldy = y;
  794.                 handlemouse(x, y, 3, -1);
  795.                 return 0;
  796.  
  797.         case WM_MOUSEMOVE:
  798.                 oldx = x; oldy = y;
  799.                 handlemouse(x, y, 0, 0);
  800.                 return 0;
  801.  
  802.         /* Mouse wheel */
  803.  
  804.         case WM_MOUSEWHEEL:
  805.                 if ((signed short)HIWORD(wParam) > 0)
  806.                         handlekey(LOWORD(wParam) & MK_SHIFT ? '+' : 'k');
  807.                 else
  808.                         handlekey(LOWORD(wParam) & MK_SHIFT ? '-' : 'j');
  809.                 return 0;
  810.  
  811.         /* Keyboard events */
  812.  
  813.         case WM_KEYDOWN:
  814.                 /* only handle special keys */
  815.                 switch (wParam)
  816.                 {
  817.                 case VK_F1:
  818.                 case VK_LEFT:
  819.                 case VK_UP:
  820.                 case VK_PRIOR:
  821.                 case VK_RIGHT:
  822.                 case VK_DOWN:
  823.                 case VK_NEXT:
  824.                 case VK_ESCAPE:
  825.                         handlekey(wParam + 256);
  826.                         handlemouse(oldx, oldy, 0, 0);  /* update cursor */
  827.                         return 0;
  828.                 }
  829.                 return 1;
  830.  
  831.         /* unicode encoded chars, including escape, backspace etc... */
  832.         case WM_CHAR:
  833.                 if (wParam < 256)
  834.                 {
  835.                         handlekey(wParam);
  836.                         handlemouse(oldx, oldy, 0, 0);  /* update cursor */
  837.                 }
  838.                 return 0;
  839.         }
  840.  
  841.         fflush(stdout);
  842.  
  843.         /* Pass on unhandled events to Windows */
  844.         return DefWindowProc(hwnd, message, wParam, lParam);
  845. }
  846.  
  847. int WINAPI
  848. WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
  849. {
  850.         int argc;
  851.         LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);
  852.         char argv0[256];
  853.         MSG msg;
  854.         int fd;
  855.         int code;
  856.  
  857.         fz_accelerate();
  858.  
  859.         pdfapp_init(&gapp);
  860.  
  861.         GetModuleFileNameA(NULL, argv0, sizeof argv0);
  862.         install_app(argv0);
  863.  
  864.         winopen();
  865.  
  866.         if (argc == 2)
  867.         {
  868.                 wcscpy(wbuf, argv[1]);
  869.         }
  870.         else
  871.         {
  872.                 if (!winfilename(wbuf, nelem(wbuf)))
  873.                         exit(0);
  874.         }
  875.  
  876.         fd = _wopen(wbuf, O_BINARY | O_RDONLY, 0666);
  877.         if (fd < 0)
  878.                 winerror(&gapp, fz_throw("cannot open file '%s'", filename));
  879.  
  880.         code = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, filename, sizeof filename, NULL, NULL);
  881.         if (code == 0)
  882.                 win32error("cannot convert filename to utf-8");
  883.  
  884.         pdfapp_open(&gapp, filename, fd, 0);
  885.  
  886.         while (GetMessage(&msg, NULL, 0, 0))
  887.         {
  888.                 TranslateMessage(&msg);
  889.                 DispatchMessage(&msg);
  890.         }
  891.  
  892.         pdfapp_close(&gapp);
  893.  
  894.         return 0;
  895. }
  896.