diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index dff87ce29..147887839 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -121,6 +121,7 @@ set(Mapper_Common_SRCS
gui/symbols/area_symbol_settings.cpp
gui/symbols/combined_symbol_settings.cpp
+ gui/symbols/icon_properties_widget.cpp
gui/symbols/line_symbol_settings.cpp
gui/symbols/point_symbol_editor_widget.cpp
gui/symbols/point_symbol_settings.cpp
diff --git a/src/gui/symbols/icon_properties_widget.cpp b/src/gui/symbols/icon_properties_widget.cpp
new file mode 100644
index 000000000..a2e847ed1
--- /dev/null
+++ b/src/gui/symbols/icon_properties_widget.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2018 Kai Pastor
+ *
+ * This file is part of OpenOrienteering.
+ *
+ * OpenOrienteering 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.
+ *
+ * OpenOrienteering 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 OpenOrienteering. If not, see .
+ */
+
+
+#include "icon_properties_widget.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "core/symbols/symbol.h"
+#include "gui/file_dialog.h"
+#include "gui/util_gui.h"
+#include "gui/symbols/symbol_setting_dialog.h"
+#include "util/backports.h"
+
+namespace OpenOrienteering {
+
+namespace {
+
+QString pngFileFilter()
+{
+ static const QString filter_template(QLatin1String("%1 (%2)"));
+ QStringList filters = {
+ filter_template.arg(::OpenOrienteering::IconPropertiesWidget::tr("PNG"), QLatin1String("*.png")),
+ ::OpenOrienteering::IconPropertiesWidget::tr("All files (*.*)")
+ };
+ return filters.join(QLatin1String(";;"));
+}
+
+
+} // namespace
+
+
+
+IconPropertiesWidget::IconPropertiesWidget(Symbol* symbol, SymbolSettingDialog* dialog)
+: QWidget(dialog)
+, dialog(dialog)
+, symbol(symbol)
+{
+ auto layout = new QFormLayout();
+ setLayout(layout);
+
+ layout->addRow(Util::Headline::create(tr("Default icon")));
+
+ default_icon_size_edit = Util::SpinBox::create(22, 256, tr("px"));
+ layout->addRow(tr("Preview width:"), default_icon_size_edit);
+
+ layout->addItem(Util::SpacerItem::create(this));
+
+ auto default_icon_layout = new QHBoxLayout();
+ default_icon_display = new QLabel();
+ default_icon_display->setFrameStyle(QFrame::Panel);
+ default_icon_display->setFrameShadow(QFrame::Sunken);
+ default_icon_display->setLineWidth(2);
+ default_icon_layout->addWidget(default_icon_display);
+ default_icon_layout->addStretch(1);
+ layout->addRow(default_icon_layout);
+
+ layout->addItem(Util::SpacerItem::create(this));
+
+ auto buttons_layer = new QHBoxLayout();
+ save_default_button = new QPushButton(tr("Save..."));
+ buttons_layer->addWidget(save_default_button);
+ copy_default_button = new QPushButton(tr("Copy to custom icon"));
+ buttons_layer->addWidget(copy_default_button);
+ buttons_layer->addStretch(1);
+
+ layout->addRow(buttons_layer);
+
+
+ layout->addItem(Util::SpacerItem::create(this));
+
+
+ layout->addRow(Util::Headline::create(tr("Custom icon")));
+
+ custom_icon_size_edit = new QLineEdit();
+ custom_icon_size_edit->setDisabled(true);
+ layout->addRow(tr("Width:"), custom_icon_size_edit);
+
+ layout->addItem(Util::SpacerItem::create(this));
+
+ auto custom_icon_layout = new QHBoxLayout();
+ custom_icon_display = new QLabel();
+ custom_icon_display->setFrameStyle(QFrame::Panel);
+ custom_icon_display->setFrameShadow(QFrame::Sunken);
+ custom_icon_display->setLineWidth(2);
+ custom_icon_layout->addWidget(custom_icon_display);
+ custom_icon_layout->addStretch(1);
+ layout->addRow(custom_icon_layout);
+
+ layout->addItem(Util::SpacerItem::create(this));
+
+ buttons_layer = new QHBoxLayout();
+ save_button = new QPushButton(tr("Save..."));
+ buttons_layer->addWidget(save_button);
+ load_button = new QPushButton(tr("Load..."));
+ buttons_layer->addWidget(load_button);
+ clear_button = new QPushButton(tr("Clear"));
+ buttons_layer->addWidget(clear_button);
+ buttons_layer->addStretch(1);
+
+ layout->addRow(buttons_layer);
+
+
+ connect(default_icon_size_edit, QOverload::of(&QSpinBox::valueChanged), this, &IconPropertiesWidget::sizeEdited, Qt::QueuedConnection);
+ connect(save_default_button, &QAbstractButton::clicked, this, &IconPropertiesWidget::saveClicked);
+ connect(copy_default_button, &QAbstractButton::clicked, this, &IconPropertiesWidget::copyClicked);
+ connect(save_button, &QAbstractButton::clicked, this, &IconPropertiesWidget::saveClicked);
+ connect(load_button, &QAbstractButton::clicked, this, &IconPropertiesWidget::loadClicked);
+ connect(clear_button, &QAbstractButton::clicked, this, &IconPropertiesWidget::clearClicked);
+
+ updateWidgets();
+}
+
+
+IconPropertiesWidget::~IconPropertiesWidget() = default;
+
+
+void IconPropertiesWidget::reset(Symbol* symbol)
+{
+ Q_ASSERT(symbol);
+ this->symbol = symbol;
+ updateWidgets();
+}
+
+
+void IconPropertiesWidget::updateWidgets()
+{
+ auto custom_icon = symbol->getCustomIcon();
+ symbol->setCustomIcon({});
+ auto default_icon = symbol->getIcon(dialog->getSourceMap());
+
+ default_icon_display->setPixmap(QPixmap::fromImage(default_icon));
+ default_icon_size_edit->setValue(default_icon.width());
+
+ custom_icon_display->setPixmap(QPixmap::fromImage(custom_icon));
+ if (custom_icon.isNull())
+ {
+ custom_icon_size_edit->setText(QLatin1String("-"));
+ save_button->setEnabled(false);
+ clear_button->setEnabled(false);
+ }
+ else
+ {
+ custom_icon_size_edit->setText(tr("%1 px").arg(custom_icon.width()));
+ save_button->setEnabled(true);
+ clear_button->setEnabled(true);
+ }
+
+ symbol->setCustomIcon(custom_icon);
+}
+
+
+void IconPropertiesWidget::sizeEdited(int size)
+{
+ if (default_icon_display->pixmap()->width() != size
+ && dialog->getSourceMap())
+ {
+ auto icon = symbol->createIcon(*dialog->getSourceMap(), size);
+ default_icon_display->setPixmap(QPixmap::fromImage(icon));
+ }
+}
+
+
+void IconPropertiesWidget::copyClicked()
+{
+ auto icon = default_icon_display->pixmap()->toImage();
+ if (symbol->getCustomIcon() != icon)
+ {
+ symbol->setCustomIcon(icon);
+ updateWidgets();
+ emit iconModified();
+ }
+}
+
+
+void IconPropertiesWidget::saveClicked()
+{
+ auto path = FileDialog::getSaveFileName(this, tr("Save symbol icon ..."), {}, pngFileFilter());
+ if (path.isEmpty())
+ return;
+
+ if (!path.endsWith(QLatin1String(".png", Qt::CaseInsensitive)))
+ path.append(QLatin1String(".png"));
+
+ auto icon = symbol->getCustomIcon();
+ if (icon.isNull() || sender() == save_default_button)
+ icon = default_icon_display->pixmap()->toImage();
+
+ QImageWriter writer{path};
+ if (!writer.write(icon))
+ {
+ QMessageBox::warning(this, tr("Error"),
+ tr("Failed to save the image:\n%1")
+ .arg(writer.errorString()) );
+ }
+}
+
+
+void IconPropertiesWidget::loadClicked()
+{
+ auto path = FileDialog::getOpenFileName(this, tr("Load symbol icon ..."), {}, pngFileFilter());
+ if (path.isEmpty())
+ return;
+
+ QImageReader reader{path};
+ auto icon = reader.read();
+ if (icon.isNull())
+ {
+ QMessageBox::warning(this, tr("Error"),
+ tr("Cannot open file:\n%1\n\n%2").
+ arg(path, reader.errorString()) );
+ return;
+ }
+
+ symbol->setCustomIcon(icon);
+ updateWidgets();
+ emit iconModified();
+}
+
+
+void IconPropertiesWidget::clearClicked()
+{
+ symbol->setCustomIcon({});
+ updateWidgets();
+ emit iconModified();
+}
+
+
+
+} // namespace OpenOrienteering
diff --git a/src/gui/symbols/icon_properties_widget.h b/src/gui/symbols/icon_properties_widget.h
new file mode 100644
index 000000000..0d9b3005e
--- /dev/null
+++ b/src/gui/symbols/icon_properties_widget.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2018 Kai Pastor
+ *
+ * This file is part of OpenOrienteering.
+ *
+ * OpenOrienteering 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.
+ *
+ * OpenOrienteering 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 OpenOrienteering. If not, see .
+ */
+
+
+#ifndef OPENORIENTEERING_ICON_PROPERTIES_WIDGET_H
+#define OPENORIENTEERING_ICON_PROPERTIES_WIDGET_H
+
+#include
+
+#include "gui/symbols/symbol_properties_widget.h"
+
+class QAbstractButton;
+class QLabel;
+class QSpinBox;
+class QLineEdit;
+
+namespace OpenOrienteering {
+
+class Map;
+class Symbol;
+
+
+/**
+ * A widget for modifying symbol icons.
+ */
+class IconPropertiesWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit IconPropertiesWidget(Symbol* symbol, SymbolSettingDialog* dialog = nullptr);
+
+ ~IconPropertiesWidget() override;
+
+ void reset(Symbol* symbol);
+
+signals:
+ void iconModified();
+
+protected:
+ void updateWidgets();
+
+ void sizeEdited(int size);
+
+ void copyClicked();
+
+ void saveClicked();
+
+ void loadClicked();
+
+ void clearClicked();
+
+private:
+ SymbolSettingDialog* const dialog;
+ Symbol* symbol;
+
+ QLabel* default_icon_display;
+ QSpinBox* default_icon_size_edit;
+ QLabel* custom_icon_display;
+ QLineEdit* custom_icon_size_edit;
+ QAbstractButton* save_default_button;
+ QAbstractButton* copy_default_button;
+ QAbstractButton* save_button;
+ QAbstractButton* load_button;
+ QAbstractButton* clear_button;
+};
+
+
+} // namespace OpenOrienteering
+
+#endif // OPENORIENTEERING_ICON_PROPERTIES_WIDGET_H
diff --git a/src/gui/symbols/symbol_properties_widget.cpp b/src/gui/symbols/symbol_properties_widget.cpp
index cb63727ad..fbe08c613 100644
--- a/src/gui/symbols/symbol_properties_widget.cpp
+++ b/src/gui/symbols/symbol_properties_widget.cpp
@@ -41,6 +41,7 @@
#include "core/map.h"
#include "core/symbols/symbol.h"
+#include "gui/symbols/icon_properties_widget.h"
#include "gui/symbols/symbol_setting_dialog.h"
#include "util/translation_util.h"
#include "util/backports.h"
@@ -77,6 +78,9 @@ SymbolPropertiesWidget::SymbolPropertiesWidget(Symbol* symbol, SymbolSettingDial
auto description_label = new QLabel(tr("Description:"));
description_edit = new QTextEdit();
helper_symbol_check = new QCheckBox(tr("Helper symbol (not shown in finished map)"));
+
+ icon_widget = new IconPropertiesWidget(symbol, dialog); // before reset()
+
SymbolPropertiesWidget::reset(symbol);
for (auto editor : number_editors)
@@ -124,6 +128,9 @@ SymbolPropertiesWidget::SymbolPropertiesWidget(Symbol* symbol, SymbolSettingDial
layout->addWidget(helper_symbol_check, row, col, 1, num_col);
addPropertiesGroup(tr("General"), generalTab);
+
+ connect(icon_widget, &IconPropertiesWidget::iconModified, this, &SymbolPropertiesWidget::propertiesModified);
+ addPropertiesGroup(tr("Icon"), icon_widget);
}
@@ -337,6 +344,8 @@ void SymbolPropertiesWidget::reset(Symbol* symbol)
}
updateTextEdits();
helper_symbol_check->setChecked(symbol->isHelperSymbol());
+
+ icon_widget->reset(symbol);
}
diff --git a/src/gui/symbols/symbol_properties_widget.h b/src/gui/symbols/symbol_properties_widget.h
index bd39351da..e12c1258d 100644
--- a/src/gui/symbols/symbol_properties_widget.h
+++ b/src/gui/symbols/symbol_properties_widget.h
@@ -38,6 +38,7 @@ class QWidget;
namespace OpenOrienteering {
+class IconPropertiesWidget;
class Symbol;
class SymbolSettingDialog;
@@ -105,6 +106,8 @@ protected slots:
QLineEdit* name_edit;
QTextEdit* description_edit;
QCheckBox* helper_symbol_check;
+
+ IconPropertiesWidget* icon_widget;
};