Tue, 07 May 2013 16:12:15 +0300
Add functionality for setting paths to ext progs in config dialog
/* * LDForge: LDraw parts authoring CAD * Copyright (C) 2013 Santeri Piippo * * 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 <http://www.gnu.org/licenses/>. */ #include <qgridlayout.h> #include <qmessagebox.h> #include <qevent.h> #include <qmenubar.h> #include <qstatusbar.h> #include <qsplitter.h> #include <qlistwidget.h> #include <qtoolbutton.h> #include <qcoreapplication.h> #include "common.h" #include "gldraw.h" #include "gui.h" #include "file.h" #include "config.h" #include "misc.h" #include "colors.h" #include "history.h" #include "config.h" vector<actionmeta> g_ActionMeta; static bool g_bSelectionLocked = false; cfg (bool, lv_colorize, true); cfg (int, gui_toolbar_iconsize, 24); cfg (str, gui_colortoolbar, "16:24:|:0:1:2:3:4:5:6:7"); extern_cfg (str, io_recentfiles); extern_cfg (bool, gl_axes); const QColor g_GroupBackgrounds[] = { QColor (0, 192, 255), // blue QColor (144, 255, 0), // green QColor (255, 128, 0), // orange QColor (255, 255, 0), // yellow QColor (160, 64, 255), // purple QColor (0, 255, 160), // emerald }; const ushort g_numGroups = 2; // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= ForgeWindow::ForgeWindow () { g_win = this; m_renderer = new GLRenderer; m_objList = new ObjectList; m_objList->setSelectionMode (QListWidget::ExtendedSelection); m_objList->setAlternatingRowColors (true); connect (m_objList, SIGNAL (itemSelectionChanged ()), this, SLOT (slot_selectionChanged ())); m_msglog = new QTextEdit; m_msglog->setReadOnly (true); m_msglog->setMaximumHeight (96); m_hsplit = new QSplitter; m_hsplit->addWidget (m_renderer); m_hsplit->addWidget (m_objList); m_vsplit = new QSplitter (Qt::Vertical); m_vsplit->addWidget (m_hsplit); m_vsplit->addWidget (m_msglog); setCentralWidget (m_vsplit); m_colorMeta = parseQuickColorMeta (); createMenuActions (); createMenus (); createToolbars (); slot_selectionChanged (); setStatusBar (new QStatusBar); setWindowIcon (getIcon ("ldforge")); setTitle (); setMinimumSize (320, 200); resize (800, 600); connect (QCoreApplication::instance (), SIGNAL (aboutToQuit ()), this, SLOT (slot_lastSecondCleanup ())); } // ============================================================================= void ForgeWindow::slot_lastSecondCleanup () { m_renderer->setParent (null); delete m_renderer; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::createMenuActions () { // Create the actions based on stored meta. for (actionmeta meta : g_ActionMeta) { QAction*& qAct = *meta.qAct; qAct = new QAction (getIcon (meta.sIconName), meta.sDisplayName, this); qAct->setStatusTip (meta.sDescription); qAct->setShortcut (*meta.conf); connect (qAct, SIGNAL (triggered ()), this, SLOT (slot_action ())); } // Grid actions and axes are checkable findAction ("gridCoarse")->setCheckable (true); findAction ("gridMedium")->setCheckable (true); findAction ("gridFine")->setCheckable (true); findAction ("axes")->setCheckable (true); findAction ("axes")->setChecked (gl_axes); // things not implemented yet findAction ("help")->setEnabled (false); History::updateActions (); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= QMenu* g_CurrentMenu; QMenu* ForgeWindow::initMenu (const char* name) { return g_CurrentMenu = menuBar ()->addMenu (tr (name)); } void ForgeWindow::addMenuAction (const char* name) { g_CurrentMenu->addAction (findAction (name)); } // ============================================================================= void ForgeWindow::createMenus () { m_recentFilesMenu = new QMenu (tr ("Open &Recent")); m_recentFilesMenu->setIcon (getIcon ("open-recent")); updateRecentFilesMenu (); QMenu*& menu = g_CurrentMenu; // File menu initMenu ("&File"); addMenuAction ("newFile"); // New addMenuAction ("open"); // Open menu->addMenu (m_recentFilesMenu); // Open Recent addMenuAction ("save"); // Save addMenuAction ("saveAs"); // Save As menu->addSeparator (); // ------- addMenuAction ("settings"); // Settings menu->addSeparator (); // ------- addMenuAction ("exit"); // Exit // View menu initMenu ("&View"); addMenuAction ("resetView"); // Reset View addMenuAction ("axes"); // Draw Axes menu->addSeparator (); // ----- addMenuAction ("screencap"); // Screencap Part addMenuAction ("showHistory"); // Edit History // Insert menu initMenu ("&Insert"); addMenuAction ("insertFrom"); // Insert from File addMenuAction ("insertRaw"); // Insert Raw menu->addSeparator (); // ------- addMenuAction ("newSubfile"); // New Subfile addMenuAction ("newLine"); // New Line addMenuAction ("newTriangle"); // New Triangle addMenuAction ("newQuad"); // New Quad addMenuAction ("newCondLine"); // New Conditional Line addMenuAction ("newComment"); // New Comment addMenuAction ("newBFC"); // New BFC Statment addMenuAction ("newVertex"); // New Vertex addMenuAction ("newRadial"); // New Radial menu->addSeparator (); // ----- addMenuAction ("beginDraw"); // Begin Drawing addMenuAction ("doneDraw"); // Cancel Drawing addMenuAction ("cancelDraw"); // Done Drawing // Edit menu initMenu ("&Edit"); addMenuAction ("undo"); // Undo addMenuAction ("redo"); // Redo menu->addSeparator (); // ----- addMenuAction ("cut"); // Cut addMenuAction ("copy"); // Copy addMenuAction ("paste"); // Paste addMenuAction ("del"); // Delete menu->addSeparator (); // ----- addMenuAction ("selectAll"); // Select All addMenuAction ("selectByColor"); // Select by Color addMenuAction ("selectByType"); // Select by Type initMenu ("&Groups"); for (uchar i = 0; i < LDObject::NumGroups; ++i) addMenuAction (fmt ("group%c", 'A' + i)); // Group * menu->addSeparator (); // ----- addMenuAction ("ungroup"); // Ungroup menu->addSeparator (); // ----- for (uchar i = 0; i < LDObject::NumGroups; ++i) addMenuAction (fmt ("selGroup%c", 'A' + i)); // Select Group * initMenu ("&Tools"); addMenuAction ("setColor"); // Set Color addMenuAction ("invert"); // Invert addMenuAction ("inlineContents"); // Inline addMenuAction ("deepInline"); // Deep Inline addMenuAction ("splitQuads"); // Split Quads addMenuAction ("setContents"); // Set Contents addMenuAction ("makeBorders"); // Make Borders addMenuAction ("makeCornerVerts"); // Make Corner Vertices addMenuAction ("roundCoords"); // Round Coordinates addMenuAction ("uncolorize"); // Uncolorize addMenuAction ("visibility"); // Toggle Visibility // Move menu initMenu ("&Move"); addMenuAction ("moveUp"); // Move Up addMenuAction ("moveDown"); // Move Down menu->addSeparator (); // ----- addMenuAction ("gridCoarse"); // Coarse Grid addMenuAction ("gridMedium"); // Medium Grid addMenuAction ("gridFine"); // Fine Grid menu->addSeparator (); // ----- addMenuAction ("moveXPos"); // Move +X addMenuAction ("moveXNeg"); // Move -X addMenuAction ("moveYPos"); // Move +Y addMenuAction ("moveYNeg"); // Move -Y addMenuAction ("moveZPos"); // Move +Z addMenuAction ("moveZNeg"); // Move -Z menu->addSeparator (); // ----- addMenuAction ("rotateXPos"); // Rotate +X addMenuAction ("rotateXNeg"); // Rotate -X addMenuAction ("rotateYPos"); // Rotate +Y addMenuAction ("rotateYNeg"); // Rotate -Y addMenuAction ("rotateZPos"); // Rotate +Z addMenuAction ("rotateZNeg"); // Rotate -Z initMenu ("E&xternal Programs"); addMenuAction ("ytruder"); #ifndef RELEASE // Debug menu initMenu ("&Debug"); addMenuAction ("addTestQuad"); // Add Test Quad addMenuAction ("addTestRadial"); // Add Test Radial #endif // RELEASE // Help menu initMenu ("&Help"); addMenuAction ("help"); // Help menu->addSeparator (); // ----- addMenuAction ("about"); // About addMenuAction ("aboutQt"); // About Qt } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::updateRecentFilesMenu () { // First, clear any items in the recent files menu for (QAction* recent : m_recentFiles) delete recent; m_recentFiles.clear (); std::vector<str> files = io_recentfiles.value / "@"; for (long i = files.size() - 1; i >= 0; --i) { str file = files[i]; QAction* recent = new QAction (getIcon ("open-recent"), file, this); connect (recent, SIGNAL (triggered ()), this, SLOT (slot_recentFile ())); m_recentFilesMenu->addAction (recent); m_recentFiles.push_back (recent); } } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= static QToolBar* g_CurrentToolBar; static Qt::ToolBarArea g_ToolBarArea = Qt::TopToolBarArea; void ForgeWindow::initSingleToolBar (const char* name) { QToolBar* toolbar = new QToolBar (name); addToolBar (g_ToolBarArea, toolbar); m_toolBars.push_back (toolbar); g_CurrentToolBar = toolbar; } // ============================================================================= void ForgeWindow::addToolBarAction (const char* name) { g_CurrentToolBar->addAction (findAction (name)); } // ============================================================================= void ForgeWindow::createToolbars () { initSingleToolBar ("File"); addToolBarAction ("newFile"); addToolBarAction ("open"); addToolBarAction ("save"); addToolBarAction ("saveAs"); // ========================================== initSingleToolBar ("Insert"); addToolBarAction ("newSubfile"); addToolBarAction ("newLine"); addToolBarAction ("newTriangle"); addToolBarAction ("newQuad"); addToolBarAction ("newCondLine"); addToolBarAction ("newComment"); addToolBarAction ("newBFC"); addToolBarAction ("newVertex"); addToolBarAction ("newRadial"); // ========================================== initSingleToolBar ("Edit"); addToolBarAction ("undo"); addToolBarAction ("redo"); addToolBarAction ("cut"); addToolBarAction ("copy"); addToolBarAction ("paste"); addToolBarAction ("del"); // ========================================== initSingleToolBar ("Select"); addToolBarAction ("selectAll"); addToolBarAction ("selectByColor"); addToolBarAction ("selectByType"); addToolBarBreak (Qt::TopToolBarArea); // ========================================== initSingleToolBar ("Move"); addToolBarAction ("moveUp"); addToolBarAction ("moveDown"); addToolBarAction ("moveXPos"); addToolBarAction ("moveXNeg"); addToolBarAction ("moveYPos"); addToolBarAction ("moveYNeg"); addToolBarAction ("moveZPos"); addToolBarAction ("moveZNeg"); // ========================================== initSingleToolBar ("Rotate"); addToolBarAction ("rotateXPos"); addToolBarAction ("rotateXNeg"); addToolBarAction ("rotateYPos"); addToolBarAction ("rotateYNeg"); addToolBarAction ("rotateZPos"); addToolBarAction ("rotateZNeg"); // ========================================== // Grid toolbar initSingleToolBar ("Grids"); addToolBarAction ("gridCoarse"); addToolBarAction ("gridMedium"); addToolBarAction ("gridFine"); addToolBarBreak (Qt::TopToolBarArea); // ========================================== initSingleToolBar ("View"); addToolBarAction ("axes"); // ========================================== // Color toolbar m_colorToolBar = new QToolBar ("Quick Colors"); addToolBar (Qt::RightToolBarArea, m_colorToolBar); // ========================================== // Left area toolbars //g_ToolBarArea = Qt::LeftToolBarArea; initSingleToolBar ("Tools"); addToolBarAction ("setColor"); addToolBarAction ("invert"); addToolBarAction ("inlineContents"); addToolBarAction ("deepInline"); addToolBarAction ("splitQuads"); addToolBarAction ("setContents"); addToolBarAction ("makeBorders"); addToolBarAction ("makeCornerVerts"); addToolBarAction ("roundCoords"); addToolBarAction ("screencap"); addToolBarAction ("uncolorize"); addToolBarAction ("visibility"); initSingleToolBar ("External Programs"); addToolBarAction ("ytruder"); initSingleToolBar ("Groups"); // We need to create the group toolbar buttons manually so that we can // set a custom background for them. for (uchar i = 0; i < LDObject::NumGroups; ++i) { QToolButton* btn = new QToolButton (this); QColor col = g_GroupBackgrounds[i]; btn->setDefaultAction (findAction (fmt ("group%c", 'A' + i))); btn->setStyleSheet (fmt ("QToolButton { background-color: \"#%.2X%.2X%.2X\"; }", col.red (), col.green (), col.blue ())); g_CurrentToolBar->addWidget (btn); } addToolBarAction ("ungroup"); updateToolBars (); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= std::vector<quickColorMetaEntry> parseQuickColorMeta () { std::vector<quickColorMetaEntry> meta; for (str colorname : gui_colortoolbar.value / ":") { if (colorname == "|") { meta.push_back ({null, null, true}); } else { color* col = getColor (atoi (colorname)); meta.push_back ({col, null, false}); } } return meta; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::updateToolBars () { for (QToolBar* qBar : m_toolBars) qBar->setIconSize (QSize (gui_toolbar_iconsize, gui_toolbar_iconsize)); // Update the quick color toolbar. for (QPushButton* qButton : m_colorButtons) delete qButton; m_colorButtons.clear (); // Clear the toolbar to remove separators m_colorToolBar->clear (); for (quickColorMetaEntry& entry : m_colorMeta) { if (entry.bSeparator) m_colorToolBar->addSeparator (); else { QPushButton* qColorButton = new QPushButton; qColorButton->setAutoFillBackground (true); qColorButton->setStyleSheet (fmt ("background-color: %s", entry.col->zColorString.chars())); qColorButton->setToolTip (entry.col->zName); connect (qColorButton, SIGNAL (clicked ()), this, SLOT (slot_quickColor ())); m_colorToolBar->addWidget (qColorButton); m_colorButtons.push_back (qColorButton); entry.btn = qColorButton; } } updateGridToolBar (); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::updateGridToolBar () { // Ensure that the current grid - and only the current grid - is selected. findAction ("gridCoarse")->setChecked (grid == Grid::Coarse); findAction ("gridMedium")->setChecked (grid == Grid::Medium); findAction ("gridFine")->setChecked (grid == Grid::Fine); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::setTitle () { str title = APPNAME " v"; title += versionString; // Append our current file if we have one if (g_curfile) { title += fmt (": %s", basename (g_curfile->m_filename.chars())); if (g_curfile->m_objs.size() > 0 && g_curfile->m_objs[0]->getType() == LDObject::Comment) { // Append title LDComment* comm = static_cast<LDComment*> (g_curfile->m_objs[0]); title += fmt (": %s", comm->text.chars()); } if (History::pos () != g_curfile->savePos) title += '*'; } setWindowTitle (title); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::slot_action () { // Get the action that triggered this slot. QAction* qAct = static_cast<QAction*> (sender ()); // Find the meta for the action. actionmeta* pMeta = null; for (actionmeta meta : g_ActionMeta) { if (*meta.qAct == qAct) { pMeta = &meta; break; } } if (!pMeta) { logf (LOG_Warning, "unknown signal sender %p!\n", qAct); return; } // We have the meta, now call the handler. (*pMeta->handler) (); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::deleteSelection (vector<ulong>* ulapIndices, std::vector<LDObject*>* papObjects) { if (m_sel.size () == 0) return; std::vector<LDObject*> selCopy = m_sel; // Delete the objects that were being selected for (LDObject* obj : selCopy) { if (papObjects && ulapIndices) { papObjects->push_back (obj->clone ()); ulapIndices->push_back (obj->getIndex (g_curfile)); } g_curfile->forgetObject (obj); delete obj; } refresh (); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::buildObjList () { if (!g_curfile) return; // Lock the selection while we do this so that refreshing the object list // doesn't trigger selection updating so that the selection doesn't get lost // while this is done. g_bSelectionLocked = true; m_objList->clear (); for (LDObject* obj : g_curfile->m_objs) { str descr; switch (obj->getType ()) { case LDObject::Comment: descr = static_cast<LDComment*> (obj)->text.chars(); // Remove leading whitespace while (~descr && descr[0] == ' ') descr -= -1; break; case LDObject::Empty: break; // leave it empty case LDObject::Line: { LDLine* line = static_cast<LDLine*> (obj); descr.format ("%s, %s", line->vaCoords[0].stringRep (true).chars(), line->vaCoords[1].stringRep (true).chars()); } break; case LDObject::Triangle: { LDTriangle* triangle = static_cast<LDTriangle*> (obj); descr.format ("%s, %s, %s", triangle->vaCoords[0].stringRep (true).chars(), triangle->vaCoords[1].stringRep (true).chars(), triangle->vaCoords[2].stringRep (true).chars()); } break; case LDObject::Quad: { LDQuad* quad = static_cast<LDQuad*> (obj); descr.format ("%s, %s, %s, %s", quad->vaCoords[0].stringRep (true).chars(), quad->vaCoords[1].stringRep (true).chars(), quad->vaCoords[2].stringRep (true).chars(), quad->vaCoords[3].stringRep (true).chars()); } break; case LDObject::CondLine: { LDCondLine* line = static_cast<LDCondLine*> (obj); descr.format ("%s, %s, %s, %s", line->vaCoords[0].stringRep (true).chars(), line->vaCoords[1].stringRep (true).chars(), line->vaCoords[2].stringRep (true).chars(), line->vaCoords[3].stringRep (true).chars()); } break; case LDObject::Gibberish: descr.format ("ERROR: %s", static_cast<LDGibberish*> (obj)->zContents.chars()); break; case LDObject::Vertex: descr.format ("%s", static_cast<LDVertex*> (obj)->vPosition.stringRep (true).chars()); break; case LDObject::Subfile: { LDSubfile* ref = static_cast<LDSubfile*> (obj); descr.format ("%s %s, (", ref->zFileName.chars(), ref->vPosition.stringRep (true).chars()); for (short i = 0; i < 9; ++i) descr.appendformat ("%s%s", ftoa (ref->mMatrix[i]).chars(), (i != 8) ? " " : ""); descr += ')'; } break; case LDObject::BFC: { LDBFC* bfc = static_cast<LDBFC*> (obj); descr = LDBFC::statements[bfc->type]; } break; case LDObject::Radial: { LDRadial* pRad = static_cast<LDRadial*> (obj); descr.format ("%d / %d %s", pRad->dSegments, pRad->dDivisions, pRad->radialTypeName()); if (pRad->eRadialType == LDRadial::Ring || pRad->eRadialType == LDRadial::Cone) descr.appendformat (" %d", pRad->dRingNum); descr.appendformat (" %s", pRad->vPosition.stringRep (true).chars ()); } break; default: descr = g_saObjTypeNames[obj->getType ()]; break; } // Put it into brackets if it's hidden if (obj->hidden ()) { str copy = descr.chars (); descr.format ("[[ %s ]]", copy.chars ()); } QListWidgetItem* item = new QListWidgetItem (descr.chars()); item->setIcon (getIcon (g_saObjTypeIcons[obj->getType ()])); // Color gibberish orange on red so it stands out. if (obj->getType() == LDObject::Gibberish) { item->setBackground (QColor ("#AA0000")); item->setForeground (QColor ("#FFAA00")); } else if (lv_colorize && obj->isColored () && obj->dColor != maincolor && obj->dColor != edgecolor) { // If the object isn't in the main or edge color, draw this // list entry in said color. color* col = getColor (obj->dColor); if (col) item->setForeground (col->qColor); } else if (obj->group () != LDObject::NoGroup) { item->setBackground (g_GroupBackgrounds[obj->group ()]); } obj->qObjListEntry = item; m_objList->insertItem (m_objList->count (), item); } g_bSelectionLocked = false; updateSelection (); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::scrollToSelection () { if (m_sel.size() == 0) return; LDObject* obj = m_sel[m_sel.size () - 1]; m_objList->scrollToItem (obj->qObjListEntry); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::slot_selectionChanged () { if (g_bSelectionLocked == true || g_curfile == null) return; /* // If the selection isn't 1 exact, disable setting contents findAction ("setContents")->setEnabled (qObjList->selectedItems().size() == 1); // If we have no selection, disable splitting quads findAction ("splitQuads")->setEnabled (qObjList->selectedItems().size() > 0); */ // Update the shared selection array, though don't do this if this was // called during GL picking, in which case the GL renderer takes care // of the selection. if (m_renderer->picking ()) return; std::vector<LDObject*> priorSelection = m_sel; // Get the objects from the object list selection m_sel.clear (); const QList<QListWidgetItem*> items = m_objList->selectedItems (); for (LDObject* obj : g_curfile->m_objs) for (QListWidgetItem* qItem : items) { if (qItem == obj->qObjListEntry) { m_sel.push_back (obj); break; } } // Update the GL renderer for (LDObject* obj : m_sel) m_renderer->recompileObject (obj); for (LDObject* obj : priorSelection) m_renderer->recompileObject (obj); m_renderer->updateSelFlash (); m_renderer->refresh (); } // ============================================================================= void ForgeWindow::slot_recentFile () { QAction* qAct = static_cast<QAction*> (sender ()); openMainFile (qAct->text ()); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::slot_quickColor () { QPushButton* button = static_cast<QPushButton*> (sender ()); color* col = null; for (quickColorMetaEntry entry : m_colorMeta) { if (entry.btn == button) { col = entry.col; break; } } if (col == null) return; std::vector<ulong> indices; std::vector<short> colors; short newColor = col->index (); for (LDObject* obj : m_sel) { if (obj->dColor == -1) continue; // uncolored object indices.push_back (obj->getIndex (g_curfile)); colors.push_back (obj->dColor); obj->dColor = newColor; } History::addEntry (new SetColorHistory (indices, colors, newColor)); refresh (); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= ulong ForgeWindow::getInsertionPoint () { if (m_sel.size () > 0) { // If we have a selection, put the item after it. return (m_sel[m_sel.size() - 1]->getIndex (g_curfile)) + 1; } // Otherwise place the object at the end. return g_curfile->m_objs.size(); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::refresh () { buildObjList (); m_renderer->hardRefresh (); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::updateSelection () { g_bSelectionLocked = true; m_objList->clearSelection (); for (LDObject* obj : m_sel) obj->qObjListEntry->setSelected (true); g_bSelectionLocked = false; slot_selectionChanged (); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= bool ForgeWindow::isSelected (LDObject* obj) { LDObject* needle = obj->topLevelParent (); if (needle == null) needle = obj; for (LDObject* hay : m_sel) if (hay == needle) return true; return false; } short ForgeWindow::getSelectedColor() { short result = -1; for (LDObject* obj : m_sel) { if (obj->dColor == -1) continue; // doesn't use color if (result != -1 && obj->dColor != result) return -1; // No consensus in object color if (result == -1) result = obj->dColor; } return result; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= LDObject::Type ForgeWindow::uniformSelectedType () { LDObject::Type eResult = LDObject::Unidentified; for (LDObject* obj : m_sel) { if (eResult != LDObject::Unidentified && obj->dColor != eResult) return LDObject::Unidentified; if (eResult == LDObject::Unidentified) eResult = obj->getType (); } return eResult; } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::closeEvent (QCloseEvent* ev) { // Check whether it's safe to close all files. for (OpenFile* f : g_loadedFiles) { if (!f->safeToClose ()) { ev->ignore (); return; } } // Save the configuration before leaving so that, for instance, grid choice // is preserved across instances. config::save (); ev->accept (); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::spawnContextMenu (const QPoint pos) { const bool single = (g_win->sel ().size () == 1); LDObject* singleObj = (single) ? g_win->sel ()[0] : null; QMenu* contextMenu = new QMenu; if (single && singleObj->getType () != LDObject::Empty) { contextMenu->addAction (findAction ("editObject")); contextMenu->addSeparator (); } contextMenu->addAction (findAction ("cut")); contextMenu->addAction (findAction ("copy")); contextMenu->addAction (findAction ("paste")); contextMenu->addAction (findAction ("del")); contextMenu->addSeparator (); contextMenu->addAction (findAction ("setColor")); if (single) contextMenu->addAction (findAction ("setContents")); contextMenu->addAction (findAction ("makeBorders")); contextMenu->exec (pos); } // ============================================================================= void ObjectList::contextMenuEvent (QContextMenuEvent* ev) { g_win->spawnContextMenu (ev->globalPos ()); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= QPixmap getIcon (const char* iconName) { return (QPixmap (fmt (":/icons/%s.png", iconName))); } // ============================================================================= bool confirm (str msg) { return confirm ("Confirm", msg); } bool confirm (str title, str msg) { return QMessageBox::question (g_win, title, msg, (QMessageBox::Yes | QMessageBox::No), QMessageBox::No) == QMessageBox::Yes; } // ============================================================================= void critical (str msg) { QMessageBox::critical (g_win, "Critical Error", msg, (QMessageBox::Close), QMessageBox::Close); } // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= // Print to message log // TODO: I don't think that the message log being a widget in the window // is a very good idea... maybe this should log into the renderer? Or into // another dialog that can be popped up? void ForgeWindow::logVA (LogType type, const char* fmtstr, va_list va) { (void) type; (void) fmtstr; (void) va; } // ============================================================================= QAction* findAction (str name) { for (actionmeta& meta : g_ActionMeta) if (name == meta.name) return *meta.qAct; fprintf (stderr, "%s: couldn't find action named `%s'!\n", __func__, name.chars ()); assert (false); return null; }