diff options
Diffstat (limited to 'dviware/dviview/DVIView/DVIView.cpp')
-rw-r--r-- | dviware/dviview/DVIView/DVIView.cpp | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/dviware/dviview/DVIView/DVIView.cpp b/dviware/dviview/DVIView/DVIView.cpp new file mode 100644 index 0000000000..1b7c5adca8 --- /dev/null +++ b/dviware/dviview/DVIView/DVIView.cpp @@ -0,0 +1,595 @@ +// DVIView.cpp : Defines the entry point for the application. +// + +/* + + DVIView: A viewer for DVI files and frontend for dvipdfmx. + Copyright (c) 2020 by Peter Frane Jr. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + + + + The author may be contacted via the e-mail address pfranejr@hotmail.com + + Please refer to the following URL for further instructions on how to download + the packages required by WebView2: + +*/ + + +#include "framework.h" +#include "DVIView.h" +#include <wchar.h> +#include <shlwapi.h> + +using namespace std; + +#pragma comment(lib, "Shlwapi.lib") + +#define MAX_LOADSTRING 100 + +using namespace Microsoft::WRL; +//using namespace std; + +// Global Variables: +HINSTANCE hInst; // current instance +WCHAR szTitle[MAX_LOADSTRING]; // The title bar text +WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +// Pointer to WebViewController +static wil::com_ptr<ICoreWebView2Controller> webviewController; + +// Pointer to WebView window +static wil::com_ptr<ICoreWebView2> webviewWindow; +static wil::com_ptr <ICoreWebView2Environment> webEnvironment; +char log_file[MAX_PATH + 1]{ 0 }; + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); +void resize_window(HWND hWnd); +void process_command_line(HWND hWnd); +int on_create(HWND hWnd); + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPWSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + // TODO: Place code here. + + // Initialize global strings + LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadStringW(hInstance, IDC_DVIVIEW, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + // Perform application initialization: + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DVIVIEW)); + + MSG msg; + + // Main message loop: + while (GetMessage(&msg, nullptr, 0, 0)) + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return (int) msg.wParam; +} + + + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEXW wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DVIVIEW)); + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_DVIVIEW); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + return RegisterClassExW(&wcex); +} + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + hInst = hInstance; // Store instance handle in our global variable + + HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); + + if (!hWnd) + { + return FALSE; + } + + ShowWindow(hWnd, nCmdShow); + UpdateWindow(hWnd); + + HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, + Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>( + [hWnd](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { + + // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd + env->CreateCoreWebView2Controller(hWnd, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>( + [hWnd](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { + if (controller != nullptr) { + webviewController = controller; + if (webviewController->get_CoreWebView2(&webviewWindow) == S_OK) + { + // Resize WebView to fit the bounds of the parent window + resize_window(hWnd); + + process_command_line(hWnd); + } + } + + return S_OK; + }).Get()); + return S_OK; + }).Get()); + + if (S_OK == hr) + return TRUE; + + DestroyWindow(hWnd); + + return FALSE; +} + +// read the messages of dvipdfx + +void read_pipe(HWND hWnd, HANDLE child_stdout_read) +{ + HANDLE hlog; + + hlog = CreateFileA(log_file, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + + if (INVALID_HANDLE_VALUE == hlog) + { + MessageBox(hWnd, L"Unable to create the log file", L"Internal Error", MB_OK); + + return; + } + else + { + const size_t BUFSIZE = 4096; + + DWORD dw_read, dw_written; + char buf[BUFSIZE]; + BOOL success = FALSE; + + while (true) + { + success = ReadFile(child_stdout_read, buf, BUFSIZE, &dw_read, NULL); + + if (!success || dw_read == 0) + break; + + success = WriteFile(hlog, buf, dw_read, &dw_written, NULL); + + if (!success) + break; + } + CloseHandle(hlog); + } +} + +struct handle +{ + HANDLE m_handle{ INVALID_HANDLE_VALUE }; + void close() + { + if (m_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(m_handle); + + m_handle = INVALID_HANDLE_VALUE; + } + } + ~handle() + { + close(); + } +}; + +void show_pdf_file(const char *filename) +{ + wchar_t pdf_file[MAX_PATH + 1]{ 0 }; + + MultiByteToWideChar(CP_UTF8, 0, filename, lstrlenA(filename), pdf_file, MAX_PATH); + + webviewWindow->Navigate(pdf_file); +} + +void create_pdf(HWND hWnd, const char* dvi_file) +{ + PROCESS_INFORMATION pi{ 0 }; + STARTUPINFOA si{ 0 }; + char param[MAX_PATH * 2 + 1]{ "dvipdfmx.exe -o " }; + char fname[_MAX_FNAME + 1]{ 0 }; + char temp_pdf_path[MAX_PATH + 1]{ 0 }; + handle child_stdout_read; + handle child_stdout_write; + SECURITY_ATTRIBUTES sa{ 0 }; + + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + + if (!CreatePipe(&child_stdout_read.m_handle, &child_stdout_write.m_handle, &sa, 0)) + { + MessageBox(hWnd, L"Unable to create a pipe", L"Internal Error", MB_OK); + + return; + } + else + { + SetHandleInformation(child_stdout_read.m_handle, HANDLE_FLAG_INHERIT, 0); + } + + if (_splitpath_s(dvi_file, 0, 0, 0, 0, fname, _MAX_FNAME, 0, 0) != 0) + { + MessageBox(hWnd, L"Unable to split the path", L"Internal Error", MB_OK); + + return; + } + + if (GetTempPathA(sizeof(temp_pdf_path) - 1, temp_pdf_path) == 0) + { + MessageBox(hWnd, L"Unable to obtain the TEMP path", L"Internal Error", MB_OK); + + return; + } + else if (!PathFileExistsA(temp_pdf_path)) + { + MessageBox(hWnd, L"The TEMP path folder is not available", L"Internal Error", MB_OK); + + return; + } + + lstrcatA(log_file, temp_pdf_path); + + lstrcatA(log_file, fname); + + lstrcatA(log_file, ".log"); + + lstrcatA(temp_pdf_path, fname); + + lstrcatA(temp_pdf_path, ".pdf"); + + // enclose the parameters in quotes + + lstrcatA(param, "\""); + + lstrcatA(param, temp_pdf_path); + + // enclose the parameters in quotes + + lstrcatA(param, "\" \""); + + lstrcatA(param, dvi_file); + + // close the quote + + lstrcatA(param, "\""); + + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + //si.hStdInput = child_stdin_read.m_handle; + si.hStdOutput = child_stdout_write.m_handle; + si.hStdError = child_stdout_write.m_handle; + si.wShowWindow = SW_HIDE; + + if (!CreateProcessA(nullptr, param, nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) + { + MessageBox(hWnd, L"Unable to execute 'dvipdfmx.exe", L"Error", MB_OK); + + return; + } + else + { + DWORD exit_code = 0; + + WaitForSingleObject(pi.hProcess, INFINITE); + + GetExitCodeProcess(pi.hProcess, &exit_code); + + CloseHandle(pi.hProcess); + + CloseHandle(pi.hThread); + + child_stdout_write.close(); + + read_pipe(hWnd, child_stdout_read.m_handle); + + child_stdout_read.close(); + + if (exit_code != 0) + { + MessageBox(hWnd, L"'dvipdfmx.exe returned an error.\nPlease see the log file in the File menu", L"Error", MB_OK); + + return; + } + else + { + WIN32_FILE_ATTRIBUTE_DATA attrib{ 0 }; + + GetFileAttributesExA(temp_pdf_path, GetFileExInfoStandard, &attrib); + + if( attrib.nFileSizeLow > 0 || attrib.nFileSizeHigh > 0) + { + show_pdf_file(temp_pdf_path); + } + } + } + return; +} + +void clear_log_file() +{ + log_file[0] = 0; +} + +void open_file(HWND hWnd) +{ + enum class file_type { ft_dvi = 1, ft_pdf = 2}; + OPENFILENAMEA ofn = { 0 }; + char filename[MAX_PATH + 1] = { 0 }; + + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hWnd; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + ofn.lpstrFilter = "DVI\0*.dvi\0PDF\0*.pdf\0\0\0"; + ofn.lpstrFile = filename; + ofn.nMaxFile = MAX_PATH; + + if (GetOpenFileNameA(&ofn) == TRUE) + { + if (DWORD(file_type::ft_dvi) == ofn.nFilterIndex) // dvi + { + create_pdf(hWnd, ofn.lpstrFile); + } + else if (DWORD(file_type::ft_pdf) == ofn.nFilterIndex) + { + clear_log_file(); + + show_pdf_file(ofn.lpstrFile); + } + } +} + +void resize_window(HWND hWnd) +{ + if (webviewController != nullptr) + { + RECT bounds; + GetClientRect(hWnd, &bounds); + webviewController->put_Bounds(bounds); + } +} + +void process_command_line(HWND hWnd) +{ + LPWSTR* arg_list; + int argc; + + arg_list = CommandLineToArgvW(GetCommandLineW(), &argc); + + if (nullptr == arg_list) + { + MessageBox(hWnd, L"CommandLineToArgvW failed", L"Error", MB_OK); + + return; + } + else if (argc > 1) + { + char dvi_file[MAX_PATH + 1]{ 0 }; + int result; + + result = WideCharToMultiByte(CP_UTF8, 0, arg_list[1], -1, dvi_file, (int)MAX_PATH, 0, FALSE); + + LocalFree(arg_list); + + if (result > 0) + { + create_pdf(hWnd, dvi_file); + } + else + { + MessageBox(hWnd, L"Unable to convert a string from wide char to multibyte", L"Internal Error", MB_OK); + + return; + } + } +} + +void run_application(HWND hWnd, const wchar_t *app_name, const wchar_t *input_filename) +{ + PROCESS_INFORMATION pi{ 0 }; + STARTUPINFOW si{ 0 }; + wchar_t param[MAX_PATH *2 + 1]{0}; + + si.cb = sizeof(si); + + lstrcpyW(param, app_name); + lstrcatW(param, L" "); + + if (input_filename) + { + lstrcatW(param, input_filename); + } + + if (!CreateProcessW(nullptr, param, nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi)) + { + wchar_t msg[MAX_PATH + 1]{ L"Unable to execute " }; + + lstrcatW(msg, app_name); + + MessageBoxW(hWnd, msg, L"Error", MB_OK); + + return; + } + + CloseHandle(pi.hProcess); + + CloseHandle(pi.hThread); +} + +void view_log_file(HWND hWnd) +{ + wchar_t input_filename[MAX_PATH + 1]{ 0 }; + + MultiByteToWideChar(CP_UTF8, 0, log_file, -1, input_filename, MAX_PATH); + + run_application(hWnd, L"notepad.exe", input_filename); +} + + +void new_instance(HWND hWnd, const wchar_t* input_filename) +{ + wchar_t app_name[MAX_PATH + 1]{ 0 }; + + GetModuleFileNameW(nullptr, app_name, MAX_PATH); + + run_application(hWnd, app_name, input_filename); +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +// WM_COMMAND - process the application menu +// WM_PAINT - Paint the main window +// WM_DESTROY - post a quit message and return +// +// +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_COMMAND: + { + int wmId = LOWORD(wParam); + // Parse the menu selections: + switch (wmId) + { + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + case IDM_EXIT: + DestroyWindow(hWnd); + break; + case IDM_FILE_NEW: + new_instance(hWnd, nullptr); + break; + case IDM_FILE_OPEN: + open_file(hWnd); + break; + case IDM_FILE_CLOSE: + webviewWindow->Navigate(L"about:blank"); + clear_log_file(); + break; + case IDM_FILE_VIEW_LOG: + view_log_file(hWnd); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + break; + case WM_INITMENU: + { + HMENU hmenu = (HMENU)wParam; + + if (log_file[0] != 0) + { + EnableMenuItem(hmenu, IDM_FILE_VIEW_LOG, MF_ENABLED); + } + else + { + EnableMenuItem(hmenu, IDM_FILE_VIEW_LOG, MF_DISABLED | MF_GRAYED); + } + } + break; + case WM_SIZE: + resize_window(hWnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + return (INT_PTR)FALSE; +} |