Warning, file /graphics/symboleditor/src/MainWindow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /********************************************************************************
0002  * Copyright (C) 2011-2015 by Stephen Allewell                                  *
0003  * steve.allewell@gmail.com                                                     *
0004  *                                                                              *
0005  * This program is free software; you can redistribute it and/or modify         *
0006  * it under the terms of the GNU General Public License as published by         *
0007  * the Free Software Foundation; either version 2 of the License, or            *
0008  * (at your option) any later version.                                          *
0009  ********************************************************************************/
0010 
0011 
0012 /**
0013  * @file
0014  * Implement the MainWindow class. This supplies the main user interface which comprises of a tabbed widget
0015  * containing a symbol editor and a library of symbols loaded from a symbol file.  A standard menu bar, tool
0016  * bar and status bar are provided to access the various functions and provide any suitable feedback or status
0017  * messages.
0018  */
0019 
0020 
0021 /**
0022  * @page main_window Main Window
0023  * The symbol editor main window comprises a tabbed widget containing the symbol editor and a list of the
0024  * current symbols in the loaded file. A standard menu bar, tool bar and status bar are provided to access
0025  * the various tools and functions and provide any necessary status messages or user feedback.
0026  *
0027  * @image html ui-main-editor.png "The user interface showing the editor tab"
0028  *
0029  * @section file_menu File Menu
0030  *
0031  * @subsection file_new New
0032  * Start a new symbol definition. The editor is cleared ready to define the new symbol. If there is an existing
0033  * symbol being edited that has not been saved, the user is prompted to save it or allow it to be overwritten.
0034  * Alternatively the user can cancel the creation of the new symbol leaving the current one intact.
0035  *
0036  * @subsection file_open Open
0037  * Open an existing symbol library. The current symbol and symbol library are cleared and the user is prompted to
0038  * select a file to be opened. If the current symbol of symbol file have not been saved the user is asked to
0039  * save them or allow them to be overwritten. Alternatively the user can cancel opening a file leaving the current
0040  * ones intact.
0041  *
0042  * @subsection file_open_recent Open Recent
0043  * Previously opened files are added to the recent files menu and can be opened by selecting the file specified.
0044  * The same rules relating to Open and the current symbol and library apply here.
0045  *
0046  * @subsection file_save Save
0047  * Save the current library to a file. If this is a new library the user will be prompted to enter a file name.
0048  *
0049  * @subsection file_save_as Save As
0050  * Save the current library using a different name. The user will be prompted to enter a file name.
0051  *
0052  * @subsection file_save_symbol Save Symbol
0053  * Save the current symbol being edited to the current library. This does not save the symbol library to disk, this
0054  * is done by the Save command.
0055  *
0056  * @subsection file_save_symbol_as_new Save Symbol as New
0057  * Save the symbol as if it was a new one. This would apply if the symbol was one from the library being edited. The
0058  * editor maintains a link between it and the library and would normally update it when saved. This will reset the
0059  * index and add the symbol to the library as a new one. The symbol being edited is now detached from the library
0060  * and subsequent saves will continue to add new symbols to the library. This can be used to create new symbols based
0061  * on an existing library symbol.
0062  *
0063  * @subsection file_import_library Import Library
0064  * Import an existing symbol library and append the symbols in it to the current library.
0065  *
0066  * @subsection file_close Close
0067  * Close the current library. The editor and the library are cleared leaving an empty library ready for new symbols
0068  * to be added. If the current symbol and library need to be saved the user is prompted to do so.
0069  *
0070  * @subsection file_quit Quit
0071  * Quit the application. If the current symbol and library need to be saved the user is prompted to do so.
0072  *
0073  * @section edit_menu Edit Menu
0074  *
0075  * @subsection edit_undo Undo
0076  * Actions that modify the symbol currently being edited or the current symbol library can be undone reverting to
0077  * the previous state. The editor and the library have independent undo stacks and changes to one do not affect the
0078  * other. The undo command affects the currently selected tab. The undo command in the menu shows a description of
0079  * the action that will be undone. For the undo command on the toolbar, the tooltip will show the desciption.
0080  *
0081  * @subsection edit_redo Redo
0082  * Actions that are undone can be redone. As with undo, the editor and the symbol library are independent of each
0083  * other and the redo command affects the currently selected tab. The redo command in the menu and the toolbar tooltip
0084  * shows a description of the action that will be redone.
0085  *
0086  * @subsection file_edit_toolbar File and Edit Toolbar
0087  * The file and edit menu toolbar allow quick access to these common functions.
0088  * @image html ui-main-toolbar.png
0089  * - @ref file_new
0090  * - @ref file_open
0091  * - @ref file_save
0092  * - @ref edit_undo
0093  * - @ref edit_redo
0094  * - @ref file_save_symbol
0095  *
0096  * @section rendering_menu Rendering Menu
0097  * The defined path can be rendered with various settings. Filled or unfilled, for which the fill method can be defined.
0098  * The path end cap can be defined as flat, square and round. The line join type can be defined as bevel, miter and round.
0099  * The line width can be increased or decreased.
0100  *
0101  * For full details of the rendering options, see the @ref path_rendering.
0102  *
0103  * @subsection rendering_toolbar Rendering Toolbar
0104  * The rendering toolbar allows quick access to these common functions.
0105  * @image html ui-rendering-toolbar.png
0106  * - @ref fill_mode
0107  * - @ref fill_rule
0108  * - @ref line_cap
0109  * - @ref line_join
0110  * - @ref line_width
0111  *
0112  * @section tools_menu Tools Menu
0113  * A number of tools are available to aid the design of the symbols. The symbols are composed of a series of sub paths
0114  * and each sub path is composed of a move to the start position (this defaults to 0,0 for new symbols) followed by
0115  * lines and curves. The curves are cubic splines having a start, an end and two control points defining the curve.
0116  * There are convenience tools to create rectangles and ellipses, but these will be broken down into lines and curves.
0117  *
0118  * All points created for the elements are moveable by dragging them to their new position. All points can be snapped
0119  * to the grid intersections if the snap option is turned on, otherwise they can be positioned anywhere.
0120  *
0121  * The symbol can be rotated clockwise and counter clockwise and also flipped vertically and horizontally. This allows
0122  * multiple symbols to be easily created based on the same design. Remember to use the save symbol as new option for this.
0123  *
0124  * For full details of the tools see the @ref editor_tools.
0125  *
0126  * @subsection tools_toolbar Tools Toolbar
0127  * The tools toolbar allows quick access to these common functions.
0128  * @image html ui-tools-toolbar.png
0129  * - @ref move_to
0130  * - @ref line_to
0131  * - @ref cubic_to
0132  * - @ref rectangle
0133  * - @ref ellipse
0134  * - @ref character
0135  * - @ref rotate_left
0136  * - @ref rotate_right
0137  * - @ref flip_horizontal
0138  * - @ref flip_vertical
0139  * - @ref scale_preferred
0140  * - @ref snap_grid
0141  * - @ref guide_lines
0142  */
0143 
0144 
0145 #include "MainWindow.h"
0146 
0147 #include <QAction>
0148 #include <QFileDialog>
0149 #include <QIcon>
0150 #include <QVBoxLayout>
0151 #include <QListWidgetItem>
0152 #include <QMenu>
0153 #include <QStatusBar>
0154 #include <QTabWidget>
0155 #include <QTemporaryFile>
0156 
0157 #include <kwidgetsaddons_version.h>
0158 #include <KActionCollection>
0159 #include <KConfigDialog>
0160 #include <KConfigGroup>
0161 #include <KIO/FileCopyJob>
0162 #include <KLocalizedString>
0163 #include <KMessageBox>
0164 #include <KRecentFilesAction>
0165 
0166 #include "ConfigurationDialogs.h"
0167 #include "Editor.h"
0168 #include "Exceptions.h"
0169 #include "SymbolListWidget.h"
0170 #include "SymbolLibrary.h"
0171 
0172 #include "ui_EditorConfigPage.h"
0173 
0174 #include "SymbolEditor.h"
0175 
0176 
0177 /**
0178  * Construct the MainWindow.
0179  * Create an instance of a symbol file.
0180  * Create the tab widget, editor, list widget and the symbol file. The tab widget is then set as
0181  * the central widget and will contain the editor and list widgets. The Editor is added to a
0182  * layout to allow it to be centralized in the main window.
0183  * Set up the actions, add the two undo stacks to the undo group and connect any signal slots required.
0184  * Set up the GUI from the applications rc file.
0185  * The editor page is selected in the tab widget which should also initialise the undo redo buttons.
0186  * The moveTo tool action is triggered to enable the moveTo tool as the initial one.
0187  * Other actions are initialised from the current Editor symbol.
0188  */
0189 MainWindow::MainWindow()
0190     :   m_tabWidget(new QTabWidget(this)),
0191         m_editor(new Editor),
0192         m_listWidget(new SymbolListWidget(m_tabWidget)),
0193         m_symbolLibrary(new SymbolLibrary(m_listWidget)),
0194         m_item(nullptr),
0195         m_menu(nullptr)
0196 {
0197     m_listWidget->loadFromLibrary(m_symbolLibrary);
0198     m_url = QUrl(i18n("Untitled"));
0199 
0200     setObjectName(QStringLiteral("MainWindow#"));
0201 
0202     KActionCollection *actions = actionCollection();
0203 
0204     m_listWidget->setIconSize(48);
0205     m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
0206 
0207     m_tabWidget->addTab(m_editor, i18nc("The editor tab title", "Editor"));
0208     m_tabWidget->addTab(m_listWidget, i18nc("The library tab title", "Library"));
0209 
0210     setCentralWidget(m_tabWidget);
0211 
0212     setupActions();
0213 
0214     m_undoGroup.addStack(m_editor->undoStack());
0215     m_undoGroup.addStack(m_symbolLibrary->undoStack());
0216     connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int)));
0217     connect(m_editor, SIGNAL(message(QString)), statusBar(), SLOT(showMessage(QString)));
0218     connect(m_editor, SIGNAL(minLineWidth(bool)), actions->action(QStringLiteral("decreaseLineWidth")), SLOT(setDisabled(bool)));
0219     connect(m_editor, SIGNAL(maxLineWidth(bool)), actions->action(QStringLiteral("increaseLineWidth")), SLOT(setDisabled(bool)));
0220     connect(&m_undoGroup, SIGNAL(canUndoChanged(bool)), actions->action(QStringLiteral("edit_undo")), SLOT(setEnabled(bool)));
0221     connect(&m_undoGroup, SIGNAL(canRedoChanged(bool)), actions->action(QStringLiteral("edit_redo")), SLOT(setEnabled(bool)));
0222     connect(&m_undoGroup, SIGNAL(undoTextChanged(QString)), this, SLOT(undoTextChanged(QString)));
0223     connect(&m_undoGroup, SIGNAL(redoTextChanged(QString)), this, SLOT(redoTextChanged(QString)));
0224     connect(&m_undoGroup, SIGNAL(cleanChanged(bool)), this, SLOT(cleanChanged(bool)));
0225     connect(m_editor->undoStack(), SIGNAL(cleanChanged(bool)), actions->action(QStringLiteral("saveSymbol")), SLOT(setDisabled(bool)));
0226     connect(m_editor->undoStack(), SIGNAL(cleanChanged(bool)), actions->action(QStringLiteral("saveSymbolAsNew")), SLOT(setDisabled(bool)));
0227     connect(m_symbolLibrary->undoStack(), SIGNAL(cleanChanged(bool)), actions->action(QStringLiteral("file_save")), SLOT(setDisabled(bool)));
0228     connect(m_listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(itemSelected(QListWidgetItem*)));
0229     connect(m_listWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(listWidgetContextMenuRequested(QPoint)));
0230 
0231     setupGUI(KXmlGuiWindow::Default, QStringLiteral("SymbolEditorui.rc"));
0232 
0233     actions->action(QStringLiteral("moveTo"))->trigger();                   // select draw tool
0234     actions->action(QStringLiteral("enableSnap"))->setChecked(true);        // enable snap
0235     actions->action(QStringLiteral("enableGuides"))->setChecked(true);      // enable creation of guides
0236     actions->action(QStringLiteral("file_save"))->setEnabled(false);        // nothing to save yet
0237     actions->action(QStringLiteral("saveSymbol"))->setEnabled(false);       // nothing to save yet
0238     actions->action(QStringLiteral("saveSymbolAsNew"))->setEnabled(false);  // nothing to save yet
0239     setActionsFromSymbol(m_editor->symbol().second);        // set the actions that depend on the current empty symbol, i.e. the defaults
0240 
0241     currentChanged(m_tabWidget->currentIndex());              // this should be the editor
0242 }
0243 
0244 
0245 /**
0246  * Descructor for the MainWindow
0247  * Delete the SymbolLibrary object. The other widgets that are created in the constructor are children of the
0248  * MainWindow and will be destroyed when this is.
0249  */
0250 MainWindow::~MainWindow()
0251 {
0252     delete m_symbolLibrary;
0253 }
0254 
0255 
0256 /**
0257  * Test if it is ok to close this window.
0258  * Check if the current symbol being edited has been changed and if the library has been changed.
0259  *
0260  * @return true if it is ok to close, false otherwise
0261  */
0262 bool MainWindow::queryClose()
0263 {
0264     return (editorClean() && libraryClean());
0265 }
0266 
0267 
0268 /**
0269  * Check if it ok to close the currently edited symbol.
0270  *
0271  * @return true if is ok to close the symbol, false otherwise
0272  */
0273 bool MainWindow::editorClean()
0274 {
0275     bool clean = m_editor->undoStack()->isClean();
0276 
0277     if (!clean) {
0278 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0279         int messageBoxResult = KMessageBox::warningTwoActionsCancel(this,
0280 #else
0281         int messageBoxResult = KMessageBox::warningYesNoCancel(this,
0282 #endif
0283                                                                i18n("Save changes to the symbol?"), QString(),
0284                                                                KStandardGuiItem::save(),
0285                                                                KStandardGuiItem::discard(),
0286                                                                KStandardGuiItem::cancel());
0287 
0288         switch (messageBoxResult) {
0289 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0290         case KMessageBox::PrimaryAction:
0291 #else
0292         case KMessageBox::Yes:
0293 #endif
0294             saveSymbol();
0295             save();
0296             clean = true;
0297             break;
0298 
0299 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0300         case KMessageBox::SecondaryAction:
0301 #else
0302         case KMessageBox::No:
0303 #endif
0304             clean = true;
0305             break;
0306 
0307         case KMessageBox::Cancel:
0308             clean = false;
0309             break;
0310         }
0311     }
0312 
0313     return clean;
0314 }
0315 
0316 
0317 /**
0318  * Check if it is ok to close the library.
0319  *
0320  * @return true if it is ok to close the library, false otherwise
0321  */
0322 bool MainWindow::libraryClean()
0323 {
0324     bool clean = m_symbolLibrary->undoStack()->isClean();
0325 
0326     if (!clean) {
0327 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0328         int messageBoxResult = KMessageBox::warningTwoActionsCancel(this,
0329 #else
0330         int messageBoxResult = KMessageBox::warningYesNoCancel(this,
0331 #endif
0332                                                                i18n("Save changes to the library?"), QString(),
0333                                                                KStandardGuiItem::save(),
0334                                                                KStandardGuiItem::discard(),
0335                                                                KStandardGuiItem::cancel());
0336 
0337         switch (messageBoxResult) {
0338 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0339         case KMessageBox::PrimaryAction:
0340 #else
0341         case KMessageBox::Yes:
0342 #endif
0343             save();
0344             clean = true;
0345             break;
0346 
0347 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0348         case KMessageBox::SecondaryAction:
0349 #else
0350         case KMessageBox::No:
0351 #endif
0352             clean = true;
0353             break;
0354 
0355         case KMessageBox::Cancel:
0356             clean = false;
0357             break;
0358         }
0359     }
0360 
0361     return clean;
0362 }
0363 
0364 
0365 /**
0366  * Open a file.
0367  * Use the QFileDialog::getOpenFileUrl to get a QUrl to open which is then passed to filOpen(const QUrl &).
0368  */
0369 void MainWindow::fileOpen()
0370 {
0371     QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Open file"), QUrl::fromLocalFile(QDir::homePath()), i18n("Cross Stitch Symbols (*.sym)"));
0372 
0373     if (!url.isEmpty()) {
0374         fileOpen(url);
0375     }
0376 }
0377 
0378 
0379 /**
0380  * If a valid url is supplied, try and download the file (in case it comes from a remote source) and
0381  * then try and open it read only. Once opened create a QDataStream and try and read the contents.
0382  * This is protected in a try-catch block to intercept any exceptions that may be thrown by the loading
0383  * routines. If there were any errors, the symbol library will be cleared and a suitable error message
0384  * will be displayed.
0385  * The url of the file is set in the symbol file object only if there were no errors. This will avoid
0386  * writing to a corrupt file or to a file that isn't a symbol file. The url is added to the recent file
0387  * list.
0388  */
0389 void MainWindow::fileOpen(const QUrl &url)
0390 {
0391     if (!editorClean() || !libraryClean()) {
0392         return;
0393     }
0394 
0395     m_symbolLibrary->clear();
0396     m_editor->clear();
0397 
0398     if (url.isValid()) {
0399         QTemporaryFile tmpFile;
0400 
0401         if (tmpFile.open()) {
0402             tmpFile.close();
0403 
0404             KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tmpFile.fileName()), -1, KIO::Overwrite);
0405 
0406             if (job->exec()) {
0407                 QFile reader(tmpFile.fileName());
0408 
0409                 if (reader.open(QIODevice::ReadOnly)) {
0410                     QDataStream stream(&reader);
0411 
0412                     try {
0413                         stream >> *m_symbolLibrary;
0414                         m_url = url;
0415                         KRecentFilesAction *action = static_cast<KRecentFilesAction *>(actionCollection()->action(QStringLiteral("file_open_recent")));
0416                         action->addUrl(url);
0417                         action->saveEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles")));
0418                         m_tabWidget->setCurrentIndex(1);
0419                     } catch (const InvalidFile &e) {
0420                         KMessageBox::error(nullptr, i18n("This doesn't appear to be a valid symbol file"));
0421                     } catch (const InvalidFileVersion &e) {
0422                         KMessageBox::error(nullptr, i18n("Version %1 of the library file is not supported in this version of SymbolEditor", e.version));
0423                     } catch (const InvalidSymbolVersion &e) {
0424                         KMessageBox::error(nullptr, i18n("Version %1 of the symbol is not supported in this version of SymbolEditor", e.version));
0425                     } catch (const FailedReadLibrary &e) {
0426                         KMessageBox::error(nullptr, i18n("Failed to read the library\n%1", e.statusMessage()));
0427                         m_symbolLibrary->clear();
0428                     }
0429                 } else {
0430                     KMessageBox::error(nullptr, reader.errorString());
0431                 }
0432             } else {
0433                 KMessageBox::error(nullptr, job->errorString());
0434             }
0435         } else {
0436             KMessageBox::error(nullptr, tmpFile.errorString());
0437         }
0438     } else {
0439         KMessageBox::error(nullptr, i18n("The url %1 is invalid", url.fileName()));
0440     }
0441 }
0442 
0443 
0444 /**
0445  * Save the library using its url, if this is Untitled than call saveAs to get a valid url.
0446  * Open the file and write to the stream. This is protected in a try-catch block to intercept
0447  * any exceptions thrown by the writing routines. If there were any exceptions thrown or the
0448  * file could not be opened a suitable error message is shown.
0449  */
0450 void MainWindow::save()
0451 {
0452     if (m_url == QUrl(i18n("Untitled"))) {
0453         saveAs();
0454     } else {
0455         QFile file(m_url.path());
0456 
0457         if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
0458             QDataStream stream(&file);
0459             stream.setVersion(QDataStream::Qt_4_0);
0460 
0461             try {
0462                 stream << *m_symbolLibrary;
0463             } catch (const FailedWriteLibrary &e) {
0464                 KMessageBox::error(nullptr, i18n("Failed to write the library\n%1", e.statusMessage()));
0465             }
0466 
0467             file.close();
0468             m_symbolLibrary->undoStack()->setClean();
0469         } else {
0470             KMessageBox::error(nullptr, i18n("Failed to open the file %1\n%2", m_url.fileName(), file.errorString()));
0471         }
0472     }
0473 }
0474 
0475 
0476 /**
0477  * Save the library using a different url.
0478  * This is also called from save when the assigned url is Untitled.
0479  * The new url is added to the recent files list.
0480  */
0481 void MainWindow::saveAs()
0482 {
0483     QUrl url = QFileDialog::getSaveFileUrl(this, i18n("Save As..."), QUrl::fromLocalFile(QDir::homePath()), i18n("Cross Stitch Symbols (*.sym)"));
0484 
0485     if (url.isValid()) {
0486         m_url = url;
0487         save();
0488         KRecentFilesAction *action = static_cast<KRecentFilesAction *>(actionCollection()->action(QStringLiteral("file_open_recent")));
0489         action->addUrl(url);
0490         action->saveEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles")));
0491     }
0492 }
0493 
0494 
0495 /**
0496  * Initialise a new symbol.
0497  * Check if the current symbol has been saved or can be overwritten and then call clear
0498  * on the editor which initializes the editor with an empty symbol.
0499  * The actions are reset to those relevant to the new empty symbol.
0500  * The moveTo action is triggered as this is the most likely to be used next.
0501  */
0502 void MainWindow::newSymbol()
0503 {
0504     if (editorClean()) {
0505         m_editor->clear();
0506         setActionsFromSymbol(m_editor->symbol().second);
0507         actionCollection()->action(QStringLiteral("moveTo"))->trigger();   // Select move tool
0508     }
0509 }
0510 
0511 
0512 /**
0513  * Save the current symbol.
0514  * Store the symbol currently in the editor into the symbol library object. If it is a
0515  * new symbol the index will be 0, a new index will then be created. Otherwise the index
0516  * will be the one from the library and storing it will overwrite the existing symbol.
0517  */
0518 void MainWindow::saveSymbol()
0519 {
0520     QPair<qint16, Symbol> pair = m_editor->symbol();
0521     m_symbolLibrary->undoStack()->push(new UpdateSymbolCommand(m_symbolLibrary, pair.first, pair.second));
0522     m_editor->undoStack()->setClean();
0523 }
0524 
0525 
0526 /**
0527  * Save the current symbol using a new index.
0528  * Store the symbol currently in the editor into the symbol library object.  Ignore the
0529  * editors index, resetting it to 0 and a new index will then be created.
0530  * Reassign this symbol back to the editor with a 0 index effectively creating a new symbol in
0531  * the editor.
0532  */
0533 void MainWindow::saveSymbolAsNew()
0534 {
0535     QPair<qint16, Symbol> pair = m_editor->symbol();
0536     pair.first = 0;
0537     m_symbolLibrary->undoStack()->push(new UpdateSymbolCommand(m_symbolLibrary, pair.first, pair.second));
0538     m_editor->setSymbol(pair);
0539 }
0540 
0541 
0542 /**
0543  * Import a library of symbols into the current library.
0544  * Get a url for the library file and try and download it in case it comes from a remote source.
0545  * Open the file and read the contents into a new SymbolLibrary object. The read is protected by
0546  * a try-catch block to intercept any exceptions thrown by the read routines. If there were no
0547  * errors an ImportLibraryCommand is created and pushed onto the symbol library undo stack.
0548  * This will copy all the symbols from the imported library into the current library.
0549  * Any errors will display a suitable error message.
0550  */
0551 void MainWindow::importLibrary()
0552 {
0553     QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Import library"), QUrl::fromLocalFile(QDir::homePath()), i18n("Cross Stitch Symbols (*.sym)"));
0554 
0555     if (url.isEmpty()) {
0556         return;
0557     }
0558 
0559     if (url.isValid()) {
0560         QTemporaryFile tmpFile;
0561 
0562         if (tmpFile.open()) {
0563             KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tmpFile.fileName()), -1, KIO::Overwrite);
0564 
0565             if (job->exec()) {
0566                 SymbolLibrary *lib = new SymbolLibrary;
0567                 QDataStream stream(&tmpFile);
0568 
0569                 try {
0570                     stream >> *lib;
0571                     m_symbolLibrary->undoStack()->push(new ImportLibraryCommand(m_symbolLibrary, lib));
0572                 } catch (const InvalidFile &e) {
0573                     KMessageBox::error(nullptr, i18n("This doesn't appear to be a valid symbol file"));
0574                 } catch (const InvalidFileVersion &e) {
0575                     KMessageBox::error(nullptr, i18n("Version %1 of the library file is not supported in this version of SymbolEditor", e.version));
0576                 }
0577             } else {
0578                 KMessageBox::error(nullptr, job->errorString());
0579             }
0580         } else {
0581             KMessageBox::error(nullptr, tmpFile.errorString());
0582         }
0583     } else {
0584         KMessageBox::error(nullptr, i18n("The url %1 is invalid", url.fileName()));
0585     }
0586 }
0587 
0588 
0589 /**
0590  * Close the current library.
0591  * Check if the current symbol and the symbol library need to be saved and then clear
0592  * the library and the editor.
0593  */
0594 void MainWindow::close()
0595 {
0596     if (editorClean() && libraryClean()) {
0597         m_editor->clear();
0598         m_symbolLibrary->clear();
0599         m_url = QUrl(i18n("Untitled"));
0600     }
0601 }
0602 
0603 
0604 /**
0605  * Quit the application.
0606  * Closes this MainWindow.
0607  */
0608 void MainWindow::quit()
0609 {
0610     KXmlGuiWindow::close();
0611 }
0612 
0613 
0614 /**
0615  * Undo the last operation.
0616  * Several undo stacks will be available. One for the library object and one for the editor.
0617  * Changing the tab will update the actions depending on the contents of the relevant undo stack.
0618  */
0619 void MainWindow::undo()
0620 {
0621     m_undoGroup.undo();
0622 }
0623 
0624 
0625 /**
0626  * Redo the last operation undone.
0627  * Several undo stacks will be available. One for the library object and one for the editor.
0628  * Changing the tab will update the actions depending on the contents of the relevant undo stack.
0629  */
0630 void MainWindow::redo()
0631 {
0632     m_undoGroup.redo();
0633 }
0634 
0635 
0636 /**
0637  * Update the undo action text to reflect the last operation available to undo.
0638  * Several undo stacks will be available. One for the library object and one for the editor.
0639  * Changing the tab will update the actions depending on the contents of the relevant undo stack.
0640  *
0641  * @param text the text string to describe the operation
0642  */
0643 void MainWindow::undoTextChanged(const QString &text)
0644 {
0645     actionCollection()->action(QStringLiteral("edit_undo"))->setText(QString(i18n("Undo %1", text)));
0646 }
0647 
0648 
0649 /**
0650  * Update the redo action text to reflect the last operation available to redo.
0651  * Several undo stacks will be available. One for the library object and one for the editor.
0652  * Changing the tab will update the actions depending on the contents of the relevant undo stack.
0653  *
0654  * @param text the text string to describe the operation
0655  */
0656 void MainWindow::redoTextChanged(const QString &text)
0657 {
0658     actionCollection()->action(QStringLiteral("edit_redo"))->setText(QString(i18n("Redo %1", text)));
0659 }
0660 
0661 
0662 /**
0663  * Update the caption based on the state of the undo stack.
0664  *
0665  * @param clean true if the symbol has not been changed, false otherwise
0666  */
0667 void MainWindow::cleanChanged(bool clean)
0668 {
0669     QString tab;
0670 
0671     if (m_tabWidget->currentIndex() == 1) {
0672         tab = QString(i18nc("The library tab title %1 is the file name", "%1 Library", m_url.fileName()));
0673     } else {
0674         tab = QString(i18nc("The editor tab title %1 is the file name", "%1 Editor", m_url.fileName()));
0675     }
0676 
0677     setCaption(tab, !clean);
0678 }
0679 
0680 
0681 /**
0682  * Change the tab selected.
0683  * This is connected to the QTabWidget::currentChanged() slot and indicates that the current tab
0684  * has changed. This allows the undo stack connections to be modified.
0685  *
0686  * @param index the index of the page
0687  */
0688 void MainWindow::currentChanged(int index)
0689 {
0690     if (index == 0) { // Editor
0691         m_undoGroup.setActiveStack(m_editor->undoStack());
0692         m_editor->updateStatusMessage();
0693     } else if (index == 1) { // QListWidget
0694         m_undoGroup.setActiveStack(m_symbolLibrary->undoStack());
0695         statusBar()->clearMessage();
0696     }
0697 }
0698 
0699 
0700 /**
0701  * Edit an existing symbol from the symbol library.
0702  * Check if the current symbol being edited has been changed. If yes, ask if it should be
0703  * saved or discarded.
0704  * Clear the contents of the editor and assign a copy of the item symbol to it to edit.
0705  * The actions are updated to reflect the settings of the symbol being edited.
0706  *
0707  * @param item a pointer to a QListWidgetItem that was double clicked.
0708  */
0709 void MainWindow::itemSelected(QListWidgetItem *item)
0710 {
0711     QPair<qint16, Symbol> pair;
0712 
0713     if (editorClean()) {
0714         m_editor->clear();
0715         pair.first = static_cast<qint16>(item->data(Qt::UserRole).toInt());
0716         pair.second = m_symbolLibrary->symbol(pair.first);
0717         m_editor->setSymbol(pair);
0718         setActionsFromSymbol(pair.second);
0719         m_tabWidget->setCurrentIndex(0);
0720     }
0721 }
0722 
0723 
0724 /**
0725  * Display a context menu for the list widget.
0726  * Options:
0727  *  Delete Symbol
0728  *
0729  * @param pos a const reference to a QPoint representing the cursor position
0730  */
0731 void MainWindow::listWidgetContextMenuRequested(const QPoint &pos)
0732 {
0733     if ((m_item = m_listWidget->itemAt(pos))) {
0734         if (!m_menu) {
0735             m_menu = new QMenu;
0736             m_menu->addAction(i18n("Delete Symbol"), this, SLOT(deleteSymbol()));
0737         }
0738 
0739         m_menu->popup(QCursor::pos());
0740     }
0741 }
0742 
0743 
0744 /**
0745  * Delete the symbol pointed to by m_item.
0746  */
0747 void MainWindow::deleteSymbol()
0748 {
0749     m_symbolLibrary->undoStack()->push(new DeleteSymbolCommand(m_symbolLibrary, static_cast<qint16>(m_item->data(Qt::UserRole).toInt())));
0750 }
0751 
0752 
0753 /**
0754  * Configure the application.
0755  * Display the configuration dialog, creating it if necessary.
0756  */
0757 void MainWindow::preferences()
0758 {
0759     if (KConfigDialog::showDialog(QStringLiteral("preferences"))) {
0760         return;
0761     }
0762 
0763     KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("preferences"), Configuration::self());
0764     dialog->setFaceType(KPageDialog::List);
0765 
0766     dialog->addPage(new EditorConfigPage(nullptr, QStringLiteral("EditorConfigPage")), i18nc("The Editor configuration page", "Editor"), QStringLiteral("preferences-desktop"));
0767 //    dialog->setHelp("ConfigurationDialog");
0768 
0769     connect(dialog, SIGNAL(settingsChanged(QString)), m_editor, SLOT(readSettings()));
0770 
0771     dialog->show();
0772 }
0773 
0774 
0775 /**
0776  * Set up the applications actions.
0777  * Create standard actions.
0778  * Create other actions, setting the icon and data as required.
0779  * Several actions are added to groups which are set as exclusive.
0780  * All actions are added to the applications QActionCollection.
0781  */
0782 void MainWindow::setupActions()
0783 {
0784     QAction *action;
0785     QActionGroup *actionGroup;
0786 
0787     KActionCollection *actions = actionCollection();
0788 
0789     // File menu
0790     KStandardAction::open(this, SLOT(fileOpen()), actions);
0791     KStandardAction::openNew(this, SLOT(newSymbol()), actions);
0792     KStandardAction::openRecent(this, SLOT(fileOpen(QUrl)), actions)->loadEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("RecentFiles")));
0793     KStandardAction::save(this, SLOT(save()), actions);
0794     KStandardAction::saveAs(this, SLOT(saveAs()), actions);
0795 
0796     action = new QAction(this);
0797     action->setText(i18n("Import Library"));
0798     action->setWhatsThis(i18n("Imports another library appending the symbols it contains to the current library."));
0799     connect(action, SIGNAL(triggered()), this, SLOT(importLibrary()));
0800     actions->addAction(QStringLiteral("importLibrary"), action);
0801 
0802     action = new QAction(this);
0803     action->setText(i18n("Save Symbol"));
0804     action->setWhatsThis(i18n("Save the symbol to the library. If this is a new symbol, subsequent saves will create additional symbols in the library. If the symbol was selected from the library to edit then saving will update that symbol in the library."));
0805     action->setIcon(QIcon::fromTheme(QStringLiteral("symboleditor-save-symbol")));
0806     connect(action, SIGNAL(triggered()), this, SLOT(saveSymbol()));
0807     actions->addAction(QStringLiteral("saveSymbol"), action);
0808 
0809     action = new QAction(this);
0810     action->setText(i18n("Save Symbol as New"));
0811     action->setWhatsThis(i18n("Save the current symbol as a new one in the library. Subsequent saves will update the new symbol."));
0812     connect(action, SIGNAL(triggered()), this, SLOT(saveSymbolAsNew()));
0813     actions->addAction(QStringLiteral("saveSymbolAsNew"), action);
0814 
0815     KStandardAction::close(this, SLOT(close()), actions);
0816     KStandardAction::quit(this, SLOT(quit()), actions);
0817 
0818     // Edit menu
0819     KStandardAction::undo(this, SLOT(undo()), actions);
0820     KStandardAction::redo(this, SLOT(redo()), actions);
0821 
0822     // Rendering menu
0823     action = new QAction(this);
0824     action->setText(i18n("Fill Path"));
0825     action->setWhatsThis(i18n("Enable path filling. The path defines the closed boundary of the shape and the path is filled with the selected fill method."));
0826     action->setIcon(QIcon::fromTheme(QStringLiteral("format-fill-color")));
0827     action->setCheckable(true);
0828     connect(action, SIGNAL(triggered(bool)), m_editor, SLOT(selectFilled(bool)));
0829     actions->addAction(QStringLiteral("fillPath"), action);
0830 
0831     actionGroup = new QActionGroup(this);
0832     actionGroup->setExclusive(true);
0833 
0834     action = new QAction(this);
0835     action->setText(i18n("Odd Even Fill"));
0836     action->setWhatsThis(i18n("The Odd Even fill method will fill alternate areas of the symbol."));
0837     action->setData(Qt::OddEvenFill);
0838     action->setIcon(QIcon::fromTheme(QStringLiteral("symboleditor-odd-even-fill")));
0839     action->setCheckable(true);
0840     actions->addAction(QStringLiteral("oddEvenFill"), action);
0841     actionGroup->addAction(action);
0842 
0843     action = new QAction(this);
0844     action->setText(i18n("Winding Fill"));
0845     action->setWhatsThis(i18n("The Winding fill method will fill the complete interior of the path."));
0846     action->setData(Qt::WindingFill);
0847     action->setIcon(QIcon::fromTheme(QStringLiteral("symboleditor-winding-fill")));
0848     action->setCheckable(true);
0849     actions->addAction(QStringLiteral("windingFill"), action);
0850     actionGroup->addAction(action);
0851 
0852     connect(actionGroup, SIGNAL(triggered(QAction*)), m_editor, SLOT(selectFillRule(QAction*)));
0853 
0854     actionGroup = new QActionGroup(this);
0855     actionGroup->setExclusive(true);
0856 
0857     action = new QAction(this);
0858     action->setText(i18n("Flat Cap"));
0859     action->setWhatsThis(i18n("The Flat Cap end type provides a square end that stops at the end point of the line.\n\nThis is only applicable to non-filled paths."));
0860     action->setData(Qt::FlatCap);
0861     action->setIcon(QIcon::fromTheme(QStringLiteral("stroke-cap-butt")));
0862     action->setCheckable(true);
0863     actions->addAction(QStringLiteral("flatCap"), action);
0864     actionGroup->addAction(action);
0865 
0866     action = new QAction(this);
0867     action->setText(i18n("Square Cap"));
0868     action->setWhatsThis(i18n("The Square Cap end type provides a square end that projects beyond the end point of the line by half the line width.\n\nThis is only applicable to non-filled paths."));
0869     action->setData(Qt::SquareCap);
0870     action->setIcon(QIcon::fromTheme(QStringLiteral("stroke-cap-square")));
0871     action->setCheckable(true);
0872     actions->addAction(QStringLiteral("squareCap"), action);
0873     actionGroup->addAction(action);
0874 
0875     action = new QAction(this);
0876     action->setText(i18n("Round Cap"));
0877     action->setWhatsThis(i18n("The Round Cap end type provides a round end that projects beyond the end point of the line with a radius of half the line width.\n\nThis is only applicable to non-filled paths."));
0878     action->setData(Qt::RoundCap);
0879     action->setIcon(QIcon::fromTheme(QStringLiteral("stroke-cap-round")));
0880     action->setCheckable(true);
0881     actions->addAction(QStringLiteral("roundCap"), action);
0882     actionGroup->addAction(action);
0883 
0884     connect(actionGroup, SIGNAL(triggered(QAction*)), m_editor, SLOT(selectCapStyle(QAction*)));
0885 
0886     actionGroup = new QActionGroup(this);
0887     actionGroup->setExclusive(true);
0888 
0889     action = new QAction(this);
0890     action->setText(i18n("Bevel Join"));
0891     action->setWhatsThis(i18n("The Bevel Join provides a beveled corner between two lines.\n\nThis is only applicable to non-filled paths."));
0892     action->setData(Qt::BevelJoin);
0893     action->setIcon(QIcon::fromTheme(QStringLiteral("stroke-join-bevel")));
0894     action->setCheckable(true);
0895     actions->addAction(QStringLiteral("bevelJoin"), action);
0896     actionGroup->addAction(action);
0897 
0898     action = new QAction(this);
0899     action->setText(i18n("Miter Join"));
0900     action->setWhatsThis(i18n("The Miter Join provides a mitered corner between two lines.\n\nThis is only applicable to non-filled paths."));
0901     action->setData(Qt::MiterJoin);
0902     action->setIcon(QIcon::fromTheme(QStringLiteral("stroke-join-miter")));
0903     action->setCheckable(true);
0904     actions->addAction(QStringLiteral("miterJoin"), action);
0905     actionGroup->addAction(action);
0906 
0907     action = new QAction(this);
0908     action->setText(i18n("Round Join"));
0909     action->setWhatsThis(i18n("The Round Join provides a rounded corner between two lines using a radius of half the line width.\n\nThis is only applicable to non-filled paths."));
0910     action->setData(Qt::RoundJoin);
0911     action->setIcon(QIcon::fromTheme(QStringLiteral("stroke-join-round")));
0912     action->setCheckable(true);
0913     actions->addAction(QStringLiteral("roundJoin"), action);
0914     actionGroup->addAction(action);
0915 
0916     connect(actionGroup, SIGNAL(triggered(QAction*)), m_editor, SLOT(selectJoinStyle(QAction*)));
0917 
0918     action = new QAction(this);
0919     action->setText(i18n("Increase Line Width"));
0920     action->setWhatsThis(i18n("Increases the line width.\n\nThis is only applicable to non-filled paths."));
0921     action->setIcon(QIcon::fromTheme(QStringLiteral("symboleditor-increase-line-width")));
0922     connect(action, SIGNAL(triggered()), m_editor, SLOT(increaseLineWidth()));
0923     actions->addAction(QStringLiteral("increaseLineWidth"), action);
0924 
0925     action = new QAction(this);
0926     action->setText(i18n("Decrease Line Width"));
0927     action->setWhatsThis(i18n("Decreases the line width.\n\nThis is only applicable to non-filled paths."));
0928     action->setIcon(QIcon::fromTheme(QStringLiteral("symboleditor-decrease-line-width")));
0929     connect(action, SIGNAL(triggered()), m_editor, SLOT(decreaseLineWidth()));
0930     actions->addAction(QStringLiteral("decreaseLineWidth"), action);
0931 
0932     // Tools Menu
0933     actionGroup = new QActionGroup(this);
0934     actionGroup->setExclusive(true);
0935 
0936     action = new QAction(this);
0937     action->setText(i18n("Move To"));
0938     action->setWhatsThis(i18n("Move the current point to a new position. This implicitly closes any existing sub path, starting a new one."));
0939     action->setData(Editor::MoveTo);
0940     action->setIcon(QIcon::fromTheme(QStringLiteral("go-jump")));
0941     action->setCheckable(true);
0942     actions->addAction(QStringLiteral("moveTo"), action);
0943     actionGroup->addAction(action);
0944 
0945     action = new QAction(this);
0946     action->setText(i18n("Draw To"));
0947     action->setWhatsThis(i18n("Add a straight line from the current position to a defined end point. The end point becomes the new current position."));
0948     action->setData(Editor::LineTo);
0949     action->setIcon(QIcon::fromTheme(QStringLiteral("draw-line")));
0950     action->setCheckable(true);
0951     actions->addAction(QStringLiteral("lineTo"), action);
0952     actionGroup->addAction(action);
0953 
0954     action = new QAction(this);
0955     action->setText(i18n("Cubic To"));
0956     action->setWhatsThis(i18n("Add a cubic bezier curve from the current position using two control points and an end point. The end point becomes the new current position."));
0957     action->setData(Editor::CubicTo);
0958     action->setIcon(QIcon::fromTheme(QStringLiteral("draw-bezier-curves")));
0959     action->setCheckable(true);
0960     actions->addAction(QStringLiteral("cubicTo"), action);
0961     actionGroup->addAction(action);
0962 
0963     action = new QAction(this);
0964     action->setText(i18n("Rectangle"));
0965     action->setWhatsThis(i18n("Add a rectangle as a separate sub path defined by two points representing the opposite corners."));
0966     action->setData(Editor::Rectangle);
0967     action->setIcon(QIcon::fromTheme(QStringLiteral("draw-rectangle")));
0968     action->setCheckable(true);
0969     actions->addAction(QStringLiteral("rectangle"), action);
0970     actionGroup->addAction(action);
0971 
0972     action = new QAction(this);
0973     action->setText(i18n("Ellipse"));
0974     action->setWhatsThis(i18n("Add an ellipse as a separate sub path defined by a bounding rectangle represented by two opposite corners."));
0975     action->setData(Editor::Ellipse);
0976     action->setIcon(QIcon::fromTheme(QStringLiteral("draw-ellipse")));
0977     action->setCheckable(true);
0978     actions->addAction(QStringLiteral("ellipse"), action);
0979     actionGroup->addAction(action);
0980 
0981     action = new QAction(this);
0982     action->setText(i18n("Insert Character"));
0983     action->setWhatsThis(i18n("Allows selection of a character from any font to be inserted as a closed sub path. The inserted character will overwrite any existing path, but additional sub paths may be added to the character."));
0984     action->setData(Editor::Character);
0985     action->setIcon(QIcon::fromTheme(QStringLiteral("insert-text")));
0986     action->setCheckable(true);
0987     actions->addAction(QStringLiteral("character"), action);
0988     actionGroup->addAction(action);
0989 
0990     connect(actionGroup, SIGNAL(triggered(QAction*)), m_editor, SLOT(selectTool(QAction*)));
0991 
0992     action = new QAction(this);
0993     action->setText(i18n("Rotate Left"));
0994     action->setWhatsThis(i18n("Rotate all the points of a path counter-clockwise 90 degrees around the center of the editor."));
0995     action->setIcon(QIcon::fromTheme(QStringLiteral("object-rotate-left")));
0996     connect(action, SIGNAL(triggered()), m_editor, SLOT(rotateLeft()));
0997     actions->addAction(QStringLiteral("rotateLeft"), action);
0998 
0999     action = new QAction(this);
1000     action->setText(i18n("Rotate Right"));
1001     action->setWhatsThis(i18n("Rotate all the points of a path clockwise 90 degrees around the center point of the editor."));
1002     action->setIcon(QIcon::fromTheme(QStringLiteral("object-rotate-right")));
1003     connect(action, SIGNAL(triggered()), m_editor, SLOT(rotateRight()));
1004     actions->addAction(QStringLiteral("rotateRight"), action);
1005 
1006     action = new QAction(this);
1007     action->setText(i18n("Flip Horizontal"));
1008     action->setWhatsThis(i18n("Flip all the points of the path horizontally about the vertical center of the editor."));
1009     action->setIcon(QIcon::fromTheme(QStringLiteral("object-flip-horizontal")));
1010     connect(action, SIGNAL(triggered()), m_editor, SLOT(flipHorizontal()));
1011     actions->addAction(QStringLiteral("flipHorizontal"), action);
1012 
1013     action = new QAction(this);
1014     action->setText(i18n("Flip Vertical"));
1015     action->setWhatsThis(i18n("Flip all the points of the path vertically about the horizontal center of the editor."));
1016     action->setIcon(QIcon::fromTheme(QStringLiteral("object-flip-vertical")));
1017     connect(action, SIGNAL(triggered()), m_editor, SLOT(flipVertical()));
1018     actions->addAction(QStringLiteral("flipVertical"), action);
1019 
1020     action = new QAction(this);
1021     action->setText(i18n("Scale to Preferred Size"));
1022     action->setWhatsThis(i18n("Scale the current symbol so that it fits within the preferred size of a symbol."));
1023     action->setIcon(QIcon::fromTheme(QStringLiteral("symboleditor-scale-preferred")));
1024     connect(action, SIGNAL(triggered()), m_editor, SLOT(scalePreferred()));
1025     actions->addAction(QStringLiteral("scalePreferred"), action);
1026 
1027     action = new QAction(this);
1028     action->setText(i18n("Enable Snap"));
1029     action->setWhatsThis(i18n("Enable snapping of points to guide intersections or to the grid."));
1030     action->setIcon(QIcon::fromTheme(QStringLiteral("snap-orthogonal")));
1031     action->setCheckable(true);
1032     connect(action, SIGNAL(toggled(bool)), m_editor, SLOT(enableSnap(bool)));
1033     actions->addAction(QStringLiteral("enableSnap"), action);
1034 
1035     action = new QAction(this);
1036     action->setText(i18n("Enable Guides"));
1037     action->setWhatsThis(i18n("Enable the generation of guide intersections."));
1038     action->setIcon(QIcon::fromTheme(QStringLiteral("snap-intersection")));
1039     action->setCheckable(true);
1040     connect(action, SIGNAL(toggled(bool)), m_editor, SLOT(enableGuides(bool)));
1041     actions->addAction(QStringLiteral("enableGuides"), action);
1042 
1043     // Settings Menu
1044     KStandardAction::preferences(this, SLOT(preferences()), actions);
1045 }
1046 
1047 
1048 /**
1049  * Set the actions status based on the settings in the specified Symbol.
1050  *
1051  * @param symbol a const reference to a Symbol
1052  */
1053 void MainWindow::setActionsFromSymbol(const Symbol &symbol)
1054 {
1055     action("fillPath")->setChecked(symbol.filled());
1056 
1057     switch (symbol.path().fillRule()) {
1058     case Qt::WindingFill:
1059         action("windingFill")->setChecked(true);
1060         break;
1061 
1062     case Qt::OddEvenFill:
1063         action("oddEvenFill")->setChecked(true);
1064         break;
1065     }
1066 
1067     switch (symbol.capStyle()) {
1068     case Qt::FlatCap:
1069         action("flatCap")->setChecked(true);
1070         break;
1071 
1072     case Qt::SquareCap:
1073         action("squareCap")->setChecked(true);
1074         break;
1075 
1076     case Qt::RoundCap:
1077         action("roundCap")->setChecked(true);
1078         break;
1079 
1080     case Qt::MPenCapStyle: // this is a combination of the Qt::FlatCap, Qt::SquareCap and Qt::RoundCap
1081         break;
1082     }
1083 
1084     switch (symbol.joinStyle()) {
1085     case Qt::BevelJoin:
1086         action("bevelJoin")->setChecked(true);
1087         break;
1088 
1089     case Qt::MiterJoin:
1090     case Qt::SvgMiterJoin:
1091         action("miterJoin")->setChecked(true);
1092         break;
1093 
1094     case Qt::RoundJoin:
1095         action("roundJoin")->setChecked(true);
1096         break;
1097 
1098     case Qt::MPenJoinStyle: // this is a combination of Qt::BevelJoin, Qt::MiterJoin, Qt::SvgMiterJoin and Qt::RoundJoin
1099         break;
1100     }
1101 
1102     action("increaseLineWidth")->setDisabled(symbol.lineWidth() == 1.00);
1103     action("decreaseLineWidth")->setDisabled(symbol.lineWidth() == 0.01);
1104 }
1105 
1106 #include "moc_MainWindow.cpp"