mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-23 06:35:38 +00:00
666 lines
22 KiB
C++
666 lines
22 KiB
C++
// SPDX-License-Identifier: MIT
|
|
//
|
|
// EmulationStation Desktop Edition
|
|
// PDFViewer.cpp
|
|
//
|
|
// Parses and renders pages using the Poppler library via the external es-pdf-convert binary.
|
|
//
|
|
|
|
#include "PDFViewer.h"
|
|
|
|
#include "Log.h"
|
|
#include "Sound.h"
|
|
#include "utils/FileSystemUtil.h"
|
|
#include "utils/StringUtil.h"
|
|
#include "views/ViewController.h"
|
|
|
|
#include <array>
|
|
|
|
#if defined(_WIN64)
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#define DEBUG_PDF_CONVERSION false
|
|
|
|
PDFViewer::PDFViewer()
|
|
: mRenderer {Renderer::getInstance()}
|
|
, mGame {nullptr}
|
|
, mFrameHeight {0.0f}
|
|
, mHelpInfoPosition {HelpInfoPosition::TOP}
|
|
{
|
|
Window::getInstance()->setPDFViewer(this);
|
|
}
|
|
|
|
bool PDFViewer::startPDFViewer(FileData* game)
|
|
{
|
|
ViewController::getInstance()->pauseViewVideos();
|
|
|
|
#if defined(_WIN64)
|
|
const std::string convertBinary {"/es-pdf-converter/es-pdf-convert.exe"};
|
|
#else
|
|
const std::string convertBinary {"/es-pdf-convert"};
|
|
#endif
|
|
mESConvertPath = Utils::FileSystem::getExePath() + convertBinary;
|
|
if (!Utils::FileSystem::exists(mESConvertPath)) {
|
|
#if defined(_WIN64)
|
|
LOG(LogError) << "Couldn't find PDF conversion binary es-pdf-convert.exe";
|
|
#else
|
|
LOG(LogError) << "Couldn't find PDF conversion binary es-pdf-convert";
|
|
#endif
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
|
ViewController::getInstance()->stopViewVideos();
|
|
return false;
|
|
}
|
|
|
|
mGame = game;
|
|
mManualPath = mGame->getManualPath();
|
|
|
|
if (!Utils::FileSystem::exists(mManualPath)) {
|
|
LOG(LogError) << "No PDF manual found for game \"" << mGame->getName() << "\"";
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
|
ViewController::getInstance()->stopViewVideos();
|
|
return false;
|
|
}
|
|
|
|
#if defined(_WIN64)
|
|
mManualPath = Utils::String::replace(mManualPath, "/", "\\");
|
|
#endif
|
|
|
|
LOG(LogDebug) << "PDFViewer::startPDFViewer(): Opening document \"" << mManualPath << "\"";
|
|
|
|
mPages.clear();
|
|
mPageImage.reset();
|
|
mPageCount = 0;
|
|
mCurrentPage = 0;
|
|
mScaleFactor = 1.0f;
|
|
mZoom = 1.0f;
|
|
mPanAmount = 0.0f;
|
|
mPanOffset = {0.0f, 0.0f, 0.0f};
|
|
|
|
// Increase the rasterization resolution when running at lower screen resolutions to make
|
|
// the texture look ok when zoomed in.
|
|
const float resolutionModifier {mRenderer->getScreenResolutionModifier()};
|
|
if (resolutionModifier < 1.0f)
|
|
mScaleFactor = 1.8f;
|
|
else if (resolutionModifier < 1.2f)
|
|
mScaleFactor = 1.3f;
|
|
else if (resolutionModifier < 1.4f)
|
|
mScaleFactor = 1.15f;
|
|
|
|
if (!getDocumentInfo()) {
|
|
LOG(LogError) << "PDFViewer: Couldn't load file \"" << mManualPath << "\"";
|
|
ViewController::getInstance()->stopViewVideos();
|
|
return false;
|
|
}
|
|
|
|
mPageCount = static_cast<int>(mPages.size());
|
|
|
|
for (int i {1}; i <= mPageCount; ++i) {
|
|
if (mPages.find(i) == mPages.end()) {
|
|
LOG(LogError) << "Couldn't read information for page " << i << ", invalid PDF file?";
|
|
ViewController::getInstance()->stopViewVideos();
|
|
return false;
|
|
}
|
|
|
|
float width {mPages[i].width};
|
|
float height {mPages[i].height};
|
|
|
|
if (mPages[i].orientation != "portrait" && mPages[i].orientation != "upside_down")
|
|
std::swap(width, height);
|
|
|
|
// Maintain page aspect ratio.
|
|
glm::vec2 textureSize {glm::vec2 {width, height}};
|
|
const glm::vec2 targetSize {glm::vec2 {mRenderer->getScreenWidth() * mScaleFactor,
|
|
mRenderer->getScreenHeight() * mScaleFactor}};
|
|
glm::vec2 resizeScale {targetSize.x / textureSize.x, targetSize.y / textureSize.y};
|
|
|
|
if (resizeScale.x < resizeScale.y) {
|
|
textureSize.x *= resizeScale.x;
|
|
textureSize.y = std::min(textureSize.y * resizeScale.x, targetSize.y);
|
|
}
|
|
else {
|
|
textureSize.y *= resizeScale.y;
|
|
textureSize.x = std::min((textureSize.y / height) * width, targetSize.x);
|
|
}
|
|
|
|
mPages[i].width = std::round(textureSize.x);
|
|
mPages[i].height = std::round(textureSize.y);
|
|
|
|
#if (DEBUG_PDF_CONVERSION)
|
|
LOG(LogDebug) << "Page " << i << ": Orientation: " << mPages[i].orientation << " / "
|
|
<< "crop box width: " << width << " / "
|
|
<< "crop box height: " << height << " / "
|
|
<< "size ratio: " << width / height << " / "
|
|
<< "texture size: " << mPages[i].width << "x" << mPages[i].height;
|
|
#endif
|
|
}
|
|
|
|
mCurrentPage = 1;
|
|
|
|
if (Settings::getInstance()->getString("MediaViewerHelpPrompts") == "disabled")
|
|
mHelpInfoPosition = HelpInfoPosition::DISABLED;
|
|
else if (Settings::getInstance()->getString("MediaViewerHelpPrompts") == "bottom")
|
|
mHelpInfoPosition = HelpInfoPosition::BOTTOM;
|
|
else
|
|
mHelpInfoPosition = HelpInfoPosition::TOP;
|
|
|
|
if (mHelpInfoPosition == HelpInfoPosition::DISABLED)
|
|
mFrameHeight = 0.0f;
|
|
else
|
|
mFrameHeight = Font::get(FONT_SIZE_MINI)->getLetterHeight() * 1.9f;
|
|
|
|
HelpStyle style;
|
|
style.font = Font::get(FONT_SIZE_MINI);
|
|
style.origin = {0.5f, 0.5f};
|
|
style.iconColor = 0xAAAAAAFF;
|
|
style.textColor = 0xAAAAAAFF;
|
|
|
|
mEntryCount = std::to_string(mPages.size());
|
|
|
|
mEntryNumText = std::make_unique<TextComponent>(
|
|
"PAGE 1 OF " + mEntryCount, Font::get(FONT_SIZE_MINI, FONT_PATH_REGULAR), 0xAAAAAAFF);
|
|
mEntryNumText->setOrigin(0.0f, 0.5f);
|
|
|
|
if (mHelpInfoPosition == HelpInfoPosition::TOP) {
|
|
mEntryNumText->setPosition(mRenderer->getScreenWidth() * 0.01f, mFrameHeight / 2.0f);
|
|
style.position = glm::vec2 {mRenderer->getScreenWidth() / 2.0f, mFrameHeight / 2.0f};
|
|
}
|
|
else if (mHelpInfoPosition == HelpInfoPosition::BOTTOM) {
|
|
mEntryNumText->setPosition(mRenderer->getScreenWidth() * 0.01f,
|
|
mRenderer->getScreenHeight() - (mFrameHeight / 2.0f));
|
|
style.position = glm::vec2 {mRenderer->getScreenWidth() / 2.0f,
|
|
mRenderer->getScreenHeight() - (mFrameHeight / 2.0f)};
|
|
}
|
|
|
|
mHelp = std::make_unique<HelpComponent>();
|
|
mHelp->setStyle(style);
|
|
mHelp->setPrompts(getHelpPrompts());
|
|
|
|
convertPage(mCurrentPage);
|
|
return true;
|
|
}
|
|
|
|
void PDFViewer::stopPDFViewer()
|
|
{
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
|
ViewController::getInstance()->stopViewVideos();
|
|
|
|
mPages.clear();
|
|
mPageImage.reset();
|
|
}
|
|
|
|
void PDFViewer::launchMediaViewer()
|
|
{
|
|
Window::getInstance()->stopPDFViewer();
|
|
Window::getInstance()->startMediaViewer(mGame);
|
|
}
|
|
|
|
bool PDFViewer::getDocumentInfo()
|
|
{
|
|
std::string commandOutput;
|
|
|
|
#if defined(_WIN64)
|
|
std::wstring command {
|
|
Utils::String::stringToWideString(Utils::FileSystem::getEscapedPath(mESConvertPath))};
|
|
command.append(L" -fileinfo ")
|
|
.append(Utils::String::stringToWideString(Utils::FileSystem::getEscapedPath(mManualPath)));
|
|
|
|
STARTUPINFOW si {};
|
|
PROCESS_INFORMATION pi;
|
|
HANDLE childStdoutRead {nullptr};
|
|
HANDLE childStdoutWrite {nullptr};
|
|
SECURITY_ATTRIBUTES saAttr {};
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saAttr.bInheritHandle = true;
|
|
saAttr.lpSecurityDescriptor = nullptr;
|
|
|
|
CreatePipe(&childStdoutRead, &childStdoutWrite, &saAttr, 0);
|
|
SetHandleInformation(childStdoutRead, HANDLE_FLAG_INHERIT, 0);
|
|
|
|
si.cb = sizeof(STARTUPINFOW);
|
|
si.hStdOutput = childStdoutWrite;
|
|
si.dwFlags |= STARTF_USESTDHANDLES;
|
|
|
|
bool processReturnValue {true};
|
|
|
|
// clang-format off
|
|
processReturnValue = CreateProcessW(
|
|
nullptr, // No application name (use command line).
|
|
const_cast<wchar_t*>(command.c_str()), // Command line.
|
|
nullptr, // Process attributes.
|
|
nullptr, // Thread attributes.
|
|
TRUE, // Handles inheritance.
|
|
0, // Creation flags.
|
|
nullptr, // Use parent's environment block.
|
|
nullptr, // Starting directory, possibly the same as parent.
|
|
&si, // Pointer to the STARTUPINFOW structure.
|
|
&pi); // Pointer to the PROCESS_INFORMATION structure.
|
|
// clang-format on
|
|
|
|
if (!processReturnValue) {
|
|
LOG(LogError) << "Couldn't read PDF document information";
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
return false;
|
|
}
|
|
|
|
// Close process and thread handles.
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
CloseHandle(childStdoutWrite);
|
|
|
|
std::array<char, 512> buffer {};
|
|
DWORD dwRead;
|
|
bool readValue {true};
|
|
|
|
while (readValue) {
|
|
readValue = ReadFile(childStdoutRead, &buffer[0], 512, &dwRead, nullptr);
|
|
if (readValue) {
|
|
for (int i {0}; i < 512; ++i) {
|
|
if (buffer[i] == '\0')
|
|
break;
|
|
commandOutput.append(1, buffer[i]);
|
|
}
|
|
buffer.fill('\0');
|
|
}
|
|
}
|
|
|
|
CloseHandle(childStdoutRead);
|
|
WaitForSingleObject(pi.hThread, INFINITE);
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
#else
|
|
FILE* commandPipe;
|
|
std::array<char, 512> buffer {};
|
|
|
|
std::string command {Utils::FileSystem::getEscapedPath(mESConvertPath)};
|
|
command.append(" -fileinfo ").append(Utils::FileSystem::getEscapedPath(mManualPath));
|
|
|
|
if (!(commandPipe = reinterpret_cast<FILE*>(popen(command.c_str(), "r")))) {
|
|
LOG(LogError) << "Couldn't open pipe to es-pdf-convert";
|
|
return false;
|
|
}
|
|
|
|
while (fread(buffer.data(), 1, 512, commandPipe)) {
|
|
for (int i {0}; i < 512; ++i) {
|
|
if (buffer[i] == '\0')
|
|
break;
|
|
commandOutput.append(1, buffer[i]);
|
|
}
|
|
buffer.fill('\0');
|
|
}
|
|
|
|
if (pclose(commandPipe) != 0)
|
|
return false;
|
|
#endif
|
|
|
|
const std::vector<std::string> pageRows {
|
|
Utils::String::delimitedStringToVector(commandOutput, "\n")};
|
|
|
|
for (auto& row : pageRows) {
|
|
const std::vector<std::string> rowValues {Utils::String::delimitedStringToVector(row, ";")};
|
|
if (rowValues.size() != 4)
|
|
continue;
|
|
mPages[atoi(&rowValues[0][0])] = PageEntry {static_cast<float>(atof(&rowValues[2][0])),
|
|
static_cast<float>(atof(&rowValues[3][0])),
|
|
rowValues[1],
|
|
{}};
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PDFViewer::convertPage(int pageNum)
|
|
{
|
|
assert(pageNum <= static_cast<int>(mPages.size()));
|
|
|
|
if (mPages[pageNum].imageData.empty()) {
|
|
#if defined(_WIN64)
|
|
std::wstring command {
|
|
Utils::String::stringToWideString(Utils::FileSystem::getEscapedPath(mESConvertPath))};
|
|
command.append(L" -convert ")
|
|
.append(
|
|
Utils::String::stringToWideString(Utils::FileSystem::getEscapedPath(mManualPath)))
|
|
.append(L" ")
|
|
.append(std::to_wstring(pageNum))
|
|
.append(L" ")
|
|
.append(std::to_wstring(static_cast<int>(mPages[pageNum].width)))
|
|
.append(L" ")
|
|
.append(std::to_wstring(static_cast<int>(mPages[pageNum].height)));
|
|
#else
|
|
std::string command {Utils::FileSystem::getEscapedPath(mESConvertPath)};
|
|
command.append(" -convert ")
|
|
.append(Utils::FileSystem::getEscapedPath(mManualPath))
|
|
.append(" ")
|
|
.append(std::to_string(pageNum))
|
|
.append(" ")
|
|
.append(std::to_string(static_cast<int>(mPages[pageNum].width)))
|
|
.append(" ")
|
|
.append(std::to_string(static_cast<int>(mPages[pageNum].height)));
|
|
#endif
|
|
|
|
#if (DEBUG_PDF_CONVERSION)
|
|
LOG(LogDebug) << "Converting page: " << mCurrentPage;
|
|
#if defined(_WIN64)
|
|
LOG(LogDebug) << Utils::String::wideStringToString(command);
|
|
#else
|
|
LOG(LogDebug) << command;
|
|
#endif
|
|
#endif
|
|
std::string imageData;
|
|
#if defined(_WIN64)
|
|
STARTUPINFOW si {};
|
|
PROCESS_INFORMATION pi;
|
|
HANDLE childStdoutRead {nullptr};
|
|
HANDLE childStdoutWrite {nullptr};
|
|
SECURITY_ATTRIBUTES saAttr {};
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saAttr.bInheritHandle = true;
|
|
saAttr.lpSecurityDescriptor = nullptr;
|
|
|
|
CreatePipe(&childStdoutRead, &childStdoutWrite, &saAttr, 0);
|
|
SetHandleInformation(childStdoutRead, HANDLE_FLAG_INHERIT, 0);
|
|
|
|
si.cb = sizeof(STARTUPINFOW);
|
|
si.hStdOutput = childStdoutWrite;
|
|
si.dwFlags |= STARTF_USESTDHANDLES;
|
|
|
|
bool processReturnValue {true};
|
|
|
|
// clang-format off
|
|
processReturnValue = CreateProcessW(
|
|
nullptr, // No application name (use command line).
|
|
const_cast<wchar_t*>(command.c_str()), // Command line.
|
|
nullptr, // Process attributes.
|
|
nullptr, // Thread attributes.
|
|
TRUE, // Handles inheritance.
|
|
0, // Creation flags.
|
|
nullptr, // Use parent's environment block.
|
|
nullptr, // Starting directory, possibly the same as parent.
|
|
&si, // Pointer to the STARTUPINFOW structure.
|
|
&pi); // Pointer to the PROCESS_INFORMATION structure.
|
|
// clang-format on
|
|
|
|
if (!processReturnValue) {
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
return;
|
|
}
|
|
|
|
// Close process and thread handles.
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
CloseHandle(childStdoutWrite);
|
|
|
|
std::array<char, 512> buffer {};
|
|
DWORD dwRead;
|
|
bool readValue {true};
|
|
|
|
while (readValue) {
|
|
readValue = ReadFile(childStdoutRead, &buffer[0], 512, &dwRead, nullptr);
|
|
if (readValue) {
|
|
mPages[pageNum].imageData.insert(mPages[pageNum].imageData.end(),
|
|
std::make_move_iterator(buffer.begin()),
|
|
std::make_move_iterator(buffer.end()));
|
|
}
|
|
}
|
|
|
|
CloseHandle(childStdoutRead);
|
|
WaitForSingleObject(pi.hThread, INFINITE);
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
#else
|
|
FILE* commandPipe;
|
|
std::array<char, 512> buffer {};
|
|
int returnValue;
|
|
|
|
if (!(commandPipe = reinterpret_cast<FILE*>(popen(command.c_str(), "r")))) {
|
|
LOG(LogError) << "Couldn't open pipe to es-pdf-convert";
|
|
return;
|
|
}
|
|
|
|
while (fread(buffer.data(), 1, 512, commandPipe)) {
|
|
mPages[pageNum].imageData.insert(mPages[pageNum].imageData.end(),
|
|
std::make_move_iterator(buffer.begin()),
|
|
std::make_move_iterator(buffer.end()));
|
|
}
|
|
|
|
returnValue = pclose(commandPipe);
|
|
#endif
|
|
const size_t imageDataSize {mPages[pageNum].imageData.size()};
|
|
#if defined(_WIN64)
|
|
if (!processReturnValue || (static_cast<int>(imageDataSize) <
|
|
mPages[pageNum].width * mPages[pageNum].height * 4)) {
|
|
#else
|
|
if (returnValue != 0 || (static_cast<int>(imageDataSize) <
|
|
mPages[pageNum].width * mPages[pageNum].height * 4)) {
|
|
#endif
|
|
LOG(LogError) << "Error reading PDF file";
|
|
mPages[pageNum].imageData.clear();
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
#if (DEBUG_PDF_CONVERSION)
|
|
LOG(LogDebug) << "Using cached texture for page: " << mCurrentPage;
|
|
#endif
|
|
}
|
|
|
|
mPageImage.reset();
|
|
mPageImage = std::make_unique<ImageComponent>(false, false);
|
|
mPageImage->setFlipY(true);
|
|
mPageImage->setLinearInterpolation(true);
|
|
mPageImage->setOrigin(0.5f, 0.5f);
|
|
if (mHelpInfoPosition == HelpInfoPosition::TOP) {
|
|
mPageImage->setPosition(mRenderer->getScreenWidth() / 2.0f,
|
|
(mRenderer->getScreenHeight() / 2.0f) + (mFrameHeight / 2.0f));
|
|
}
|
|
else if (mHelpInfoPosition == HelpInfoPosition::BOTTOM) {
|
|
mPageImage->setPosition(Renderer::getScreenWidth() / 2.0f,
|
|
(Renderer::getScreenHeight() / 2.0f) - (mFrameHeight / 2.0f));
|
|
}
|
|
else {
|
|
mPageImage->setPosition(mRenderer->getScreenWidth() / 2.0f,
|
|
mRenderer->getScreenHeight() / 2.0f);
|
|
}
|
|
|
|
float sizeReduction {0.0f};
|
|
if (mPages[pageNum].height / mScaleFactor > mRenderer->getScreenHeight() - mFrameHeight)
|
|
sizeReduction =
|
|
(mPages[pageNum].height / mScaleFactor) - (mRenderer->getScreenHeight() - mFrameHeight);
|
|
|
|
mPageImage->setMaxSize(
|
|
glm::vec2 {(mPages[pageNum].width / mScaleFactor) * mZoom,
|
|
((mPages[pageNum].height / mScaleFactor) * mZoom) - sizeReduction});
|
|
|
|
mPageImage->setRawImage(reinterpret_cast<const unsigned char*>(&mPages[pageNum].imageData[0]),
|
|
static_cast<size_t>(mPages[pageNum].width),
|
|
static_cast<size_t>(mPages[pageNum].height));
|
|
|
|
mPanAmount = std::min(mRenderer->getScreenWidth(), mRenderer->getScreenHeight()) * 0.1f;
|
|
|
|
#if (DEBUG_PDF_CONVERSION)
|
|
LOG(LogDebug) << "ABGR32 data stream size: " << mPages[pageNum].imageData.size();
|
|
#endif
|
|
}
|
|
|
|
void PDFViewer::render(const glm::mat4& /*parentTrans*/)
|
|
{
|
|
glm::mat4 trans {Renderer::getIdentity()};
|
|
mRenderer->setMatrix(trans);
|
|
|
|
// Render a black background below the game media.
|
|
mRenderer->drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(),
|
|
0x000000FF, 0x000000FF);
|
|
|
|
if (mZoom != 1.0f)
|
|
mPageImage->setPosition(mPageImage->getPosition() + (mPanOffset * mZoom));
|
|
|
|
if (mPageImage != nullptr)
|
|
mPageImage->render(trans);
|
|
|
|
if (mZoom != 1.0f)
|
|
mPageImage->setPosition(mPageImage->getPosition() - (mPanOffset * mZoom));
|
|
|
|
if (mHelpInfoPosition != HelpInfoPosition::DISABLED) {
|
|
// Render a dark gray frame behind the help info.
|
|
mRenderer->setMatrix(mRenderer->getIdentity());
|
|
mRenderer->drawRect(0.0f,
|
|
(mHelpInfoPosition == HelpInfoPosition::TOP ?
|
|
0.0f :
|
|
Renderer::getScreenHeight() - mFrameHeight),
|
|
Renderer::getScreenWidth(), mFrameHeight, 0x222222FF, 0x222222FF);
|
|
mHelp->render(trans);
|
|
mEntryNumText->render(trans);
|
|
}
|
|
}
|
|
|
|
std::vector<HelpPrompt> PDFViewer::getHelpPrompts()
|
|
{
|
|
std::vector<HelpPrompt> prompts;
|
|
if (mZoom > 1.0f) {
|
|
prompts.push_back(HelpPrompt("up/down/left/right", "pan"));
|
|
prompts.push_back(HelpPrompt("ltrt", "reset"));
|
|
}
|
|
else {
|
|
prompts.push_back(HelpPrompt("left/right", "browse"));
|
|
prompts.push_back(HelpPrompt("down", "game media"));
|
|
prompts.push_back(HelpPrompt("lt", "first"));
|
|
prompts.push_back(HelpPrompt("rt", "last"));
|
|
}
|
|
|
|
prompts.push_back(HelpPrompt("lr", "zoom"));
|
|
|
|
return prompts;
|
|
}
|
|
|
|
void PDFViewer::showNextPage()
|
|
{
|
|
if (mCurrentPage == mPageCount)
|
|
return;
|
|
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
|
++mCurrentPage;
|
|
mEntryNumText->setText("PAGE " + std::to_string(mCurrentPage) + " OF " + mEntryCount);
|
|
convertPage(mCurrentPage);
|
|
}
|
|
|
|
void PDFViewer::showPreviousPage()
|
|
{
|
|
if (mCurrentPage == 1)
|
|
return;
|
|
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
|
--mCurrentPage;
|
|
mEntryNumText->setText("PAGE " + std::to_string(mCurrentPage) + " OF " + mEntryCount);
|
|
convertPage(mCurrentPage);
|
|
}
|
|
|
|
void PDFViewer::navigateUp()
|
|
{
|
|
if (mZoom != 1.0f) {
|
|
if (mPanOffset.y * mZoom <= mPageImage->getSize().y / 2.0f)
|
|
mPanOffset.y += mPanAmount;
|
|
}
|
|
}
|
|
|
|
void PDFViewer::navigateDown()
|
|
{
|
|
if (mZoom != 1.0f) {
|
|
if (mPanOffset.y * mZoom >= -(mPageImage->getSize().y / 2.0f))
|
|
mPanOffset.y -= mPanAmount;
|
|
}
|
|
else {
|
|
launchMediaViewer();
|
|
}
|
|
}
|
|
|
|
void PDFViewer::navigateLeft()
|
|
{
|
|
if (mZoom != 1.0f) {
|
|
if (mPanOffset.x * mZoom <= mPageImage->getSize().x / 2.0f)
|
|
mPanOffset.x += mPanAmount;
|
|
}
|
|
else {
|
|
mPanOffset = {0.0f, 0.0f, 0.0f};
|
|
showPreviousPage();
|
|
}
|
|
}
|
|
|
|
void PDFViewer::navigateRight()
|
|
{
|
|
if (mZoom != 1.0f) {
|
|
if (mPanOffset.x * mZoom > -(mPageImage->getSize().x / 2.0f))
|
|
mPanOffset.x -= mPanAmount;
|
|
}
|
|
else {
|
|
mPanOffset = {0.0f, 0.0f, 0.0f};
|
|
showNextPage();
|
|
}
|
|
}
|
|
|
|
void PDFViewer::navigateRightShoulder()
|
|
{
|
|
if (mZoom <= 2.5f)
|
|
mZoom += 0.5f;
|
|
|
|
if (mZoom == 1.5f)
|
|
mHelp->setPrompts(getHelpPrompts());
|
|
|
|
convertPage(mCurrentPage);
|
|
}
|
|
|
|
void PDFViewer::navigateLeftShoulder()
|
|
{
|
|
if (mZoom == 1.0f)
|
|
mPanOffset = {0.0f, 0.0f, 0.0f};
|
|
|
|
if (mZoom >= 1.5f)
|
|
mZoom -= 0.5f;
|
|
|
|
if (mZoom == 1.0f)
|
|
mHelp->setPrompts(getHelpPrompts());
|
|
|
|
convertPage(mCurrentPage);
|
|
}
|
|
|
|
void PDFViewer::navigateLeftTrigger()
|
|
{
|
|
if (mZoom != 1.0f) {
|
|
mZoom = 1.0f;
|
|
mPanOffset = {0.0f, 0.0f, 0.0f};
|
|
mHelp->setPrompts(getHelpPrompts());
|
|
convertPage(mCurrentPage);
|
|
return;
|
|
}
|
|
|
|
if (mCurrentPage == 1)
|
|
return;
|
|
|
|
mPanOffset = {0.0f, 0.0f, 0.0f};
|
|
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
|
mCurrentPage = 1;
|
|
mEntryNumText->setText("PAGE " + std::to_string(mCurrentPage) + " OF " + mEntryCount);
|
|
convertPage(mCurrentPage);
|
|
}
|
|
|
|
void PDFViewer::navigateRightTrigger()
|
|
{
|
|
if (mZoom != 1.0f) {
|
|
mZoom = 1.0f;
|
|
mPanOffset = {0.0f, 0.0f, 0.0f};
|
|
mHelp->setPrompts(getHelpPrompts());
|
|
convertPage(mCurrentPage);
|
|
return;
|
|
}
|
|
|
|
if (mCurrentPage == mPageCount)
|
|
return;
|
|
|
|
mPanOffset = {0.0f, 0.0f, 0.0f};
|
|
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
|
mCurrentPage = mPageCount;
|
|
mEntryNumText->setText("PAGE " + std::to_string(mCurrentPage) + " OF " + mEntryCount);
|
|
convertPage(mCurrentPage);
|
|
}
|