/* * roar -- mIRC art editor for Windows & Linux * Copyright (C) 2018, 2019 Lucio Andrés Illanes Albornoz * * 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 . */ #include #include #if defined(_WIN32) #include #elif defined(__linux__) #include #include #include #else #error Unknown platform #endif /* defined(_WIN32), defined(__linux__) */ #include #include #include "../libtools/ToolCircle.hpp" #include "../libtools/ToolCursor.hpp" #include "../libtools/ToolErase.hpp" #include "../libtools/ToolFill.hpp" #include "../libtools/ToolLine.hpp" #include "../libtools/ToolObject.hpp" #include "../libtools/ToolPickColour.hpp" #include "../libtools/ToolRect.hpp" #include "../libtools/ToolText.hpp" #include "CanvasEditorWindow.hpp" /* * Private variables */ std::map s_colour_names = { {-1, L"Transparent colour"}, { 0, L"Bright White"}, { 1, L"Black"}, { 2, L"Light Blue"}, { 3, L"Green"}, { 4, L"Red"}, { 5, L"Light Red"}, { 6, L"Pink"}, { 7, L"Yellow"}, { 8, L"Light Yellow"}, { 9, L"Light Green"}, {10, L"Cyan"}, {11, L"Light Cyan"}, {12, L"Blue"}, {13, L"Light Pink"}, {14, L"Grey"}, {15, L"Light Grey"}, }; /* * Private subroutines */ static Qt::MouseButton translateMouseButton(Tool::MOUSE_BUTTON button); static Tool::MOUSE_BUTTON translateMouseButtonQt(Qt::MouseButton buttonQt); static Tool::MOUSE_BUTTON translateMouseButtonsQt(Qt::MouseButtons buttonsQt); static Qt::MouseButton translateMouseButton( Tool::MOUSE_BUTTON button ) { switch (button) { case Tool::MBUTTON_LEFT: return Qt::MouseButton::LeftButton; case Tool::MBUTTON_MIDDLE: return Qt::MouseButton::MiddleButton; case Tool::MBUTTON_RIGHT: return Qt::MouseButton::RightButton; default: return Qt::NoButton; } } static Tool::MOUSE_BUTTON translateMouseButtonQt( Qt::MouseButton buttonQt ) { switch (buttonQt) { case Qt::MouseButton::LeftButton: return Tool::MBUTTON_LEFT; case Qt::MouseButton::MiddleButton: return Tool::MBUTTON_MIDDLE; case Qt::MouseButton::RightButton: return Tool::MBUTTON_RIGHT; default: return Tool::MBUTTON_NONE; } } static Tool::MOUSE_BUTTON translateMouseButtonsQt( Qt::MouseButtons buttonsQt ) { switch (buttonsQt) { case Qt::MouseButton::LeftButton: return Tool::MBUTTON_LEFT; case Qt::MouseButton::MiddleButton: return Tool::MBUTTON_MIDDLE; case Qt::MouseButton::RightButton: return Tool::MBUTTON_RIGHT; default: return Tool::MBUTTON_NONE; } } /* * Public class methods */ CanvasEditorWindow::CanvasEditorWindow( Canvas::BRUSH brush, QWidget * parent ) : QMainWindow(parent), brush(brush), brushPos{0, 0}, currentColourAction(nullptr), currentColourBgAction(nullptr), currentOperatorAction(nullptr), currentToolAction(nullptr), currentTool(nullptr), cursorCells(), dirty(false), fileName(L""), lastEvent(CanvasWidget::ETYPE_NONE), mouseState{MOUSE_STATE_EMPTY}, statusLabel(nullptr) { QStringList arguments; auto mainLayout = new QHBoxLayout(); auto scrollArea = new QScrollArea(); Rtl::STATUS status; this->canvasWidget = new CanvasWidget(); this->canvasWidget->setEventDispatcher((CanvasWidget::EVENT_DISPATCHER)[&](QKeyEvent *k, QMouseEvent *m, QWheelEvent *w, CanvasWidget::EVENT_TYPE t) {return this->updateToolState(k, m, w, t); }); this->canvasWidget->setFocusPolicy(Qt::FocusPolicy::ClickFocus); this->canvasWidget->setFocus(Qt::FocusReason::ActiveWindowFocusReason); this->canvasWidget->setMouseTracking(true); scrollArea->setWidget(this->canvasWidget); ui.setupUi(this); for (const auto& action : { this->ui.actionDecreaseBrushHeight, this->ui.actionDecreaseBrushSize, this->ui.actionDecreaseBrushWidth, this->ui.actionIncreaseBrushHeight, this->ui.actionIncreaseBrushSize, this->ui.actionIncreaseBrushWidth}) (void)this->connect(action, &QAction::triggered, this, &CanvasEditorWindow::on_actionBrushSize_triggered); for (const auto& action : { this->ui.actionDecreaseCanvasHeight, this->ui.actionDecreaseCanvasSize, this->ui.actionDecreaseCanvasWidth, this->ui.actionIncreaseCanvasHeight, this->ui.actionIncreaseCanvasSize, this->ui.actionIncreaseCanvasWidth}) (void)this->connect(action, &QAction::triggered, this, &CanvasEditorWindow::on_actionCanvasSize_triggered); for (const auto& action : { this->ui.actionColour00, this->ui.actionColour01, this->ui.actionColour02, this->ui.actionColour03, this->ui.actionColour04, this->ui.actionColour05, this->ui.actionColour06, this->ui.actionColour07, this->ui.actionColour08, this->ui.actionColour09, this->ui.actionColour10, this->ui.actionColour11, this->ui.actionColour12, this->ui.actionColour13, this->ui.actionColour14, this->ui.actionColour15, this->ui.actionColourTransparent}) (void)this->connect(action, &QAction::triggered, this, &CanvasEditorWindow::on_actionColour_triggered); for (const auto& action : { this->ui.actionColourBg00, this->ui.actionColourBg01, this->ui.actionColourBg02, this->ui.actionColourBg03, this->ui.actionColourBg04, this->ui.actionColourBg05, this->ui.actionColourBg06, this->ui.actionColourBg07, this->ui.actionColourBg08, this->ui.actionColourBg09, this->ui.actionColourBg10, this->ui.actionColourBg11, this->ui.actionColourBg12, this->ui.actionColourBg13, this->ui.actionColourBg14, this->ui.actionColourBg15, this->ui.actionColourBgTransparent}) (void)this->connect(action, &QAction::triggered, this, &CanvasEditorWindow::on_actionColourBg_triggered); (void)this->connect(this->ui.actionFlipColours, &QAction::triggered, this, &CanvasEditorWindow::on_actionFlipColours_triggered); for (auto &action : { this->ui.actionFlip, this->ui.actionFlipHorizontally, this->ui.actionInvertColours, this->ui.actionRotate, this->ui.actionTile}) (void)this->connect(action, &QAction::triggered, this, &CanvasEditorWindow::on_actionOperator_triggered); (void)this->connect(this->ui.actionRedo, &QAction::triggered, this, &CanvasEditorWindow::on_actionReUndo_triggered); (void)this->connect(this->ui.actionUndo, &QAction::triggered, this, &CanvasEditorWindow::on_actionReUndo_triggered); for (auto& action : { this->ui.actionCursor, this->ui.actionRectangle, this->ui.actionCircle, this->ui.actionFill, this->ui.actionLine, this->ui.actionText, this->ui.actionObject, this->ui.actionErase, this->ui.actionPickColour}) (void)this->connect(action, &QAction::triggered, this, &CanvasEditorWindow::on_actionTool_triggered); mainLayout->addWidget(scrollArea); this->setRandomAppIcon(status); this->statusLabel = new QLabel(this); this->ui.statusbar->addPermanentWidget(this->statusLabel, 1); this->ui.centralwidget->setLayout(mainLayout); arguments = QCoreApplication::arguments(); if (arguments.size() > 1) this->importMiRCfromFile(status, arguments[1].toStdWString(), true, true); else this->importNew(status, true, true); emit this->ui.actionCursor->triggered(); emit this->ui.actionColour03->triggered(); emit this->ui.actionColourBgTransparent->triggered(); this->updateStatusBar(); } CanvasEditorWindow::~CanvasEditorWindow( ) { /* XXX */ } /* * Private class methods */ void CanvasEditorWindow::applyLastTool( ) { Tool::TOOL_CELLS rc(false, Tool::TOOL_CELLS_PAIR(Canvas::CELL_LIST(), Canvas::CELL_LIST())); if (this->currentTool) { if (this->mouseState.dragState.status >= Tool::DSTATUS_BEGIN) rc = this->currentTool->drag(this->brush, this->brushPos, this->canvasWidget->canvas, this->mouseState.dragState, this->brushPos); else rc = this->currentTool->cursor(this->brush, this->brushPos, this->canvasWidget->canvas, this->brushPos); } if (rc) { (void)this->applyTool(rc); this->updateStatusBar(); } } Tool::TOOL_CELLS CanvasEditorWindow::applyTool( Tool::TOOL_CELLS rc ) { bool commitfl = false; Rtl::Either rc_(false, Canvas::RECT()); if (rc.right.first.size()) { this->dirty = commitfl = this->canvasWidget->canvas.commit(rc.right.first, false); if (commitfl && (this->canvasWidget->canvas.cellsUndo.size > 0)) this->ui.actionUndo->setEnabled(true); else this->ui.actionUndo->setEnabled(false); if ((rc_ = this->canvasWidget->renderCells(false, rc.right.first))) this->canvasWidget->update(QRect(rc_.right.p0.x, rc_.right.p0.y, rc_.right.p1.x, rc_.right.p1.y)); } if (rc.right.second.size()) { this->cursorHide(); this->cursorShow(rc.right.second); } return rc; } void CanvasEditorWindow::cursorHide( ) { Canvas::CELL_LIST cells; Rtl::Either rc(false, Canvas::RECT()); for (auto& cell : this->cursorCells) { if ((cell.p.x < this->canvasWidget->canvas.size.w) && (cell.p.y < this->canvasWidget->canvas.size.h)) cells.push_back(this->canvasWidget->canvas.cells[cell.p.y][cell.p.x]); } if (cells.size() && (rc = this->canvasWidget->renderCells(false, cells))) this->canvasWidget->update(QRect(rc.right.p0.x, rc.right.p0.y, rc.right.p1.x, rc.right.p1.y)); } void CanvasEditorWindow::cursorShow( const Canvas::CELL_LIST& cells ) { Rtl::Either rc(false, Canvas::RECT()); if ((rc = this->canvasWidget->renderCells(true, cells))) { this->cursorCells = cells; this->canvasWidget->update(QRect(rc.right.p0.x, rc.right.p0.y, rc.right.p1.x, rc.right.p1.y)); } } bool CanvasEditorWindow::exportMiRCtoFile( Rtl::STATUS& pstatus, const std::wstring& fileName, bool printError, bool setTitle ) { int fd = -1; std::string fileData; std::wstring fileDataW; #if defined(__linux__) std::string fileName_; #endif /* defined(__linux__) */ #if defined(_WIN32) int nwritten = -2; #elif defined(__linux__) ssize_t nwritten = -2; #endif /* defined(_WIN32), defined(__linux__) */ Rtl::STATUS status = Rtl::STATUS_NONE_SUCCESS; try { #if defined(_WIN32) if (STATUS_BIND_LIBC(status, fd = _wopen(fileName.c_str(), _O_BINARY | _O_CREAT | _O_WRONLY, 0600)) #elif defined(__linux__) if (Rtl::convert_wstring(&status, fileName, fileName_) && STATUS_BIND_LIBC(status, fd = ::open(fileName_.c_str(), O_CREAT | O_WRONLY, 0600)) #endif /* defined(_WIN32), defined(__linux__) */ && this->canvasWidget->canvas.export_(status, fileDataW, Canvas::DTYPE_MIRC) && Rtl::convert_wstring(&status, fileDataW, fileData) #if defined(_WIN32) && STATUS_BIND_LIBC(status, nwritten = _write(fd, &fileData[0], (unsigned int)fileData.length())) #elif defined(__linux__) && STATUS_BIND_LIBC(status, nwritten = ::write(fd, &fileData[0], (size_t)fileData.length())) #endif /* defined(_WIN32), defined(__linux__) */ && STATUS_BIND_ROAR(status, "short write", ((size_t)nwritten == fileData.length()))) { this->dirty = false, this->fileName = fileName; if (setTitle) this->setWindowTitle(QString("%1 - roar").arg(QString().fromUtf16((const ushort *)this->fileName.c_str()))); } } catch (std::bad_alloc) { status = Rtl::STATUS_POSIX(ENOMEM); } if (fd != -1) #if defined(_WIN32) _close(fd); #elif defined(__linux__) ::close(fd); #endif /* defined(_WIN32), defined(__linux__) */ printf("fileData.length()=%zu fileDataW.length()=%zu nwritten=%d\n", fileData.length(), fileDataW.length(), nwritten); if (!status && printError) QMessageBox::critical(this, "File error", QString("Failed to open %1: %2").arg(QString().fromUtf16((const ushort *)fileName.c_str())).arg(QString().fromUtf16((const ushort *)Rtl::perror(status).c_str()))); return (bool)(pstatus = status); } bool CanvasEditorWindow::importMiRCfromFile( Rtl::STATUS& pstatus, const std::wstring& fileName, bool printError, bool setTitle ) { int fd = -1; std::string fileData; std::wstring fileDataW; #if defined(__linux__) std::string fileName_; #endif /* defined(__linux__) */ int64_t fileSize = 0; #if defined(_WIN32) int nread; #elif defined(__linux__) ssize_t nread; #endif /* defined(_WIN32), defined(__linux__) */ Rtl::STATUS status = Rtl::STATUS_NONE_SUCCESS; try { #if defined(_WIN32) if (STATUS_BIND_LIBC(status, fd = _wopen(fileName.c_str(), _O_BINARY | _O_RDONLY)) && STATUS_BIND_LIBC(status, _lseeki64(fd, 0, SEEK_END)) && STATUS_BIND_LIBC(status, fileSize = _telli64(fd)) #elif defined(__linux__) if (Rtl::convert_wstring(&status, fileName, fileName_) && STATUS_BIND_LIBC(status, fd = ::open(fileName_.c_str(), O_RDONLY)) && STATUS_BIND_LIBC(status, lseek(fd, 0, SEEK_END)) && STATUS_BIND_LIBC(status, fileSize = lseek(fd, 0, SEEK_CUR)) #endif /* defined(_WIN32), defined(__linux__) */ && STATUS_BIND_TRUE(fileData.resize(fileSize)) #if defined(_WIN32) && STATUS_BIND_LIBC(status, _lseeki64(fd, 0, SEEK_SET)) && STATUS_BIND_LIBC(status, nread = _read(fd, &fileData[0], fileSize)) #elif defined(__linux__) && STATUS_BIND_LIBC(status, lseek(fd, 0, SEEK_CUR)) && STATUS_BIND_LIBC(status, nread = ::read(fd, &fileData[0], fileSize)) #endif /* defined(_WIN32), defined(__linux__) */ && STATUS_BIND_ROAR(status, "short read", (size_t)nread == (size_t)fileSize) && Rtl::convert_cstring(&status, fileData, fileDataW) && this->canvasWidget->import(status, fileDataW, Canvas::DTYPE_MIRC)) { this->dirty = false, this->fileName = fileName; this->ui.actionRedo->setEnabled(false), this->ui.actionUndo->setEnabled(false); if (setTitle) this->setWindowTitle(QString("%1 - roar").arg(QString().fromUtf16((const ushort *)this->fileName.c_str()))); } } catch (std::bad_alloc) { status = Rtl::STATUS_POSIX(ENOMEM); } if (fd != -1) #if defined(_WIN32) _close(fd); #elif defined(__linux__) ::close(fd); #endif /* defined(_WIN32), defined(__linux__) */ if (!status && printError) QMessageBox::critical(this, "File error", QString("Failed to open %1: %2").arg(QString().fromUtf16((const ushort *)fileName.c_str())).arg(QString().fromUtf16((const ushort *)Rtl::perror(status).c_str()))); return (bool)(pstatus = status); } bool CanvasEditorWindow::importNew( Rtl::STATUS& pstatus, bool printError, bool setTitle ) { Canvas::CELL_MAP cell_map; Rtl::STATUS status = Rtl::STATUS_NONE_SUCCESS; try { cell_map = Canvas::CELL_MAP(this->canvasWidget->sizeDefault.h, Canvas::CELL_LIST(this->canvasWidget->sizeDefault.w)); for (uint64_t y = 0; y < cell_map.size(); y++) { for (uint64_t x = 0; x < cell_map[y].size(); x++) cell_map[y][x] = Canvas::CELL{Canvas::CATTR_NONE, this->brush.colours.bg, this->brush.colours.fg, {x, y}, L" "}; } if (this->canvasWidget->import(status, cell_map, this->canvasWidget->sizeDefault)) { this->dirty = false, this->fileName = L""; this->ui.actionRedo->setEnabled(false), this->ui.actionUndo->setEnabled(false); if (setTitle) this->setWindowTitle(QString("(Untitled) - roar")); } } catch (std::bad_alloc) { status = Rtl::STATUS_POSIX(ENOMEM); } if (!status && printError) QMessageBox::critical(this, "Canvas error", QString("%1").arg(QString().fromUtf16((const ushort *)Rtl::perror(status).c_str()))); return (bool)(pstatus = status); } void CanvasEditorWindow::on_actionBrushSize_triggered( bool checked ) { QAction * object = (QAction *)sender(); bool updatefl = false; (void)checked; if (object == this->ui.actionDecreaseBrushHeight) { if (this->brush.size.h > 0) this->brush.size.h--, updatefl = true; } else if (object == this->ui.actionDecreaseBrushSize) { if (this->brush.size.h > 0) this->brush.size.h--, updatefl = true; if (this->brush.size.w > 0) this->brush.size.w--, updatefl = true; } else if (object == this->ui.actionDecreaseBrushWidth) { if (this->brush.size.w > 0) this->brush.size.w--, updatefl = true; } else if (object == this->ui.actionIncreaseBrushHeight) { this->brush.size.h++, updatefl = true; } else if (object == this->ui.actionIncreaseBrushSize) { this->brush.size.h++, this->brush.size.w++, updatefl = true; } else if (object == this->ui.actionIncreaseBrushWidth) { this->brush.size.w++, updatefl = true; } if (updatefl) this->applyLastTool(); } void CanvasEditorWindow::on_actionCanvasSize_triggered( bool checked ) { QAction * object = (QAction *)sender(); (void)checked; if (object == this->ui.actionDecreaseCanvasHeight) { } else if (object == this->ui.actionDecreaseCanvasSize) { } else if (object == this->ui.actionDecreaseCanvasWidth) { } else if (object == this->ui.actionIncreaseCanvasHeight) { } else if (object == this->ui.actionIncreaseCanvasSize) { } else if (object == this->ui.actionIncreaseCanvasWidth) { } } void CanvasEditorWindow::on_actionColour_triggered( bool checked ) { QAction * object = (QAction *)sender(); bool updatefl = false; (void)checked; if (this->currentColourAction) this->currentColourAction->setChecked(false); this->currentColourAction = object; this->currentColourAction->setChecked(true); if (object == this->ui.actionColour00) this->brush.colours.fg = 0, updatefl = true; else if (object == this->ui.actionColour01) this->brush.colours.fg = 1, updatefl = true; else if (object == this->ui.actionColour02) this->brush.colours.fg = 2, updatefl = true; else if (object == this->ui.actionColour03) this->brush.colours.fg = 3, updatefl = true; else if (object == this->ui.actionColour04) this->brush.colours.fg = 4, updatefl = true; else if (object == this->ui.actionColour05) this->brush.colours.fg = 5, updatefl = true; else if (object == this->ui.actionColour06) this->brush.colours.fg = 6, updatefl = true; else if (object == this->ui.actionColour07) this->brush.colours.fg = 7, updatefl = true; else if (object == this->ui.actionColour08) this->brush.colours.fg = 8, updatefl = true; else if (object == this->ui.actionColour09) this->brush.colours.fg = 9, updatefl = true; else if (object == this->ui.actionColour10) this->brush.colours.fg = 10, updatefl = true; else if (object == this->ui.actionColour11) this->brush.colours.fg = 11, updatefl = true; else if (object == this->ui.actionColour12) this->brush.colours.fg = 12, updatefl = true; else if (object == this->ui.actionColour13) this->brush.colours.fg = 13, updatefl = true; else if (object == this->ui.actionColour14) this->brush.colours.fg = 14, updatefl = true; else if (object == this->ui.actionColour15) this->brush.colours.fg = 15, updatefl = true; else if (object == this->ui.actionColourTransparent) this->brush.colours.fg = -1, updatefl = true; if (updatefl) this->applyLastTool(); } void CanvasEditorWindow::on_actionColourBg_triggered( bool checked ) { QAction * object = (QAction *)sender(); bool updatefl = false; (void)checked; if (this->currentColourBgAction) this->currentColourBgAction->setChecked(false); this->currentColourBgAction = object; this->currentColourBgAction->setChecked(true); if (object == this->ui.actionColourBg00) this->brush.colours.bg = 0, updatefl = true; else if (object == this->ui.actionColourBg01) this->brush.colours.bg = 1, updatefl = true; else if (object == this->ui.actionColourBg02) this->brush.colours.bg = 2, updatefl = true; else if (object == this->ui.actionColourBg03) this->brush.colours.bg = 3, updatefl = true; else if (object == this->ui.actionColourBg04) this->brush.colours.bg = 4, updatefl = true; else if (object == this->ui.actionColourBg05) this->brush.colours.bg = 5, updatefl = true; else if (object == this->ui.actionColourBg06) this->brush.colours.bg = 6, updatefl = true; else if (object == this->ui.actionColourBg07) this->brush.colours.bg = 7, updatefl = true; else if (object == this->ui.actionColourBg08) this->brush.colours.bg = 8, updatefl = true; else if (object == this->ui.actionColourBg09) this->brush.colours.bg = 9, updatefl = true; else if (object == this->ui.actionColourBg10) this->brush.colours.bg = 10, updatefl = true; else if (object == this->ui.actionColourBg11) this->brush.colours.bg = 11, updatefl = true; else if (object == this->ui.actionColourBg12) this->brush.colours.bg = 12, updatefl = true; else if (object == this->ui.actionColourBg13) this->brush.colours.bg = 13, updatefl = true; else if (object == this->ui.actionColourBg14) this->brush.colours.bg = 14, updatefl = true; else if (object == this->ui.actionColourBg15) this->brush.colours.bg = 15, updatefl = true; else if (object == this->ui.actionColourBgTransparent) this->brush.colours.bg = -1, updatefl = true; if (updatefl) this->applyLastTool(); } void CanvasEditorWindow::on_actionFlipColours_triggered( bool checked ) { (void)checked; std::swap(this->brush.colours.bg, this->brush.colours.fg); this->applyLastTool(); } void CanvasEditorWindow::on_actionOperator_triggered( bool checked ) { QAction * object = (QAction *)sender(); (void)checked; if (this->currentOperatorAction) this->currentOperatorAction->setChecked(false); this->currentOperatorAction = object; this->currentOperatorAction->setChecked(true); if (object == this->ui.actionFlip) { } else if (object == this->ui.actionFlipHorizontally) { } else if (object == this->ui.actionInvertColours) { } else if (object == this->ui.actionRotate) { } else if (object == this->ui.actionTile) { } } void CanvasEditorWindow::on_actionReUndo_triggered( bool checked ) { const Canvas::CELL_LIST * cells = nullptr; QAction * object = (QAction *)sender(); Rtl::Either rc(false, Canvas::RECT()); (void)checked; if (object == this->ui.actionRedo) { cells = this->canvasWidget->canvas.redo(); } else if (object == this->ui.actionUndo) { cells = this->canvasWidget->canvas.undo(); } if (this->canvasWidget->canvas.cellsUndo.index > 0) this->ui.actionRedo->setEnabled(true); else this->ui.actionRedo->setEnabled(false); if (this->canvasWidget->canvas.cellsUndo.index < this->canvasWidget->canvas.cellsUndo.size) this->ui.actionUndo->setEnabled(true); else this->ui.actionUndo->setEnabled(false); if (cells && (rc = this->canvasWidget->renderCells(false, *cells))) this->canvasWidget->update(QRect(rc.right.p0.x, rc.right.p0.y, rc.right.p1.x, rc.right.p1.y)); } void CanvasEditorWindow::on_actionTool_triggered( bool checked ) { QAction * object = (QAction *)sender(); (void)checked; if (this->currentToolAction) this->currentToolAction->setChecked(false); this->currentToolAction = object; this->currentToolAction->setChecked(true); if (object == this->ui.actionCircle) this->currentTool = std::make_unique(); else if (object == this->ui.actionCursor) this->currentTool = std::make_unique(); else if (object == this->ui.actionObject) this->currentTool = std::make_unique(); else if (object == this->ui.actionRectangle) this->currentTool = std::make_unique(); else if (object == this->ui.actionLine) this->currentTool = std::make_unique(); else if (object == this->ui.actionText) this->currentTool = std::make_unique(); else if (object == this->ui.actionPickColour) this->currentTool = std::make_unique(); else if (object == this->ui.actionErase) this->currentTool = std::make_unique(); else if (object == this->ui.actionFill) this->currentTool = std::make_unique(); this->updateStatusBar(); } bool CanvasEditorWindow::promptSaveChanges( ) { std::wstring fileName; bool rc; fileName = (this->fileName == L"") ? L"(Untitled)" : this->fileName; switch (QMessageBox::question( this, "roar", QString("Do you want to save changes to %1?").arg(QString().fromUtf16((const ushort *)fileName.c_str())), QMessageBox::Cancel | QMessageBox::Discard | QMessageBox::Save, QMessageBox::Save)) { case QMessageBox::Cancel: rc = false; break; case QMessageBox::Discard: rc = true; break; case QMessageBox::Save: rc = this->saveAsMiRC("", true, false); break; default: rc = false; } return rc; } bool CanvasEditorWindow::saveAsMiRC( const QString& fileName, bool printError, bool promptName ) { QString fileName_; std::wstring fileName_W; Rtl::STATUS status = Rtl::STATUS_NONE_SUCCESS; if (fileName == "") { if (promptName || (this->fileName == L"")) { if ((fileName_ = QFileDialog::getSaveFileName(this, tr("Save..."), QString(""), tr("mIRC art files (*.txt);;All Files (*.*)"))) != "") { if (Rtl::convert_cstring(&status, fileName_.toStdString(), fileName_W)) this->fileName = fileName_W; } else return false; } } else Rtl::convert_cstring(&status, fileName.toStdString(), fileName_W); if (status) this->exportMiRCtoFile(status, this->fileName, true, true); else if (printError) QMessageBox::critical(this, "Save...", QString("%1").arg(QString().fromUtf16((const ushort *)Rtl::perror(status).c_str()))); return (bool)status; } bool CanvasEditorWindow::setRandomAppIcon( Rtl::STATUS& pstatus ) { struct _wfinddata_t fileinfo; intptr_t findHandle; QIcon icon; std::vector iconNames; QPixmap pixmap; QString pixmapFileName; int rc; Rtl::STATUS status = Rtl::STATUS_NONE_SUCCESS; if (STATUS_BIND_LIBC(status, findHandle = _wfindfirst(L"assets\\images\\logo*.bmp", &fileinfo))) { while (STATUS_BIND_LIBC(status, (rc = _wfindnext(findHandle, &fileinfo)))) iconNames.push_back(L"assets\\images\\" + std::wstring(fileinfo.name)); if (!status && (status.cond == ENOENT)) status = Rtl::STATUS_NONE_SUCCESS; _findclose(findHandle); } pixmapFileName = QString().fromUtf16((const ushort *)iconNames[rand() % iconNames.size()].c_str()); pixmap = QPixmap(pixmapFileName); icon = QIcon(pixmap); this->setWindowIcon(icon); return (bool)(pstatus = status); } Canvas::POINT CanvasEditorWindow::translateMousePointQt( QPoint pointQt ) { Canvas::POINT point = {0, 0}; if (pointQt.x() > 0) point.x = pointQt.x() / this->canvasWidget->cellSize.w; if (pointQt.y() > 0) point.y = pointQt.y() / this->canvasWidget->cellSize.h; return point; } void CanvasEditorWindow::updateStatusBar( ) { std::wstring statusText; std::wstringstream statusText_stream; statusText_stream << L"X: " << setfillw(L'0', 3) << this->brushPos.x << L" " << L"Y: " << setfillw(L'0', 3) << this->brushPos.y << L" | " << L"W: " << setfillw(L'0', 3) << this->canvasWidget->canvas.size.w << L" " << L"H: " << setfillw(L'0', 3) << this->canvasWidget->canvas.size.h << L" | " << L"B: " << setfillw(L'0', 2) << this->brush.size.w << L"x" << setfillw(L'0', 2) << this->brush.size.h << L" | " << L"FG " << setfillw(L'0', 2) << this->brush.colours.fg << L" (" << s_colour_names[this->brush.colours.fg] << L"), " << L"BG " << setfillw(L'0', 2) << this->brush.colours.bg << L" (" << s_colour_names[this->brush.colours.bg]; if (this->currentTool) statusText_stream << L" | T: " << this->currentTool->name; if (this->dirty) statusText_stream << " | *"; statusText = statusText_stream.str(); this->statusLabel->setText(QString().fromUtf16((const ushort *)statusText.c_str())); } bool CanvasEditorWindow::updateToolState( QKeyEvent * keyEvent, QMouseEvent * mouseEvent, QWheelEvent * wheelEvent, CanvasWidget::EVENT_TYPE type ) { QPoint angleDelta; Tool::MOUSE_BUTTON button = Tool::MBUTTON_NONE; Qt::MouseButtons buttonsQt; Canvas::POINT canvasPos{0, 0}; Qt::KeyboardModifiers modifiers; Tool::TOOL_CELLS rc(false, Tool::TOOL_CELLS_PAIR(Canvas::CELL_LIST(), Canvas::CELL_LIST())); bool updatefl = false; (void)keyEvent; if (!this->currentTool) return false; switch (type) { case CanvasWidget::ETYPE_KEYBOARD_PRESS: case CanvasWidget::ETYPE_KEYBOARD_RELEASE: break; case CanvasWidget::ETYPE_MOUSE_MOVE: case CanvasWidget::ETYPE_MOUSE_PRESS: case CanvasWidget::ETYPE_MOUSE_RELEASE: button = translateMouseButtonQt(mouseEvent->button()), canvasPos = translateMousePointQt(mouseEvent->localPos().toPoint()); modifiers = mouseEvent->modifiers(); break; case CanvasWidget::ETYPE_MOUSE_WHEEL: angleDelta = wheelEvent->angleDelta(), modifiers = wheelEvent->modifiers(); break; default: break; } switch (type) { case CanvasWidget::ETYPE_MOUSE_MOVE: if (!this->canvasWidget->hasFocus()) this->canvasWidget->setFocus(Qt::FocusReason::ActiveWindowFocusReason); buttonsQt = mouseEvent->buttons(); switch (this->mouseState.dragState.status) { case Tool::DSTATUS_NONE: if (this->mouseState.lastPos != canvasPos) { if (buttonsQt == Qt::NoButton) { rc = this->currentTool->cursor(this->brush, this->brushPos, this->canvasWidget->canvas, canvasPos); this->mouseState.lastPos = canvasPos; } else if ((button = translateMouseButtonsQt(buttonsQt)) != Tool::MBUTTON_NONE) { rc = this->currentTool->click(this->brush, this->brushPos, button, this->canvasWidget->canvas, canvasPos); this->mouseState.lastPos = canvasPos; } } break; case Tool::DSTATUS_BEGIN: if (this->mouseState.dragState.begin != canvasPos) { if (buttonsQt == translateMouseButton(this->mouseState.dragState.button)) { this->mouseState.dragState.status = Tool::DSTATUS_MOVE; rc = this->currentTool->drag(this->brush, this->brushPos, this->canvasWidget->canvas, this->mouseState.dragState, canvasPos); this->mouseState.lastPos = canvasPos; } } break; case Tool::DSTATUS_MOVE: if (this->mouseState.lastPos != canvasPos) { rc = this->currentTool->drag(this->brush, this->brushPos, this->canvasWidget->canvas, this->mouseState.dragState, canvasPos); this->mouseState.lastPos = canvasPos; } break; default: break; } mouseEvent->accept(); break; case CanvasWidget::ETYPE_MOUSE_PRESS: switch (this->mouseState.dragState.status) { case Tool::DSTATUS_NONE: if (modifiers == Qt::ControlModifier) { this->mouseState.dragState.begin = canvasPos; this->mouseState.dragState.button = button; this->mouseState.dragState.status = Tool::DSTATUS_BEGIN; this->mouseState.lastPos = canvasPos; } break; default: break; } mouseEvent->accept(); break; case CanvasWidget::ETYPE_MOUSE_RELEASE: switch (this->mouseState.dragState.status) { case Tool::DSTATUS_BEGIN: this->mouseState.dragState = Tool::DRAG_STATE{DRAG_STATE_EMPTY}; case Tool::DSTATUS_NONE: this->mouseState.lastPos = canvasPos; rc = this->currentTool->click(this->brush, this->brushPos, button, this->canvasWidget->canvas, canvasPos); break; case Tool::DSTATUS_MOVE: if (button == this->mouseState.dragState.button) { this->mouseState.dragState.set = canvasPos; this->mouseState.dragState.status = Tool::DSTATUS_SET; rc = this->currentTool->drag(this->brush, this->brushPos, this->canvasWidget->canvas, this->mouseState.dragState, canvasPos); this->mouseState.dragState = Tool::DRAG_STATE{DRAG_STATE_EMPTY}; this->mouseState.lastPos = canvasPos; } break; default: break; } mouseEvent->accept(); break; case CanvasWidget::ETYPE_MOUSE_WHEEL: if (modifiers == Qt::ControlModifier) { if (angleDelta.y() > 0) this->brush.size.h++, this->brush.size.w++, updatefl = true; else { if (this->brush.size.h > 0) this->brush.size.h--, updatefl = true; if (this->brush.size.w > 0) this->brush.size.w--, updatefl = true; } if (updatefl) this->applyLastTool(); wheelEvent->accept(); } else wheelEvent->ignore(); break; default: break; } if (rc) { rc = this->applyTool(rc); this->updateStatusBar(); } return rc; } /* * Private Qt class slot functions */ void CanvasEditorWindow::on_actionAboutRoar_triggered( bool checked ) { QDialog * about = new QDialog(0, Qt::WindowCloseButtonHint | Qt::WindowTitleHint); (void)checked; this->uiAbout.setupUi(about); about->setFixedSize(about->size()); about->show(); } void CanvasEditorWindow::on_actionExit_triggered( bool checked ) { (void)checked; if (!this->dirty || (this->dirty && this->promptSaveChanges())) this->close(); } void CanvasEditorWindow::on_actionExportAsANSI_triggered( bool checked ) { (void)checked; } void CanvasEditorWindow::on_actionExportAsPNG_triggered( bool checked ) { (void)checked; } void CanvasEditorWindow::on_actionExportToClipboard_triggered( bool checked ) { (void)checked; } void CanvasEditorWindow::on_actionExportToImgur_triggered( bool checked ) { (void)checked; } void CanvasEditorWindow::on_actionExportToPastebin_triggered( bool checked ) { (void)checked; } void CanvasEditorWindow::on_actionImportANSI_triggered( bool checked ) { (void)checked; } void CanvasEditorWindow::on_actionImportFromClipboard_triggered( bool checked ) { (void)checked; } void CanvasEditorWindow::on_actionImportSAUCE_triggered( bool checked ) { (void)checked; } void CanvasEditorWindow::on_actionNew_triggered( bool checked ) { Rtl::STATUS status = Rtl::STATUS_NONE_SUCCESS; (void)checked; if (!this->dirty || (this->dirty && this->promptSaveChanges())) this->importNew(status, true, true); } void CanvasEditorWindow::on_actionOpen_triggered( bool checked ) { QString fileName; std::wstring fileNameW; Rtl::STATUS status = Rtl::STATUS_NONE_SUCCESS; (void)checked; if (((fileName = QFileDialog::getOpenFileName(this, tr("Open..."), QString(""), tr("mIRC art files (*.txt);;All Files (*.*)"))) != "") && (!this->dirty || (this->dirty && this->promptSaveChanges()))) { QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::processEvents(); if (Rtl::convert_cstring(&status, fileName.toStdString(), fileNameW)) this->importMiRCfromFile(status, fileNameW, true, true); QApplication::restoreOverrideCursor(); QApplication::processEvents(); } if (!status) QMessageBox::critical(this, "Open...", QString("Failed to open %1: %2").arg(fileName, QString().fromUtf16((const ushort *)Rtl::perror(status).c_str()))); } void CanvasEditorWindow::on_actionSave_triggered( bool checked ) { (void)checked; this->saveAsMiRC("", true, false); } void CanvasEditorWindow::on_actionSaveAs_triggered( bool checked ) { Rtl::STATUS status; (void)checked; this->saveAsMiRC("", true, true); }