Tue, 09 Sep 2014 01:17:36 +0300
- re-committed bfc work (this makes 903ec1e46298 a suitable common ancestor for experimental branch features)
src/basics.h | file | annotate | diff | comparison | revisions | |
src/glCompiler.cc | file | annotate | diff | comparison | revisions | |
src/glShared.h | file | annotate | diff | comparison | revisions | |
src/ldDocument.cc | file | annotate | diff | comparison | revisions | |
src/ldDocument.h | file | annotate | diff | comparison | revisions | |
src/ldObject.cc | file | annotate | diff | comparison | revisions | |
src/ldObject.h | file | annotate | diff | comparison | revisions | |
src/mainWindow.cc | file | annotate | diff | comparison | revisions | |
src/partDownloader.cc | file | annotate | diff | comparison | revisions | |
src/partDownloader.h | file | annotate | diff | comparison | revisions | |
ui/config.ui | file | annotate | diff | comparison | revisions | |
ui/ldforge.ui | file | annotate | diff | comparison | revisions |
--- a/src/basics.h Tue Sep 09 01:16:24 2014 +0300 +++ b/src/basics.h Tue Sep 09 01:17:36 2014 +0300 @@ -56,6 +56,21 @@ Z }; +enum class Winding +{ + CW, + CCW, + None +}; + +inline void invertWinding (Winding& winding) +{ + if (winding == Winding::CW) + winding = Winding::CCW; + elif (winding == Winding::CCW) + winding = Winding::CW; +} + // // Derivative of QVector3D: this class is used for the vertices. //
--- a/src/glCompiler.cc Tue Sep 09 01:16:24 2014 +0300 +++ b/src/glCompiler.cc Tue Sep 09 01:17:36 2014 +0300 @@ -49,10 +49,6 @@ EXTERN_CFGENTRY (Bool, BlackEdges) EXTERN_CFGENTRY (String, BackgroundColor) -static QList<int> g_warnedColors; -static const QColor g_BFCFrontColor (64, 192, 80); -static const QColor g_BFCBackColor (208, 64, 64); - // static QMap<LDObjectPtr, String> g_objectOrigins; // ============================================================================= @@ -135,7 +131,12 @@ QColor GLCompiler::getColorForPolygon (LDPolygon& poly, LDObjectPtr topobj, EVBOComplement complement) const { + static const QColor bfcFrontColor (64, 192, 80); + static const QColor bfcBackColor (208, 64, 64); + static const QColor bfcDisabledColor (64, 64, 208); + static QList<int> warnedcolors; QColor qcol; + print ("Winding of %1 is %2", poly.id, (int) poly.winding); switch (complement) { @@ -144,11 +145,11 @@ return QColor(); case VBOCM_BFCFrontColors: - qcol = g_BFCFrontColor; + qcol = (poly.winding != Winding::None) ? bfcFrontColor : bfcDisabledColor; break; case VBOCM_BFCBackColors: - qcol = g_BFCBackColor; + qcol = (poly.winding != Winding::None) ? bfcBackColor : bfcDisabledColor; break; case VBOCM_PickColors: @@ -184,16 +185,13 @@ { // The color was unknown. Use main color to make the polygon at least // not appear pitch-black. - if (poly.num != 2 and poly.num != 5) - qcol = GLRenderer::getMainColor(); - else - qcol = Qt::black; + qcol = (poly.num != 2 and poly.num != 5) ? GLRenderer::getMainColor() : Qt::black; // Warn about the unknown color, but only once. - if (not g_warnedColors.contains (poly.color)) + if (not warnedcolors.contains (poly.color)) { print ("Unknown color %1!\n", poly.color); - g_warnedColors << poly.color; + warnedcolors << poly.color; } return qcol; @@ -285,16 +283,13 @@ if (it.key() == null) { it = m_objectInfo.erase (it); - continue; } - - if (it.key().toStrongRef()->document() == CurrentDocument() + elif (it.key().toStrongRef()->document() == CurrentDocument() and not it.key().toStrongRef()->isHidden()) { vbodata += it->data[vbonum]; + ++it; } - - ++it; } glBindBuffer (GL_ARRAY_BUFFER, m_vbo[vbonum]); @@ -392,7 +387,9 @@ QVector<GLfloat>& vbodata = objinfo->data[vbonum]; const QColor color = getColorForPolygon (poly, topobj, complement); - for (int vert = 0; vert < numverts; ++vert) + bool inverted = (poly.winding != Winding::CCW); + + auto func = [&](int vert) { if (complement == VBOCM_Surfaces) { @@ -408,6 +405,17 @@ << ((GLfloat) color.blue()) / 255.0f << ((GLfloat) color.alpha()) / 255.0f; } + }; + + if (not inverted) + { + for (int vert = 0; vert < numverts; ++vert) + func (vert); + } + else + { + for (int vert = numverts - 1; vert >= 0; --vert) + func (vert); } } }
--- a/src/glShared.h Tue Sep 09 01:16:24 2014 +0300 +++ b/src/glShared.h Tue Sep 09 01:17:36 2014 +0300 @@ -32,6 +32,7 @@ Vertex vertices[4]; int id; int color; + Winding winding; inline int numVertices() const {
--- a/src/ldDocument.cc Tue Sep 09 01:16:24 2014 +0300 +++ b/src/ldDocument.cc Tue Sep 09 01:17:36 2014 +0300 @@ -35,7 +35,6 @@ CFGENTRY (String, LDrawPath, "") CFGENTRY (List, RecentFiles, {}) -CFGENTRY (Bool, TryDownloadMissingFiles, false) EXTERN_CFGENTRY (String, DownloadFilePath) EXTERN_CFGENTRY (Bool, UseLogoStuds) @@ -209,6 +208,12 @@ } } +LDObjectList const& LDDocument::objects() +{ + sweepBFC(); + return m_objects; +} + // ============================================================================= // QList<LDDocumentPtr> const& LDDocument::explicitDocuments() @@ -757,22 +762,18 @@ unknowns << obj.staticCast<LDError>()->fileReferenced(); } - if (cfg::TryDownloadMissingFiles and not unknowns.isEmpty()) + if (not unknowns.isEmpty()) { PartDownloader dl; - - if (dl.checkValidPath()) - { - dl.setSource (PartDownloader::PartsTracker); - dl.setPrimaryFile (file); + dl.setSource (PartDownloader::PartsTracker); + dl.setPrimaryFile (file); - for (QString const& unknown : unknowns) - dl.downloadFromPartsTracker (unknown); + for (QString const& unknown : unknowns) + dl.downloadFromPartsTracker (unknown); - dl.exec(); - dl.checkIfFinished(); - file->reloadAllSubfiles(); - } + dl.exec(); + dl.checkIfFinished(); + file->reloadAllSubfiles(); } } @@ -1147,6 +1148,7 @@ } // ============================================================================= +// Adds an object at the end of the file. // int LDDocument::addObject (LDObjectPtr obj) { @@ -1155,6 +1157,7 @@ addKnownVertices (obj); obj->setDocument (self()); g_win->R()->compileObject (obj); + requireBFCSweep(); return getObjectCount() - 1; } @@ -1167,6 +1170,8 @@ if (obj != null) addObject (obj); } + + requireBFCSweep(); } // ============================================================================= @@ -1177,8 +1182,8 @@ m_objects.insert (pos, obj); obj->setDocument (self()); g_win->R()->compileObject (obj); + requireBFCSweep(); - #ifdef DEBUG if (not isImplicit()) dprint ("Inserted object #%1 (%2) at %3\n", obj->id(), obj->typeName(), pos); @@ -1214,6 +1219,11 @@ m_objectVertices.remove (obj); } + // We only need a sweep if we got rid of a BFC object, no processing is required if a polygon + // just got removed. + if (obj->type() == OBJ_BFC) + requireBFCSweep(); + m_objects.removeAt (idx); obj->setDocument (LDDocumentPtr()); } @@ -1257,11 +1267,12 @@ // ============================================================================= // -LDObjectPtr LDDocument::getObject (int pos) const +LDObjectPtr LDDocument::getObject (int pos) { if (m_objects.size() <= pos) return LDObjectPtr(); + sweepBFC(); return m_objects[pos]; } @@ -1269,7 +1280,7 @@ // int LDDocument::getObjectCount() const { - return objects().size(); + return m_objects.size(); } // ============================================================================= @@ -1507,8 +1518,9 @@ // ============================================================================= // -const LDObjectList& LDDocument::getSelection() const +const LDObjectList& LDDocument::getSelection() { + sweepBFC(); return m_sel; } @@ -1522,6 +1534,7 @@ m_objects[b] = one; m_objects[a] = other; addToHistory (new SwapHistory (one->id(), other->id())); + requireBFCSweep(); } // ============================================================================= @@ -1554,3 +1567,72 @@ { m_needVertexMerge = true; } + +// +// Sweeps through the file and adjusts the block windings of all objects. +// +void LDDocument::sweepBFC() +{ + if (not m_needBFCSweep) + return; + + QTime t0 (QTime::currentTime()); + Winding winding (Winding::None); + Winding preclip (winding); + LDBFCPtr bfc; + bool invertnext (false); + + for (LDObjectPtr obj : m_objects) + { + if (obj->type() == OBJ_BFC) + { + switch (obj.staticCast<LDBFC>()->statement()) + { + case BFCStatement::CCW: + case BFCStatement::CertifyCCW: + winding = Winding::CCW; + break; + + case BFCStatement::CW: + case BFCStatement::CertifyCW: + winding = Winding::CW; + + case BFCStatement::NoCertify: + winding = Winding::None; + break; + + case BFCStatement::NoClip: + preclip = winding; + winding = Winding::None; + break; + + case BFCStatement::InvertNext: + invertnext = true; + break; + + case BFCStatement::Clip: + winding = preclip; + break; + + default: + break; + } + } + else + { + Winding objwinding (winding); + + if (invertnext) + { + invertWinding (objwinding); + invertnext = false; + } + + print ("%1: BFC sweep: set winding of %2 to %3", getDisplayName(), obj->id(), int (objwinding)); + obj->setBlockWinding (objwinding); + } + } + + print ("%1: BFC sweep done in %2ms", getDisplayName(), t0.msecsTo (QTime::currentTime())); + m_needBFCSweep = false; +}
--- a/src/ldDocument.h Tue Sep 09 01:16:24 2014 +0300 +++ b/src/ldDocument.h Tue Sep 09 01:17:36 2014 +0300 @@ -64,7 +64,6 @@ { public: PROPERTY (public, QString, name, setName, STOCK_WRITE) - PROPERTY (private, LDObjectList, objects, setObjects, STOCK_WRITE) PROPERTY (private, LDObjectList, cache, setCache, STOCK_WRITE) PROPERTY (private, History*, history, setHistory, STOCK_WRITE) PROPERTY (public, QString, fullPath, setFullPath, STOCK_WRITE) @@ -90,13 +89,13 @@ void clearSelection(); void forgetObject (LDObjectPtr obj); // Deletes the given object from the object chain. QString getDisplayName(); - const LDObjectList& getSelection() const; + const LDObjectList& getSelection(); bool hasUnsavedChanges() const; // Does this document have unsaved changes? void initializeCachedData(); LDObjectList inlineContents (bool deep, bool renderinline); void insertObj (int pos, LDObjectPtr obj); int getObjectCount() const; - LDObjectPtr getObject (int pos) const; + LDObjectPtr getObject (int pos); bool save (QString path = "", int64* sizeptr = null); // Saves this file to disk. void swapObjects (LDObjectPtr one, LDObjectPtr other); bool isSafeToClose(); // Perform safety checks. Do this before closing any files! @@ -109,6 +108,8 @@ void redoVertices(); void needVertexMerge(); void reloadAllSubfiles(); + void sweepBFC(); + LDObjectList const& objects(); inline LDDocument& operator<< (LDObjectPtr obj) { @@ -146,6 +147,11 @@ setImplicit (true); } + inline void requireBFCSweep() + { + m_needBFCSweep = true; + } + static LDDocumentPtr current(); static void setCurrent (LDDocumentPtr f); static void closeInitialFile(); @@ -170,12 +176,16 @@ friend class GLRenderer; private: + LDObjectList m_objects; LDObjectList m_sel; LDGLData* m_gldata; // If set to true, next polygon inline of this document discards the // stored polygon data and re-builds it. bool m_needsReCache; + + // If set to true, next object reference request causes BFC sweep. + bool m_needBFCSweep; }; inline LDDocumentPtr CurrentDocument()
--- a/src/ldObject.cc Tue Sep 09 01:16:24 2014 +0300 +++ b/src/ldObject.cc Tue Sep 09 01:17:36 2014 +0300 @@ -419,6 +419,7 @@ data->id = id(); data->num = num; data->color = color().index(); + data->winding = blockWinding(); for (int i = 0; i < data->numVertices(); ++i) data->vertices[i] = vertex (i); @@ -430,14 +431,29 @@ // QList<LDPolygon> LDSubfile::inlinePolygons() { + bool isInverted (false); + LDBFCPtr bfc (previous().dynamicCast<LDBFC>()); + + if ((bfc != null and bfc->statement() == BFCStatement::InvertNext) + or transform().getDeterminant() < 0) + { + isInverted = true; + } + + print ("inlining polygons of subfile-ref %1: inverted: %2", fileInfo()->name(), isInverted ? "true" : "false"); + QList<LDPolygon> data = fileInfo()->inlinePolygons(); for (LDPolygon& entry : data) { for (int i = 0; i < entry.numVertices(); ++i) entry.vertices[i].transform (transform(), position()); + + if (isInverted) + invertWinding (entry.winding); } + print ("Using winding: %1\n", (data[0].winding == Winding::CCW) ? "CCW" : (data[0].winding == Winding::CW) ? "CW" : "None"); return data; }
--- a/src/ldObject.h Tue Sep 09 01:16:24 2014 +0300 +++ b/src/ldObject.h Tue Sep 09 01:17:36 2014 +0300 @@ -101,6 +101,11 @@ PROPERTY (private, QColor, randomColor, setRandomColor, STOCK_WRITE) PROPERTY (private, LDObjectWeakPtr, self, setSelf, STOCK_WRITE) + // What winding the object is supposed to be wound in, depending on the BFC statements + // This is CW if the part is CW-certified at this object, and vice versa. Can also be + // Winding::None for NOCERTIFY parts. + PROPERTY (public, Winding, blockWinding, setBlockWinding, STOCK_WRITE) + public: LDObject (LDObjectPtr* selfptr);
--- a/src/mainWindow.cc Tue Sep 09 01:16:24 2014 +0300 +++ b/src/mainWindow.cc Tue Sep 09 01:17:36 2014 +0300 @@ -215,16 +215,14 @@ void MainWindow::updateColorToolbar() { m_colorButtons.clear(); - ui->toolBarColors->clear(); - ui->toolBarColors->addAction (ui->actionUncolor); - ui->toolBarColors->addSeparator(); + ui->colorToolbar->clear(); + ui->colorToolbar->addAction (ui->actionUncolor); + ui->colorToolbar->addSeparator(); for (LDQuickColor& entry : m_quickColors) { if (entry.isSeparator()) - { - ui->toolBarColors->addSeparator(); - } + ui->colorToolbar->addSeparator(); else { QToolButton* colorButton = new QToolButton; @@ -233,7 +231,7 @@ colorButton->setToolTip (entry.color().name()); connect (colorButton, SIGNAL (clicked()), this, SLOT (slot_quickColor())); - ui->toolBarColors->addWidget (colorButton); + ui->colorToolbar->addWidget (colorButton); m_colorButtons << colorButton; entry.setToolButton (colorButton);
--- a/src/partDownloader.cc Tue Sep 09 01:16:24 2014 +0300 +++ b/src/partDownloader.cc Tue Sep 09 01:17:36 2014 +0300 @@ -22,14 +22,13 @@ #include <QDir> #include <QProgressBar> #include <QPushButton> -#include <QFileDialog> -#include <QMessageBox> #include "partDownloader.h" #include "ui_downloadfrom.h" #include "basics.h" #include "mainWindow.h" #include "ldDocument.h" #include "glRenderer.h" +#include "configDialog.h" CFGENTRY (String, DownloadFilePath, "") CFGENTRY (Bool, GuessDownloadPaths, true) @@ -41,12 +40,19 @@ // void PartDownloader::staticBegin() { - PartDownloader dlg; + QString path = getDownloadPath(); + + if (path.isEmpty() or not QDir (path).exists()) + { + Critical (PartDownloader::tr ("You need to specify a valid path for " + "downloaded files in the configuration to download paths.")); - if (not dlg.checkValidPath()) + (new ConfigDialog (ConfigDialog::DownloadTab, null))->exec(); return; + } - dlg.exec(); + PartDownloader* dlg = new PartDownloader; + dlg->exec(); } // ============================================================================= @@ -96,26 +102,6 @@ // ============================================================================= // -bool PartDownloader::checkValidPath() -{ - QString path = getDownloadPath(); - - if (path.isEmpty() or not QDir (path).exists()) - { - QMessageBox::information(this, "Notice", "Please input a path for files to download."); - path = QFileDialog::getExistingDirectory (this, "Path for downloaded files:"); - - if (path.isEmpty()) - return false; - - cfg::DownloadFilePath = path; - } - - return true; -} - -// ============================================================================= -// QString PartDownloader::getURL() { const Source src = getSource();
--- a/src/partDownloader.h Tue Sep 09 01:16:24 2014 +0300 +++ b/src/partDownloader.h Tue Sep 09 01:17:36 2014 +0300 @@ -70,7 +70,6 @@ virtual ~PartDownloader(); void addFile (LDDocumentPtr f); - bool checkValidPath(); void downloadFile (QString dest, QString url, bool primary); void downloadFromPartsTracker (QString file); QPushButton* getButton (Button i);
--- a/ui/config.ui Tue Sep 09 01:16:24 2014 +0300 +++ b/ui/config.ui Tue Sep 09 01:17:36 2014 +0300 @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>648</width> + <width>644</width> <height>370</height> </rect> </property> @@ -995,13 +995,6 @@ </layout> </item> <item> - <widget class="QCheckBox" name="configTryDownloadMissingFiles"> - <property name="text"> - <string>Attempt to download missing parts from the PT</string> - </property> - </widget> - </item> - <item> <widget class="QCheckBox" name="configGuessDownloadPaths"> <property name="whatsThis"> <string><p>When this is set, LDForge tries to adjust and correct part paths based on the input. A full path given to the download prompt should be of form <tt>"&lt;dir&gt;/&lt;file&gt;.dat"</tt> - with this set, input can be automatically completed.</p>
--- a/ui/ldforge.ui Tue Sep 09 01:16:24 2014 +0300 +++ b/ui/ldforge.ui Tue Sep 09 01:17:36 2014 +0300 @@ -47,8 +47,8 @@ <rect> <x>0</x> <y>0</y> - <width>237</width> - <height>414</height> + <width>233</width> + <height>399</height> </rect> </property> <attribute name="label"> @@ -75,8 +75,8 @@ <rect> <x>0</x> <y>0</y> - <width>237</width> - <height>414</height> + <width>256</width> + <height>121</height> </rect> </property> <attribute name="label"> @@ -157,8 +157,8 @@ <rect> <x>0</x> <y>0</y> - <width>237</width> - <height>414</height> + <width>101</width> + <height>101</height> </rect> </property> <attribute name="label"> @@ -194,7 +194,7 @@ <x>0</x> <y>0</y> <width>1010</width> - <height>26</height> + <height>27</height> </rect> </property> <widget class="QMenu" name="menuFile"> @@ -286,11 +286,7 @@ <addaction name="actionSelectByType"/> <addaction name="separator"/> <addaction name="actionModeSelect"/> - <addaction name="actionModeMagicWand"/> <addaction name="actionModeDraw"/> - <addaction name="actionModeRectangle"/> - <addaction name="actionModeCircle"/> - <addaction name="actionModeLinePath"/> <addaction name="separator"/> <addaction name="actionSetDrawDepth"/> <addaction name="separator"/> @@ -404,9 +400,9 @@ <addaction name="menuHelp"/> </widget> <widget class="QStatusBar" name="statusbar"/> - <widget class="QToolBar" name="toolBarFile"> + <widget class="QToolBar" name="toolBar"> <property name="windowTitle"> - <string>File</string> + <string>toolBar</string> </property> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> @@ -420,9 +416,9 @@ <addaction name="actionSave"/> <addaction name="actionSaveAs"/> </widget> - <widget class="QToolBar" name="toolBarNewObject"> + <widget class="QToolBar" name="toolBar_2"> <property name="windowTitle"> - <string>New Object</string> + <string>toolBar_2</string> </property> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> @@ -439,9 +435,9 @@ <addaction name="actionNewBFC"/> <addaction name="actionNewVertex"/> </widget> - <widget class="QToolBar" name="toolBarBasicTools"> + <widget class="QToolBar" name="toolBar_3"> <property name="windowTitle"> - <string>Basic tools</string> + <string>toolBar_3</string> </property> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> @@ -456,9 +452,9 @@ <addaction name="actionPaste"/> <addaction name="actionDelete"/> </widget> - <widget class="QToolBar" name="toolBarSelect"> + <widget class="QToolBar" name="toolBar_4"> <property name="windowTitle"> - <string>Select</string> + <string>toolBar_4</string> </property> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> @@ -470,9 +466,9 @@ <addaction name="actionSelectByColor"/> <addaction name="actionSelectByType"/> </widget> - <widget class="QToolBar" name="toolBarGrid"> + <widget class="QToolBar" name="toolBar_5"> <property name="windowTitle"> - <string>Grid</string> + <string>toolBar_5</string> </property> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> @@ -484,9 +480,9 @@ <addaction name="actionGridMedium"/> <addaction name="actionGridFine"/> </widget> - <widget class="QToolBar" name="toolBarDisplay"> + <widget class="QToolBar" name="toolBar_6"> <property name="windowTitle"> - <string>Display options</string> + <string>toolBar_6</string> </property> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> @@ -500,9 +496,9 @@ <addaction name="actionRandomColors"/> <addaction name="actionDrawAngles"/> </widget> - <widget class="QToolBar" name="toolBarEditTools"> + <widget class="QToolBar" name="toolBar_7"> <property name="windowTitle"> - <string>Editing tools</string> + <string>toolBar_7</string> </property> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> @@ -524,9 +520,9 @@ <addaction name="actionVisibilityToggle"/> <addaction name="actionVisibilityReveal"/> </widget> - <widget class="QToolBar" name="toolBarEditModes"> + <widget class="QToolBar" name="toolBar_8"> <property name="windowTitle"> - <string>Editing modes</string> + <string>toolBar_8</string> </property> <attribute name="toolBarArea"> <enum>LeftToolBarArea</enum> @@ -535,15 +531,15 @@ <bool>false</bool> </attribute> <addaction name="actionModeSelect"/> - <addaction name="actionModeMagicWand"/> <addaction name="actionModeDraw"/> <addaction name="actionModeRectangle"/> <addaction name="actionModeCircle"/> + <addaction name="actionModeMagicWand"/> <addaction name="actionModeLinePath"/> </widget> - <widget class="QToolBar" name="toolBarColors"> + <widget class="QToolBar" name="colorToolbar"> <property name="windowTitle"> - <string>Colors</string> + <string>toolBar_9</string> </property> <attribute name="toolBarArea"> <enum>RightToolBarArea</enum> @@ -552,9 +548,9 @@ <bool>false</bool> </attribute> </widget> - <widget class="QToolBar" name="toolBarExternalPrograms"> + <widget class="QToolBar" name="toolBar_9"> <property name="windowTitle"> - <string>External Programs</string> + <string>toolBar_9</string> </property> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> @@ -990,7 +986,7 @@ <string>Select Mode</string> </property> <property name="shortcut"> - <string>S</string> + <string>Ctrl+1</string> </property> </action> <action name="actionModeDraw"> @@ -1005,7 +1001,7 @@ <string>Draw Mode</string> </property> <property name="shortcut"> - <string>D</string> + <string>Ctrl+2</string> </property> </action> <action name="actionSetDrawDepth"> @@ -1025,7 +1021,7 @@ <string>Set the color on given objects.</string> </property> <property name="shortcut"> - <string>Shift+C</string> + <string>C</string> </property> </action> <action name="actionAutocolor"> @@ -1204,9 +1200,6 @@ <property name="statusTip"> <string>Flip coordinates.</string> </property> - <property name="shortcut"> - <string>Ctrl+Shift+F</string> - </property> </action> <action name="actionDemote"> <property name="text"> @@ -1507,6 +1500,9 @@ <property name="text"> <string>Save All</string> </property> + <property name="shortcut"> + <string>Ctrl+Shift+S</string> + </property> </action> <action name="actionClose"> <property name="text"> @@ -1566,7 +1562,7 @@ <string>Circle Mode</string> </property> <property name="shortcut"> - <string>C</string> + <string>Ctrl+4</string> </property> </action> <action name="actionVisibilityHide"> @@ -1686,7 +1682,7 @@ <string>Magic wand</string> </property> <property name="shortcut"> - <string>W</string> + <string>Ctrl+5</string> </property> </action> <action name="actionModeRectangle"> @@ -1701,7 +1697,7 @@ <string>Rectangle Mode</string> </property> <property name="shortcut"> - <string>R</string> + <string>Ctrl+3</string> </property> </action> <action name="actionModeLinePath"> @@ -1716,7 +1712,7 @@ <string>Line Path Mode</string> </property> <property name="shortcut"> - <string>P</string> + <string>Ctrl+6</string> </property> </action> </widget>