aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlessandro Portale <alessandro.portale@qt.io>2016-09-15 17:55:28 +0200
committerAlessandro Portale <alessandro.portale@qt.io>2016-09-16 12:39:49 +0000
commit4ea332b0620c773abbd1dcf1689ec6c7253069ca (patch)
treef9c4e5c806dfc87aaebe4de041eb99357de96fd8
parent1a9919a20811cea6bf2fc77639f668c3a595883a (diff)
ScxmlEditor: Initial import
Change-Id: I4701b77ebd4e2520f2616c42206ac17be3a12b60 Reviewed-by: Tobias Hunger <tobias.hunger@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/plugins/plugins.pro1
-rw-r--r--src/plugins/plugins.qbs1
-rw-r--r--src/plugins/scxmleditor/ScxmlEditor.json.in20
-rw-r--r--src/plugins/scxmleditor/ScxmlEditor.mimetypes.xml8
-rw-r--r--src/plugins/scxmleditor/common/colorpicker.cpp117
-rw-r--r--src/plugins/scxmleditor/common/colorpicker.h60
-rw-r--r--src/plugins/scxmleditor/common/colorpicker.ui175
-rw-r--r--src/plugins/scxmleditor/common/colorsettings.cpp111
-rw-r--r--src/plugins/scxmleditor/common/colorsettings.h54
-rw-r--r--src/plugins/scxmleditor/common/colorsettings.ui66
-rw-r--r--src/plugins/scxmleditor/common/colorthemedialog.cpp43
-rw-r--r--src/plugins/scxmleditor/common/colorthemedialog.h49
-rw-r--r--src/plugins/scxmleditor/common/colorthemedialog.ui83
-rw-r--r--src/plugins/scxmleditor/common/colorthemes.cpp188
-rw-r--r--src/plugins/scxmleditor/common/colorthemes.h73
-rw-r--r--src/plugins/scxmleditor/common/colorthemeview.cpp189
-rw-r--r--src/plugins/scxmleditor/common/colorthemeview.h95
-rw-r--r--src/plugins/scxmleditor/common/colortoolbutton.cpp113
-rw-r--r--src/plugins/scxmleditor/common/colortoolbutton.h79
-rw-r--r--src/plugins/scxmleditor/common/common.pri77
-rw-r--r--src/plugins/scxmleditor/common/common.qrc45
-rw-r--r--src/plugins/scxmleditor/common/dragshapebutton.cpp67
-rw-r--r--src/plugins/scxmleditor/common/dragshapebutton.h52
-rw-r--r--src/plugins/scxmleditor/common/graphicsview.cpp326
-rw-r--r--src/plugins/scxmleditor/common/graphicsview.h101
-rw-r--r--src/plugins/scxmleditor/common/images/adjust_height.pngbin0 -> 1305 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/adjust_size.pngbin0 -> 2452 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/adjust_width.pngbin0 -> 1137 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/align_bottom.pngbin0 -> 1263 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/align_horizontal.pngbin0 -> 1060 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/align_left.pngbin0 -> 1069 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/align_right.pngbin0 -> 1061 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/align_top.pngbin0 -> 1261 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/align_vertical.pngbin0 -> 973 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/arrow_down.pngbin0 -> 177 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/arrow_right.pngbin0 -> 136 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/colorthemes.pngbin0 -> 761 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/final.pngbin0 -> 3452 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/font_color.pngbin0 -> 689 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/fullnamespace.pngbin0 -> 108 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/history.pngbin0 -> 4888 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-close.pngbin0 -> 179 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-copy.pngbin0 -> 1519 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-cut.pngbin0 -> 1736 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-export-canvas.pngbin0 -> 856 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-filter.pngbin0 -> 146 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-fit-screen.pngbin0 -> 244 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-pan.pngbin0 -> 783 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-paste.pngbin0 -> 1861 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-redo.pngbin0 -> 745 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-search.pngbin0 -> 997 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-undo.pngbin0 -> 726 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-zoom-in.pngbin0 -> 1119 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/icon-zoom-out.pngbin0 -> 1053 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/initial.pngbin0 -> 591 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/more_colors.pngbin0 -> 3139 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/navigator.pngbin0 -> 966 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/parallel.pngbin0 -> 5251 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/parallel_icon.pngbin0 -> 535 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/properties.pngbin0 -> 1415 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/screenshot.pngbin0 -> 1321 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/search.pngbin0 -> 919 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/state.pngbin0 -> 4476 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/state_color.pngbin0 -> 3485 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/statistics.pngbin0 -> 957 bytes
-rw-r--r--src/plugins/scxmleditor/common/images/structure.pngbin0 -> 5924 bytes
-rw-r--r--src/plugins/scxmleditor/common/magnifier.cpp128
-rw-r--r--src/plugins/scxmleditor/common/magnifier.h75
-rw-r--r--src/plugins/scxmleditor/common/magnifier.ui49
-rw-r--r--src/plugins/scxmleditor/common/mainwidget.cpp789
-rw-r--r--src/plugins/scxmleditor/common/mainwidget.h135
-rw-r--r--src/plugins/scxmleditor/common/mainwidget.ui191
-rw-r--r--src/plugins/scxmleditor/common/movableframe.cpp60
-rw-r--r--src/plugins/scxmleditor/common/movableframe.h62
-rw-r--r--src/plugins/scxmleditor/common/navigator.cpp75
-rw-r--r--src/plugins/scxmleditor/common/navigator.h64
-rw-r--r--src/plugins/scxmleditor/common/navigator.ui146
-rw-r--r--src/plugins/scxmleditor/common/navigatorgraphicsview.cpp105
-rw-r--r--src/plugins/scxmleditor/common/navigatorgraphicsview.h66
-rw-r--r--src/plugins/scxmleditor/common/navigatorslider.cpp57
-rw-r--r--src/plugins/scxmleditor/common/navigatorslider.h60
-rw-r--r--src/plugins/scxmleditor/common/navigatorslider.ui143
-rw-r--r--src/plugins/scxmleditor/common/search.cpp99
-rw-r--r--src/plugins/scxmleditor/common/search.h86
-rw-r--r--src/plugins/scxmleditor/common/search.ui210
-rw-r--r--src/plugins/scxmleditor/common/searchmodel.cpp148
-rw-r--r--src/plugins/scxmleditor/common/searchmodel.h66
-rw-r--r--src/plugins/scxmleditor/common/shapegroupwidget.cpp59
-rw-r--r--src/plugins/scxmleditor/common/shapegroupwidget.h50
-rw-r--r--src/plugins/scxmleditor/common/shapegroupwidget.ui166
-rw-r--r--src/plugins/scxmleditor/common/shapestoolbox.cpp73
-rw-r--r--src/plugins/scxmleditor/common/shapestoolbox.h62
-rw-r--r--src/plugins/scxmleditor/common/shapestoolbox.ui114
-rw-r--r--src/plugins/scxmleditor/common/sizegrip.cpp96
-rw-r--r--src/plugins/scxmleditor/common/sizegrip.h57
-rw-r--r--src/plugins/scxmleditor/common/stateproperties.cpp148
-rw-r--r--src/plugins/scxmleditor/common/stateproperties.h78
-rw-r--r--src/plugins/scxmleditor/common/stateproperties.ui284
-rw-r--r--src/plugins/scxmleditor/common/stateview.cpp108
-rw-r--r--src/plugins/scxmleditor/common/stateview.h71
-rw-r--r--src/plugins/scxmleditor/common/stateview.ui99
-rw-r--r--src/plugins/scxmleditor/common/statistics.cpp157
-rw-r--r--src/plugins/scxmleditor/common/statistics.h82
-rw-r--r--src/plugins/scxmleditor/common/statistics.ui143
-rw-r--r--src/plugins/scxmleditor/common/statisticsdialog.cpp42
-rw-r--r--src/plugins/scxmleditor/common/statisticsdialog.h52
-rw-r--r--src/plugins/scxmleditor/common/statisticsdialog.ui75
-rw-r--r--src/plugins/scxmleditor/common/structure.cpp294
-rw-r--r--src/plugins/scxmleditor/common/structure.h105
-rw-r--r--src/plugins/scxmleditor/common/structure.ui331
-rw-r--r--src/plugins/scxmleditor/common/structuremodel.cpp328
-rw-r--r--src/plugins/scxmleditor/common/structuremodel.h109
-rw-r--r--src/plugins/scxmleditor/common/treeview.cpp61
-rw-r--r--src/plugins/scxmleditor/common/treeview.h63
-rw-r--r--src/plugins/scxmleditor/outputpane/errorwidget.cpp191
-rw-r--r--src/plugins/scxmleditor/outputpane/errorwidget.h84
-rw-r--r--src/plugins/scxmleditor/outputpane/errorwidget.ui168
-rw-r--r--src/plugins/scxmleditor/outputpane/outputpane.h59
-rw-r--r--src/plugins/scxmleditor/outputpane/outputpane.pri20
-rw-r--r--src/plugins/scxmleditor/outputpane/outputtabwidget.cpp197
-rw-r--r--src/plugins/scxmleditor/outputpane/outputtabwidget.h98
-rw-r--r--src/plugins/scxmleditor/outputpane/outputtabwidget.ui113
-rw-r--r--src/plugins/scxmleditor/outputpane/tableview.cpp40
-rw-r--r--src/plugins/scxmleditor/outputpane/tableview.h49
-rw-r--r--src/plugins/scxmleditor/outputpane/warning.cpp75
-rw-r--r--src/plugins/scxmleditor/outputpane/warning.h93
-rw-r--r--src/plugins/scxmleditor/outputpane/warningmodel.cpp252
-rw-r--r--src/plugins/scxmleditor/outputpane/warningmodel.h84
-rw-r--r--src/plugins/scxmleditor/plugin_interface/actionhandler.cpp90
-rw-r--r--src/plugins/scxmleditor/plugin_interface/actionhandler.h52
-rw-r--r--src/plugins/scxmleditor/plugin_interface/actionprovider.h52
-rw-r--r--src/plugins/scxmleditor/plugin_interface/attributeitemdelegate.cpp44
-rw-r--r--src/plugins/scxmleditor/plugin_interface/attributeitemdelegate.h52
-rw-r--r--src/plugins/scxmleditor/plugin_interface/attributeitemmodel.cpp48
-rw-r--r--src/plugins/scxmleditor/plugin_interface/attributeitemmodel.h54
-rw-r--r--src/plugins/scxmleditor/plugin_interface/baseitem.cpp430
-rw-r--r--src/plugins/scxmleditor/plugin_interface/baseitem.h151
-rw-r--r--src/plugins/scxmleditor/plugin_interface/connectableitem.cpp809
-rw-r--r--src/plugins/scxmleditor/plugin_interface/connectableitem.h146
-rw-r--r--src/plugins/scxmleditor/plugin_interface/cornergrabberitem.cpp93
-rw-r--r--src/plugins/scxmleditor/plugin_interface/cornergrabberitem.h96
-rw-r--r--src/plugins/scxmleditor/plugin_interface/finalstateitem.cpp80
-rw-r--r--src/plugins/scxmleditor/plugin_interface/finalstateitem.h61
-rw-r--r--src/plugins/scxmleditor/plugin_interface/genericscxmlplugin.cpp85
-rw-r--r--src/plugins/scxmleditor/plugin_interface/genericscxmlplugin.h69
-rw-r--r--src/plugins/scxmleditor/plugin_interface/graphicsitemprovider.h51
-rw-r--r--src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp944
-rw-r--r--src/plugins/scxmleditor/plugin_interface/graphicsscene.h152
-rw-r--r--src/plugins/scxmleditor/plugin_interface/highlightitem.cpp96
-rw-r--r--src/plugins/scxmleditor/plugin_interface/highlightitem.h61
-rw-r--r--src/plugins/scxmleditor/plugin_interface/historyitem.cpp86
-rw-r--r--src/plugins/scxmleditor/plugin_interface/historyitem.h61
-rw-r--r--src/plugins/scxmleditor/plugin_interface/idwarningitem.cpp86
-rw-r--r--src/plugins/scxmleditor/plugin_interface/idwarningitem.h61
-rw-r--r--src/plugins/scxmleditor/plugin_interface/imageprovider.cpp33
-rw-r--r--src/plugins/scxmleditor/plugin_interface/imageprovider.h47
-rw-r--r--src/plugins/scxmleditor/plugin_interface/initialstateitem.cpp134
-rw-r--r--src/plugins/scxmleditor/plugin_interface/initialstateitem.h67
-rw-r--r--src/plugins/scxmleditor/plugin_interface/initialwarningitem.cpp51
-rw-r--r--src/plugins/scxmleditor/plugin_interface/initialwarningitem.h55
-rw-r--r--src/plugins/scxmleditor/plugin_interface/isceditor.h78
-rw-r--r--src/plugins/scxmleditor/plugin_interface/layoutitem.cpp53
-rw-r--r--src/plugins/scxmleditor/plugin_interface/layoutitem.h55
-rw-r--r--src/plugins/scxmleditor/plugin_interface/mytypes.h115
-rw-r--r--src/plugins/scxmleditor/plugin_interface/parallelitem.cpp117
-rw-r--r--src/plugins/scxmleditor/plugin_interface/parallelitem.h59
-rw-r--r--src/plugins/scxmleditor/plugin_interface/plugin_interface.pri93
-rw-r--r--src/plugins/scxmleditor/plugin_interface/quicktransitionitem.cpp133
-rw-r--r--src/plugins/scxmleditor/plugin_interface/quicktransitionitem.h79
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.cpp107
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.h46
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.cpp171
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.h48
-rw-r--r--src/plugins/scxmleditor/plugin_interface/sceneutils.cpp419
-rw-r--r--src/plugins/scxmleditor/plugin_interface/sceneutils.h90
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scgraphicsitemprovider.cpp54
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scgraphicsitemprovider.h43
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scshapeprovider.cpp181
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scshapeprovider.h68
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scutilsprovider.cpp87
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scutilsprovider.h45
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp701
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmldocument.h296
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmlnamespace.cpp55
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmlnamespace.h52
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmltag.cpp679
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmltag.h148
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmltagutils.cpp386
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmltagutils.h60
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmltypes.h449
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmluifactory.cpp126
-rw-r--r--src/plugins/scxmleditor/plugin_interface/scxmluifactory.h71
-rw-r--r--src/plugins/scxmleditor/plugin_interface/serializer.cpp121
-rw-r--r--src/plugins/scxmleditor/plugin_interface/serializer.h133
-rw-r--r--src/plugins/scxmleditor/plugin_interface/shapeprovider.cpp33
-rw-r--r--src/plugins/scxmleditor/plugin_interface/shapeprovider.h85
-rw-r--r--src/plugins/scxmleditor/plugin_interface/snapline.cpp57
-rw-r--r--src/plugins/scxmleditor/plugin_interface/snapline.h50
-rw-r--r--src/plugins/scxmleditor/plugin_interface/stateitem.cpp581
-rw-r--r--src/plugins/scxmleditor/plugin_interface/stateitem.h105
-rw-r--r--src/plugins/scxmleditor/plugin_interface/statewarningitem.cpp74
-rw-r--r--src/plugins/scxmleditor/plugin_interface/statewarningitem.h63
-rw-r--r--src/plugins/scxmleditor/plugin_interface/tagtextitem.cpp143
-rw-r--r--src/plugins/scxmleditor/plugin_interface/tagtextitem.h82
-rw-r--r--src/plugins/scxmleditor/plugin_interface/textitem.cpp194
-rw-r--r--src/plugins/scxmleditor/plugin_interface/textitem.h76
-rw-r--r--src/plugins/scxmleditor/plugin_interface/transitionitem.cpp1276
-rw-r--r--src/plugins/scxmleditor/plugin_interface/transitionitem.h181
-rw-r--r--src/plugins/scxmleditor/plugin_interface/transitionwarningitem.cpp53
-rw-r--r--src/plugins/scxmleditor/plugin_interface/transitionwarningitem.h58
-rw-r--r--src/plugins/scxmleditor/plugin_interface/undocommands.cpp403
-rw-r--r--src/plugins/scxmleditor/plugin_interface/undocommands.h232
-rw-r--r--src/plugins/scxmleditor/plugin_interface/utilsprovider.cpp33
-rw-r--r--src/plugins/scxmleditor/plugin_interface/utilsprovider.h49
-rw-r--r--src/plugins/scxmleditor/plugin_interface/warningitem.cpp171
-rw-r--r--src/plugins/scxmleditor/plugin_interface/warningitem.h85
-rw-r--r--src/plugins/scxmleditor/plugin_interface/warningprovider.h54
-rw-r--r--src/plugins/scxmleditor/resources.qrc5
-rw-r--r--src/plugins/scxmleditor/scxmlcontext.cpp35
-rw-r--r--src/plugins/scxmleditor/scxmlcontext.h40
-rw-r--r--src/plugins/scxmleditor/scxmleditor.pro30
-rw-r--r--src/plugins/scxmleditor/scxmleditor.qbs162
-rw-r--r--src/plugins/scxmleditor/scxmleditor_dependencies.pri10
-rw-r--r--src/plugins/scxmleditor/scxmleditor_global.h34
-rw-r--r--src/plugins/scxmleditor/scxmleditorconstants.h84
-rw-r--r--src/plugins/scxmleditor/scxmleditordata.cpp243
-rw-r--r--src/plugins/scxmleditor/scxmleditordata.h74
-rw-r--r--src/plugins/scxmleditor/scxmleditordocument.cpp162
-rw-r--r--src/plugins/scxmleditor/scxmleditordocument.h73
-rw-r--r--src/plugins/scxmleditor/scxmleditorfactory.cpp59
-rw-r--r--src/plugins/scxmleditor/scxmleditorfactory.h49
-rw-r--r--src/plugins/scxmleditor/scxmleditorplugin.cpp69
-rw-r--r--src/plugins/scxmleditor/scxmleditorplugin.h46
-rw-r--r--src/plugins/scxmleditor/scxmleditorstack.cpp96
-rw-r--r--src/plugins/scxmleditor/scxmleditorstack.h61
-rw-r--r--src/plugins/scxmleditor/scxmltexteditor.cpp80
-rw-r--r--src/plugins/scxmleditor/scxmltexteditor.h55
237 files changed, 25257 insertions, 0 deletions
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 4df72409821..1d1c759a0d1 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -57,6 +57,7 @@ SUBDIRS = \
winrt \
qmlprofiler \
updateinfo \
+ scxmleditor \
welcome
DO_NOT_BUILD_QMLDESIGNER = $$(DO_NOT_BUILD_QMLDESIGNER)
diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs
index 6402b453ffa..98a0e728c6c 100644
--- a/src/plugins/plugins.qbs
+++ b/src/plugins/plugins.qbs
@@ -57,6 +57,7 @@ Project {
"qtsupport/qtsupport.qbs",
"remotelinux/remotelinux.qbs",
"resourceeditor/resourceeditor.qbs",
+ "scxmleditor/scxmleditor.qbs",
"subversion/subversion.qbs",
"tasklist/tasklist.qbs",
"texteditor/texteditor.qbs",
diff --git a/src/plugins/scxmleditor/ScxmlEditor.json.in b/src/plugins/scxmleditor/ScxmlEditor.json.in
new file mode 100644
index 00000000000..b9c965354b3
--- /dev/null
+++ b/src/plugins/scxmleditor/ScxmlEditor.json.in
@@ -0,0 +1,20 @@
+{
+ \"Name\" : \"ScxmlEditor\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) 2016 The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
+ ],
+ \"Category\" : \"Modeling\",
+ \"Description\" : \"Visual Editor for SCXML (State Chart XML) files.\",
+ \"Url\" : \"http://www.qt.io\",
+ \"Experimental\" : true,
+ $$dependencyList
+}
diff --git a/src/plugins/scxmleditor/ScxmlEditor.mimetypes.xml b/src/plugins/scxmleditor/ScxmlEditor.mimetypes.xml
new file mode 100644
index 00000000000..491f364f894
--- /dev/null
+++ b/src/plugins/scxmleditor/ScxmlEditor.mimetypes.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
+ <mime-type type="application/scxml+xml">
+ <sub-class-of type="text/xml"/>
+ <comment>SCXML file</comment>
+ <glob pattern="*.scxml"/>
+ </mime-type>
+</mime-info>
diff --git a/src/plugins/scxmleditor/common/colorpicker.cpp b/src/plugins/scxmleditor/common/colorpicker.cpp
new file mode 100644
index 00000000000..fc999765964
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorpicker.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "colorpicker.h"
+#include "scxmleditorconstants.h"
+
+#include <QToolButton>
+
+#include <coreplugin/icore.h>
+
+using namespace ScxmlEditor::Common;
+
+const char C_SETTINGS_COLORPICKER_LASTUSEDCOLORS[] = "ScxmlEditor/ColorPickerLastUsedColors_%1";
+
+ColorPicker::ColorPicker(const QString &key, QWidget *parent)
+ : QFrame(parent)
+ , m_key(key)
+{
+ m_ui.setupUi(this);
+
+ const QVector<QRgb> colors = {
+ qRgb(0xed, 0xf7, 0xf2), qRgb(0xdf, 0xd3, 0xb6), qRgb(0x89, 0x72, 0x5b), qRgb(0xff, 0xd3, 0x93), qRgb(0xff, 0x97, 0x4f),
+ qRgb(0xff, 0x85, 0x0d), qRgb(0xf7, 0xe9, 0x67), qRgb(0xef, 0xc9, 0x4c), qRgb(0xff, 0xe1, 0x1a), qRgb(0xc2, 0xe0, 0x78),
+ qRgb(0xa2, 0xd7, 0x00), qRgb(0x45, 0xbf, 0x08), qRgb(0x91, 0xe3, 0xd8), qRgb(0x3c, 0xb3, 0xfd), qRgb(0x34, 0x67, 0xba),
+ qRgb(0xc5, 0xba, 0xfc), qRgb(0xb6, 0x65, 0xfc), qRgb(0xa5, 0x08, 0xd0), qRgb(0xcc, 0x56, 0x64), qRgb(0x96, 0x2d, 0x3e)
+ };
+
+ auto vBoxLayout = new QVBoxLayout;
+ vBoxLayout->setContentsMargins(0, 0, 0, 0);
+ vBoxLayout->setMargin(0);
+ vBoxLayout->setSpacing(0);
+
+ const int buttonRowsCount = 4;
+ const int buttonColumnsCount = 5;
+
+ for (int r = 0; r < buttonRowsCount; ++r) {
+ auto hBoxLayout = new QHBoxLayout;
+ hBoxLayout->setContentsMargins(0, 0, 0, 0);
+ hBoxLayout->setMargin(0);
+ hBoxLayout->setSpacing(0);
+
+ for (int c = 0; c < buttonColumnsCount; ++c)
+ hBoxLayout->addWidget(createButton(colors[r * buttonColumnsCount + c]));
+
+ hBoxLayout->addStretch();
+ vBoxLayout->addLayout(hBoxLayout);
+ }
+
+ m_ui.basicColorContentFrame->setLayout(vBoxLayout);
+
+ const QStringList lastColors = Core::ICore::settings()->value(
+ QString::fromLatin1(C_SETTINGS_COLORPICKER_LASTUSEDCOLORS).arg(m_key), QStringList()).toStringList();
+ for (int i = lastColors.count(); i--;)
+ setLastUsedColor(lastColors[i]);
+}
+
+ColorPicker::~ColorPicker()
+{
+ Core::ICore::settings()->setValue(QString::fromLatin1(C_SETTINGS_COLORPICKER_LASTUSEDCOLORS).arg(m_key),
+ m_lastUsedColorNames);
+}
+
+void ColorPicker::setLastUsedColor(const QString &colorName)
+{
+ if (colorName.isEmpty())
+ return;
+
+ if (m_lastUsedColorNames.contains(colorName))
+ return;
+
+ m_lastUsedColorNames.insert(0, colorName);
+ m_lastUsedColorButtons.insert(0, createButton(colorName));
+
+ while (m_lastUsedColorButtons.count() > 5) {
+ m_lastUsedColorButtons.takeLast()->deleteLater();
+ m_lastUsedColorNames.takeLast();
+ }
+
+ m_ui.lastUsedColorLayout->insertWidget(0, m_lastUsedColorButtons.first());
+}
+
+QToolButton *ColorPicker::createButton(const QColor &color)
+{
+ auto button = new QToolButton;
+ button->setObjectName("colorPickerButton");
+ QPixmap pixmap(15, 15);
+ pixmap.fill(color);
+ button->setIcon(QIcon(pixmap));
+
+ connect(button, &QToolButton::clicked, this, [this, color] {
+ emit colorSelected(QColor(color).name());
+ });
+
+ return button;
+}
diff --git a/src/plugins/scxmleditor/common/colorpicker.h b/src/plugins/scxmleditor/common/colorpicker.h
new file mode 100644
index 00000000000..70200aa3b55
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorpicker.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_colorpicker.h"
+#include <QFrame>
+
+QT_FORWARD_DECLARE_CLASS(QToolButton)
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+class ColorPicker : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit ColorPicker(const QString &key, QWidget *parent = nullptr);
+ ~ColorPicker() override;
+
+ void setLastUsedColor(const QString &colorName);
+
+signals:
+ void colorSelected(const QString &colorName);
+
+private:
+ QToolButton *createButton(const QColor &color);
+
+ QStringList m_lastUsedColorNames;
+ QVector<QToolButton*> m_lastUsedColorButtons;
+ QString m_key;
+ Ui::ColorPicker m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/colorpicker.ui b/src/plugins/scxmleditor/common/colorpicker.ui
new file mode 100644
index 00000000000..12521f41152
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorpicker.ui
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::ColorPicker</class>
+ <widget class="QFrame" name="ScxmlEditor::Common::ColorPicker">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>79</width>
+ <height>183</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Frame</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="basicColorFrame">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="basicColorTitle">
+ <property name="text">
+ <string>Basic Colors</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="basicColorContentFrame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="lastUsedColorFrame">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="lastUsedColorTitle">
+ <property name="text">
+ <string>Last used colors</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="lastUsedColorContentFrame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="lastUsedColorLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/colorsettings.cpp b/src/plugins/scxmleditor/common/colorsettings.cpp
new file mode 100644
index 00000000000..e71b302fbd9
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorsettings.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "colorsettings.h"
+#include "scxmleditorconstants.h"
+
+#include <QInputDialog>
+#include <QMessageBox>
+
+#include <coreplugin/icore.h>
+
+using namespace ScxmlEditor::Common;
+
+ColorSettings::ColorSettings(QWidget *parent)
+ : QFrame(parent)
+{
+ m_ui.setupUi(this);
+
+ m_ui.m_colorThemeView->setEnabled(false);
+ connect(m_ui.m_comboColorThemes, static_cast<void (QComboBox::*)(const QString&)>(&QComboBox::currentIndexChanged), this, &ColorSettings::selectTheme);
+ connect(m_ui.m_colorThemeView, &ColorThemeView::colorChanged, this, &ColorSettings::updateCurrentColors);
+ connect(m_ui.m_addColorTheme, &QToolButton::clicked, this, &ColorSettings::createTheme);
+ connect(m_ui.m_removeColorTheme, &QToolButton::clicked, this, &ColorSettings::removeTheme);
+
+ const QSettings *s = Core::ICore::settings();
+
+ m_colorThemes = s->value(Constants::C_SETTINGS_COLORSETTINGS_COLORTHEMES).toMap();
+
+ m_ui.m_comboColorThemes->clear();
+ foreach (const QString &key, m_colorThemes.keys())
+ m_ui.m_comboColorThemes->addItem(key);
+
+ m_ui.m_comboColorThemes->setCurrentText(s->value(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME).toString());
+}
+
+void ColorSettings::save()
+{
+ QSettings *s = Core::ICore::settings();
+ s->setValue(Constants::C_SETTINGS_COLORSETTINGS_COLORTHEMES, m_colorThemes);
+ s->setValue(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME, m_ui.m_comboColorThemes->currentText());
+}
+
+void ColorSettings::updateCurrentColors()
+{
+ m_colorThemes[m_ui.m_comboColorThemes->currentText()] = m_ui.m_colorThemeView->colorData();
+}
+
+void ColorSettings::selectTheme(const QString &name)
+{
+ m_ui.m_colorThemeView->reset();
+ if (!name.isEmpty() && m_colorThemes.contains(name)) {
+ m_ui.m_colorThemeView->setEnabled(true);
+ QVariantMap colordata = m_colorThemes[name].toMap();
+ foreach (const QString &index, colordata.keys())
+ m_ui.m_colorThemeView->setColor(index.toInt(), QColor(colordata[index].toString()));
+ } else {
+ m_ui.m_colorThemeView->setEnabled(false);
+ }
+}
+
+void ColorSettings::createTheme()
+{
+ QString name = QInputDialog::getText(this, tr("Create New Color Theme"), tr("Theme ID"));
+ if (!name.isEmpty()) {
+ if (m_colorThemes.keys().contains(name)) {
+ QMessageBox::warning(this, tr("Cannot Create Theme"), tr("Theme %1 is already available.").arg(name));
+ } else {
+ m_ui.m_colorThemeView->reset();
+ m_colorThemes[name] = QVariantMap();
+ m_ui.m_comboColorThemes->addItem(name);
+ m_ui.m_comboColorThemes->setCurrentText(name);
+ }
+ }
+}
+
+void ColorSettings::removeTheme()
+{
+ const QString name = m_ui.m_comboColorThemes->currentText();
+ const QMessageBox::StandardButton result = QMessageBox::question(this, tr("Remove Color Theme"),
+ tr("Are you sure you want to delete color theme %1?").arg(name),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+ if (result == QMessageBox::Yes) {
+ m_ui.m_comboColorThemes->removeItem(m_ui.m_comboColorThemes->currentIndex());
+ m_colorThemes.remove(name);
+ m_ui.m_comboColorThemes->setCurrentIndex(0);
+ if (m_colorThemes.isEmpty())
+ m_ui.m_colorThemeView->setEnabled(false);
+ }
+}
diff --git a/src/plugins/scxmleditor/common/colorsettings.h b/src/plugins/scxmleditor/common/colorsettings.h
new file mode 100644
index 00000000000..5e56c505735
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorsettings.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_colorsettings.h"
+#include <QFrame>
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+class ColorSettings : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit ColorSettings(QWidget *parent = nullptr);
+
+ void save();
+ void updateCurrentColors();
+ void selectTheme(const QString &name);
+ void createTheme();
+ void removeTheme();
+
+private:
+ QVariantMap m_colorThemes;
+ Ui::ColorSettings m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/colorsettings.ui b/src/plugins/scxmleditor/common/colorsettings.ui
new file mode 100644
index 00000000000..f854992accc
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorsettings.ui
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::ColorSettings</class>
+ <widget class="QWidget" name="ScxmlEditor::Common::ColorSettings">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>330</width>
+ <height>306</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QComboBox" name="m_comboColorThemes"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_addColorTheme">
+ <property name="text">
+ <string>+</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_removeColorTheme">
+ <property name="text">
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="ScxmlEditor::Common::ColorThemeView" name="m_colorThemeView">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ColorThemeView</class>
+ <extends>QFrame</extends>
+ <header>colorthemeview.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/colorthemedialog.cpp b/src/plugins/scxmleditor/common/colorthemedialog.cpp
new file mode 100644
index 00000000000..539a23daf87
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorthemedialog.cpp
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "colorthemedialog.h"
+
+using namespace ScxmlEditor::Common;
+
+ColorThemeDialog::ColorThemeDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ m_ui.setupUi(this);
+
+ connect(m_ui.m_btnOk, &QPushButton::clicked, this, &ColorThemeDialog::accept);
+ connect(m_ui.m_btnCancel, &QPushButton::clicked, this, &ColorThemeDialog::reject);
+ connect(m_ui.m_btnApply, &QPushButton::clicked, this, &ColorThemeDialog::save);
+}
+
+void ColorThemeDialog::save()
+{
+ m_ui.m_colorSettings->save();
+}
diff --git a/src/plugins/scxmleditor/common/colorthemedialog.h b/src/plugins/scxmleditor/common/colorthemedialog.h
new file mode 100644
index 00000000000..6e160b16fc1
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorthemedialog.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_colorthemedialog.h"
+#include <QDialog>
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+class ColorThemeDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ColorThemeDialog(QWidget *parent = nullptr);
+
+ void save();
+
+private:
+ Ui::ColorThemeDialog m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/colorthemedialog.ui b/src/plugins/scxmleditor/common/colorthemedialog.ui
new file mode 100644
index 00000000000..6b0de010348
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorthemedialog.ui
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::ColorThemeDialog</class>
+ <widget class="QDialog" name="ScxmlEditor::Common::ColorThemeDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="ScxmlEditor::Common::ColorSettings" name="m_colorSettings">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_btnOk">
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_btnCancel">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_btnApply">
+ <property name="text">
+ <string>Apply</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ColorSettings</class>
+ <extends>QFrame</extends>
+ <header>colorsettings.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/colorthemes.cpp b/src/plugins/scxmleditor/common/colorthemes.cpp
new file mode 100644
index 00000000000..821e98e123b
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorthemes.cpp
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "colorthemes.h"
+#include "colorthemedialog.h"
+#include "colorthemeview.h"
+#include "scxmldocument.h"
+#include "scxmleditorconstants.h"
+#include "scxmltag.h"
+
+#include <QMenu>
+
+#include <coreplugin/icore.h>
+
+using namespace ScxmlEditor::Common;
+
+ColorThemes::ColorThemes(QObject *parent)
+ : QObject(parent)
+{
+ m_modifyAction = new QAction(QIcon(":/scxmleditor/images/colorthemes.png"), tr("Modify Color Themes..."), this);
+ m_modifyAction->setToolTip(tr("Modify Color Theme"));
+
+ m_toolButton = new QToolButton;
+ m_toolButton->setIcon(QIcon(":/scxmleditor/images/colorthemes.png"));
+ m_toolButton->setToolTip(tr("Select Color Theme"));
+ m_toolButton->setPopupMode(QToolButton::InstantPopup);
+
+ m_menu = new QMenu;
+
+ connect(m_modifyAction, &QAction::triggered, this, &ColorThemes::showDialog);
+
+ updateColorThemeMenu();
+}
+
+ColorThemes::~ColorThemes()
+{
+ delete m_toolButton;
+ delete m_menu;
+}
+
+QAction *ColorThemes::modifyThemeAction() const
+{
+ return m_modifyAction;
+}
+
+QToolButton *ColorThemes::themeToolButton() const
+{
+ return m_toolButton;
+}
+
+QMenu *ColorThemes::themeMenu() const
+{
+ return m_menu;
+}
+
+void ColorThemes::showDialog()
+{
+ ColorThemeDialog dialog;
+ if (dialog.exec() == QDialog::Accepted) {
+ dialog.save();
+ updateColorThemeMenu();
+ }
+}
+
+void ColorThemes::updateColorThemeMenu()
+{
+ m_menu->clear();
+
+ const QSettings *s = Core::ICore::settings();
+ const QString currentTheme = s->value(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME, Constants::C_COLOR_SCHEME_DEFAULT).toString();
+ const QVariantMap data = s->value(Constants::C_SETTINGS_COLORSETTINGS_COLORTHEMES).toMap();
+ QStringList keys = data.keys();
+ keys.append(Constants::C_COLOR_SCHEME_SCXMLDOCUMENT);
+ keys.append(Constants::C_COLOR_SCHEME_DEFAULT);
+
+ for (const QString &key: keys) {
+ const QString actionText = key == Constants::C_COLOR_SCHEME_DEFAULT
+ ? tr("Factory Default") : key == Constants::C_COLOR_SCHEME_SCXMLDOCUMENT
+ ? tr("Colors from SCXML-document")
+ : key;
+ QAction *action = m_menu->addAction(actionText, this, [this, key]() {
+ selectColorTheme(key);
+ });
+ action->setData(key);
+ action->setCheckable(true);
+ }
+
+ m_menu->addSeparator();
+ m_menu->addAction(m_modifyAction);
+ m_toolButton->setMenu(m_menu);
+
+ selectColorTheme(currentTheme);
+}
+
+void ColorThemes::selectColorTheme(const QString &name)
+{
+ QVariantMap colorData;
+ if (m_document && !name.isEmpty()) {
+ QSettings *s = Core::ICore::settings();
+
+ if (name == Constants::C_COLOR_SCHEME_SCXMLDOCUMENT) {
+ colorData = m_documentColors;
+ s->setValue(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME, name);
+ } else if (name == Constants::C_COLOR_SCHEME_DEFAULT) {
+ colorData = QVariantMap();
+ s->setValue(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME, name);
+ } else {
+ const QVariantMap data = s->value(Constants::C_SETTINGS_COLORSETTINGS_COLORTHEMES).toMap();
+ if (data.contains(name)) {
+ colorData = data[name].toMap();
+ s->setValue(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME, name);
+ }
+ }
+ }
+
+ QList<QAction*> actions = m_menu->actions();
+ for (int i = 0; i < actions.count(); ++i)
+ actions[i]->setChecked(actions[i]->data().toString() == name);
+
+ setCurrentColors(colorData);
+}
+
+void ColorThemes::setDocument(ScxmlEditor::PluginInterface::ScxmlDocument *doc)
+{
+ m_document = doc;
+
+ // Get document colors
+ QVariantMap documentColors;
+ if (m_document) {
+ PluginInterface::ScxmlTag *scxmlTag = m_document->scxmlRootTag();
+ if (scxmlTag && scxmlTag->hasEditorInfo(Constants::C_SCXML_EDITORINFO_COLORS)) {
+ const QStringList colors = scxmlTag->editorInfo(Constants::C_SCXML_EDITORINFO_COLORS).split(";;", QString::SkipEmptyParts);
+ for (const QString &color : colors) {
+ const QStringList colorInfo = color.split("_", QString::SkipEmptyParts);
+ if (colorInfo.count() == 2)
+ documentColors[colorInfo[0]] = colorInfo[1];
+ }
+ }
+ }
+
+ m_documentColors = documentColors;
+ if (m_documentColors.isEmpty())
+ updateColorThemeMenu();
+ else
+ selectColorTheme(Constants::C_COLOR_SCHEME_SCXMLDOCUMENT);
+}
+
+void ColorThemes::setCurrentColors(const QVariantMap &colorData)
+{
+ if (!m_document)
+ return;
+
+ QStringList serializedColors;
+
+ QVector<QColor> colors = ColorThemeView::defaultColors();
+ for (QVariantMap::const_iterator i = colorData.begin(); i != colorData.end(); ++i) {
+ const int k = i.key().toInt();
+ if (k >= 0 && k < colors.count()) {
+ colors[k] = QColor(i.value().toString());
+ serializedColors << QString::fromLatin1("%1_%2").arg(k).arg(colors[k].name());
+ }
+ }
+
+ m_document->setLevelColors(colors);
+ m_document->setEditorInfo(m_document->scxmlRootTag(), Constants::C_SCXML_EDITORINFO_COLORS, serializedColors.join(";;"));
+}
diff --git a/src/plugins/scxmleditor/common/colorthemes.h b/src/plugins/scxmleditor/common/colorthemes.h
new file mode 100644
index 00000000000..fbd777d04a1
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorthemes.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QColor>
+#include <QObject>
+#include <QPointer>
+#include <QVariantMap>
+#include <QVector>
+
+QT_FORWARD_DECLARE_CLASS(QAction)
+QT_FORWARD_DECLARE_CLASS(QMenu)
+QT_FORWARD_DECLARE_CLASS(QToolButton)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface { class ScxmlDocument; }
+
+namespace Common {
+
+class ColorThemes : public QObject
+{
+ Q_OBJECT
+
+public:
+ ColorThemes(QObject *parent = nullptr);
+ ~ColorThemes() override;
+
+ QAction *modifyThemeAction() const;
+ QToolButton *themeToolButton() const;
+ QMenu *themeMenu() const;
+
+ void showDialog();
+ void setDocument(PluginInterface::ScxmlDocument *doc);
+
+private:
+ void updateColorThemeMenu();
+ void selectColorTheme(const QString &name);
+ void setCurrentColors(const QVariantMap &colorData);
+
+ QString m_currentTheme;
+ QAction *m_modifyAction = nullptr;
+ QToolButton *m_toolButton = nullptr;
+ QMenu *m_menu = nullptr;
+ QPointer<PluginInterface::ScxmlDocument> m_document;
+ QVariantMap m_documentColors;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/colorthemeview.cpp b/src/plugins/scxmleditor/common/colorthemeview.cpp
new file mode 100644
index 00000000000..729da31fe12
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorthemeview.cpp
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "colorthemeview.h"
+
+#include <QBrush>
+#include <QColorDialog>
+#include <QLinearGradient>
+#include <QPainter>
+#include <QVariantMap>
+
+using namespace ScxmlEditor::Common;
+
+ColorThemeItem::ColorThemeItem(int index, const QColor &color, QWidget *parent)
+ : QFrame(parent)
+ , m_index(index)
+ , m_color(color)
+{
+ m_pen = QPen(Qt::black);
+ m_pen.setCosmetic(true);
+ connect(this, &ColorThemeItem::mousePressed, this, &ColorThemeItem::openColorDialog);
+}
+
+void ColorThemeItem::setColor(const QColor &color)
+{
+ m_color = color;
+ update();
+}
+
+QColor ColorThemeItem::color() const
+{
+ return m_color;
+}
+
+void ColorThemeItem::openColorDialog()
+{
+ QColor oldColor = m_color;
+
+ QColorDialog dialog(oldColor, 0);
+ dialog.setWindowTitle(tr("Pick Color"));
+ connect(&dialog, &QColorDialog::currentColorChanged, this, &ColorThemeItem::setColor);
+ QPoint topRight = parentWidget()->mapToGlobal(parentWidget()->rect().topRight());
+ dialog.move(topRight.x(), topRight.y());
+ if (dialog.exec() == QDialog::Accepted) {
+ setColor(dialog.currentColor());
+ emit colorChanged();
+ } else {
+ setColor(oldColor);
+ }
+}
+
+void ColorThemeItem::enterEvent(QEvent *e)
+{
+ m_pen.setWidth(isEnabled() ? 3 : 1);
+
+ update();
+ QFrame::enterEvent(e);
+}
+
+void ColorThemeItem::leaveEvent(QEvent *e)
+{
+ m_pen.setWidth(1);
+ update();
+ QFrame::leaveEvent(e);
+}
+
+void ColorThemeItem::mousePressEvent(QMouseEvent *e)
+{
+ QFrame::mousePressEvent(e);
+ emit mousePressed();
+}
+
+void ColorThemeItem::paintEvent(QPaintEvent *e)
+{
+ QFrame::paintEvent(e);
+
+ QPainter p(this);
+ p.setRenderHint(QPainter::Antialiasing, true);
+
+ QRectF r = QRectF(rect()).adjusted(1.5, 1.5, -1.5, -1.5);
+ QLinearGradient grad(r.topLeft(), r.bottomLeft());
+ grad.setColorAt(0, m_color.lighter(115));
+ grad.setColorAt(1, m_color);
+ p.setBrush(QBrush(grad));
+ p.setPen(m_pen);
+
+ qreal radius = r.width() * 0.1;
+ p.drawRoundedRect(r, radius, radius);
+}
+
+ColorThemeView::ColorThemeView(QWidget *parent)
+ : QFrame(parent)
+{
+ for (int i = 0; i < defaultColors().count(); ++i) {
+ m_themeItems << createItem(i, defaultColors()[i]);
+ connect(m_themeItems[i], &ColorThemeItem::colorChanged, this, &ColorThemeView::colorChanged);
+ }
+}
+
+void ColorThemeView::reset()
+{
+ for (int i = 0; i < m_themeItems.count(); ++i)
+ m_themeItems[i]->setColor(defaultColors()[i]);
+}
+
+void ColorThemeView::setColor(int index, const QColor &color)
+{
+ if (index >= 0 && index < m_themeItems.count())
+ m_themeItems[index]->setColor(color);
+}
+
+QColor ColorThemeView::color(int index) const
+{
+ if (index >= 0 && index < m_themeItems.count())
+ return m_themeItems[index]->color();
+
+ return QColor();
+}
+
+QVariantMap ColorThemeView::colorData() const
+{
+ QVariantMap data;
+ for (int i = 0; i < m_themeItems.count(); ++i) {
+ if (m_themeItems[i]->color() != defaultColors()[i])
+ data[QString::fromLatin1("%1").arg(i)] = m_themeItems[i]->color().name();
+ }
+
+ return data;
+}
+
+ColorThemeItem *ColorThemeView::createItem(int index, const QColor &color)
+{
+ return new ColorThemeItem(index, color, this);
+}
+
+const QVector<QColor> &ColorThemeView::defaultColors()
+{
+ static const QVector<QColor> colors = {
+ QColor(0xe0, 0xe0, 0xe0),
+ QColor(0xd3, 0xe4, 0xc3),
+ QColor(0xeb, 0xe4, 0xba),
+ QColor(0xb8, 0xdd, 0xeb),
+ QColor(0xc7, 0xc8, 0xdd),
+ QColor(0xf0, 0xce, 0xa5),
+ QColor(0xf1, 0xba, 0xba)
+ };
+ return colors;
+}
+
+void ColorThemeView::resizeEvent(QResizeEvent *e)
+{
+ QFrame::resizeEvent(e);
+ updateItemRects();
+}
+
+void ColorThemeView::updateItemRects()
+{
+ int size = qMin(width() / 2, height() / 2);
+
+ int capx = size / defaultColors().count();
+ int capy = capx;
+
+ for (int i = 0; i < m_themeItems.count(); ++i) {
+ m_themeItems[i]->resize(size, size);
+ m_themeItems[i]->move(QPoint(capx * (i + 1), capy * (i + 1)));
+ }
+}
diff --git a/src/plugins/scxmleditor/common/colorthemeview.h b/src/plugins/scxmleditor/common/colorthemeview.h
new file mode 100644
index 00000000000..56e7002fa2f
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colorthemeview.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QFrame>
+#include <QPen>
+
+QT_FORWARD_DECLARE_CLASS(QMouseEvent)
+QT_FORWARD_DECLARE_CLASS(QPaintEvent)
+QT_FORWARD_DECLARE_CLASS(QResizeEvent)
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+class ColorThemeItem : public QFrame
+{
+ Q_OBJECT
+
+public:
+ ColorThemeItem(int index, const QColor &color, QWidget *parent = nullptr);
+
+ void setIndex(int index);
+ QColor color() const;
+ void setColor(const QColor &color);
+
+signals:
+ void mousePressed();
+ void colorChanged();
+
+protected:
+ void paintEvent(QPaintEvent *e) override;
+ void enterEvent(QEvent *e) override;
+ void leaveEvent(QEvent *e) override;
+ void mousePressEvent(QMouseEvent *e) override;
+
+private:
+ void openColorDialog();
+
+ int m_index;
+ QColor m_color;
+ QPen m_pen;
+};
+
+class ColorThemeView : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit ColorThemeView(QWidget *parent = nullptr);
+
+ void reset();
+ void setColor(int index, const QColor &color);
+ QColor color(int index) const;
+ QVariantMap colorData() const;
+ static const QVector<QColor> &defaultColors();
+
+signals:
+ void colorChanged();
+
+protected:
+ void resizeEvent(QResizeEvent *e) override;
+
+private:
+ void updateItemRects();
+ ColorThemeItem *createItem(int index, const QColor &color);
+
+ QVector<ColorThemeItem*> m_themeItems;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/colortoolbutton.cpp b/src/plugins/scxmleditor/common/colortoolbutton.cpp
new file mode 100644
index 00000000000..8664f3d8646
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colortoolbutton.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "colortoolbutton.h"
+#include "colorpicker.h"
+#include <QColorDialog>
+#include <QPainter>
+
+using namespace ScxmlEditor::Common;
+
+ColorPickerAction::ColorPickerAction(const QString &key, QObject *parent)
+ : QWidgetAction(parent)
+ , m_key(key)
+{
+}
+
+QWidget *ColorPickerAction::createWidget(QWidget *parent)
+{
+ auto picker = new ColorPicker(m_key, parent);
+ connect(picker, &ColorPicker::colorSelected, this, &ColorPickerAction::colorSelected);
+ connect(this, &ColorPickerAction::lastUsedColor, picker, &ColorPicker::setLastUsedColor);
+
+ return picker;
+}
+
+ColorToolButton::ColorToolButton(const QString &key, const QString &iconName, const QString &tooltip, QWidget *parent)
+ : QToolButton(parent)
+{
+ setIcon(QIcon(iconName));
+ setToolTip(tooltip);
+ setPopupMode(QToolButton::MenuButtonPopup);
+
+ connect(this, &ColorToolButton::clicked, this, [this]() {
+ setCurrentColor(m_color);
+ });
+
+ QPixmap p(15, 15);
+ p.fill(Qt::black);
+
+ m_colorPickerAction = new ColorPickerAction(key, this);
+ connect(m_colorPickerAction, &ColorPickerAction::colorSelected, this, &ColorToolButton::setCurrentColor);
+ connect(this, &ColorToolButton::colorSelected, m_colorPickerAction, &ColorPickerAction::lastUsedColor);
+
+ m_menu = new QMenu(this);
+ m_menu->addAction(QIcon(p), tr("Automatic Color"), this, &ColorToolButton::autoColorSelected);
+ m_menu->addSeparator();
+ m_menu->addAction(m_colorPickerAction);
+ m_menu->addSeparator();
+ m_menu->addAction(QIcon(QPixmap(":/scxmleditor/images/more_colors.png")), tr("More Colors..."), this, &ColorToolButton::showColorDialog);
+ setMenu(m_menu);
+}
+
+ColorToolButton::~ColorToolButton()
+{
+ m_menu->deleteLater();
+}
+
+void ColorToolButton::autoColorSelected()
+{
+ setCurrentColor(QString());
+}
+
+void ColorToolButton::showColorDialog()
+{
+ QColor c = QColorDialog::getColor();
+ if (c.isValid())
+ setCurrentColor(c.name());
+}
+
+void ColorToolButton::setCurrentColor(const QString &currentColor)
+{
+ menu()->hide();
+ m_color = currentColor;
+ emit colorSelected(m_color);
+ update();
+}
+
+void ColorToolButton::paintEvent(QPaintEvent *e)
+{
+ QToolButton::paintEvent(e);
+
+ QPainter p(this);
+ QRect r(2, height() - 7, width() - 17, 4);
+ p.fillRect(r, QBrush(QColor(m_color)));
+
+ if (!isEnabled()) {
+ QColor c(Qt::gray);
+ c.setAlpha(200);
+ p.fillRect(r, QBrush(c));
+ }
+}
diff --git a/src/plugins/scxmleditor/common/colortoolbutton.h b/src/plugins/scxmleditor/common/colortoolbutton.h
new file mode 100644
index 00000000000..44185e5d191
--- /dev/null
+++ b/src/plugins/scxmleditor/common/colortoolbutton.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QMenu>
+#include <QToolButton>
+#include <QWidgetAction>
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+class ColorPickerAction : public QWidgetAction
+{
+ Q_OBJECT
+
+public:
+ ColorPickerAction(const QString &key, QObject *parent);
+
+protected:
+ QWidget *createWidget(QWidget *parent) override;
+
+signals:
+ void colorSelected(const QString &colorName);
+ void lastUsedColor(const QString &colorName);
+
+private:
+ QString m_key;
+};
+
+class ColorToolButton : public QToolButton
+{
+ Q_OBJECT
+
+public:
+ ColorToolButton(const QString &key, const QString &iconName, const QString &tooltip, QWidget *parent = nullptr);
+ ~ColorToolButton() override;
+
+protected:
+ void paintEvent(QPaintEvent *e) override;
+
+signals:
+ void colorSelected(const QString &colorName);
+
+private:
+ void autoColorSelected();
+ void showColorDialog();
+ void setCurrentColor(const QString &currentColor);
+
+ ColorPickerAction *m_colorPickerAction;
+ QString m_color;
+ QMenu *m_menu;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/common.pri b/src/plugins/scxmleditor/common/common.pri
new file mode 100644
index 00000000000..eb4bf8d90e4
--- /dev/null
+++ b/src/plugins/scxmleditor/common/common.pri
@@ -0,0 +1,77 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/colorpicker.h \
+ $$PWD/colorsettings.h \
+ $$PWD/colorthemedialog.h \
+ $$PWD/colorthemes.h \
+ $$PWD/colorthemeview.h \
+ $$PWD/colortoolbutton.h \
+ $$PWD/dragshapebutton.h \
+ $$PWD/graphicsview.h \
+ $$PWD/magnifier.h \
+ $$PWD/mainwidget.h \
+ $$PWD/movableframe.h \
+ $$PWD/navigator.h \
+ $$PWD/navigatorgraphicsview.h \
+ $$PWD/navigatorslider.h \
+ $$PWD/search.h \
+ $$PWD/searchmodel.h \
+ $$PWD/shapegroupwidget.h \
+ $$PWD/shapestoolbox.h \
+ $$PWD/sizegrip.h \
+ $$PWD/stateproperties.h \
+ $$PWD/stateview.h \
+ $$PWD/statistics.h \
+ $$PWD/statisticsdialog.h \
+ $$PWD/structure.h \
+ $$PWD/structuremodel.h \
+ $$PWD/treeview.h
+
+SOURCES += \
+ $$PWD/colorpicker.cpp \
+ $$PWD/colorsettings.cpp \
+ $$PWD/colorthemedialog.cpp \
+ $$PWD/colorthemes.cpp \
+ $$PWD/colorthemeview.cpp \
+ $$PWD/colortoolbutton.cpp \
+ $$PWD/dragshapebutton.cpp \
+ $$PWD/graphicsview.cpp \
+ $$PWD/magnifier.cpp \
+ $$PWD/mainwidget.cpp \
+ $$PWD/movableframe.cpp \
+ $$PWD/navigator.cpp \
+ $$PWD/navigatorgraphicsview.cpp \
+ $$PWD/navigatorslider.cpp \
+ $$PWD/search.cpp \
+ $$PWD/searchmodel.cpp \
+ $$PWD/shapegroupwidget.cpp \
+ $$PWD/shapestoolbox.cpp \
+ $$PWD/sizegrip.cpp \
+ $$PWD/stateproperties.cpp \
+ $$PWD/stateview.cpp \
+ $$PWD/statistics.cpp \
+ $$PWD/statisticsdialog.cpp \
+ $$PWD/structure.cpp \
+ $$PWD/structuremodel.cpp \
+ $$PWD/treeview.cpp
+
+FORMS += \
+ $$PWD/colorpicker.ui \
+ $$PWD/colorsettings.ui \
+ $$PWD/colorthemedialog.ui \
+ $$PWD/magnifier.ui \
+ $$PWD/mainwidget.ui \
+ $$PWD/navigator.ui \
+ $$PWD/navigatorslider.ui \
+ $$PWD/search.ui \
+ $$PWD/shapegroupwidget.ui \
+ $$PWD/shapestoolbox.ui \
+ $$PWD/stateproperties.ui \
+ $$PWD/stateview.ui \
+ $$PWD/statistics.ui \
+ $$PWD/statisticsdialog.ui \
+ $$PWD/structure.ui
+
+RESOURCES += \
+ $$PWD/common.qrc
diff --git a/src/plugins/scxmleditor/common/common.qrc b/src/plugins/scxmleditor/common/common.qrc
new file mode 100644
index 00000000000..345d2aab57b
--- /dev/null
+++ b/src/plugins/scxmleditor/common/common.qrc
@@ -0,0 +1,45 @@
+<RCC>
+ <qresource prefix="/scxmleditor">
+ <file>images/adjust_height.png</file>
+ <file>images/adjust_size.png</file>
+ <file>images/adjust_width.png</file>
+ <file>images/align_bottom.png</file>
+ <file>images/align_horizontal.png</file>
+ <file>images/align_left.png</file>
+ <file>images/align_right.png</file>
+ <file>images/align_top.png</file>
+ <file>images/align_vertical.png</file>
+ <file>images/arrow_down.png</file>
+ <file>images/arrow_right.png</file>
+ <file>images/colorthemes.png</file>
+ <file>images/final.png</file>
+ <file>images/font_color.png</file>
+ <file>images/fullnamespace.png</file>
+ <file>images/history.png</file>
+ <file>images/icon-close.png</file>
+ <file>images/icon-copy.png</file>
+ <file>images/icon-cut.png</file>
+ <file>images/icon-export-canvas.png</file>
+ <file>images/icon-filter.png</file>
+ <file>images/icon-fit-screen.png</file>
+ <file>images/icon-pan.png</file>
+ <file>images/icon-paste.png</file>
+ <file>images/icon-redo.png</file>
+ <file>images/icon-search.png</file>
+ <file>images/icon-undo.png</file>
+ <file>images/icon-zoom-in.png</file>
+ <file>images/icon-zoom-out.png</file>
+ <file>images/initial.png</file>
+ <file>images/more_colors.png</file>
+ <file>images/navigator.png</file>
+ <file>images/parallel.png</file>
+ <file>images/parallel_icon.png</file>
+ <file>images/properties.png</file>
+ <file>images/screenshot.png</file>
+ <file>images/search.png</file>
+ <file>images/state.png</file>
+ <file>images/state_color.png</file>
+ <file>images/statistics.png</file>
+ <file>images/structure.png</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/scxmleditor/common/dragshapebutton.cpp b/src/plugins/scxmleditor/common/dragshapebutton.cpp
new file mode 100644
index 00000000000..5b5d91dce8b
--- /dev/null
+++ b/src/plugins/scxmleditor/common/dragshapebutton.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "dragshapebutton.h"
+#include "baseitem.h"
+#include <QDrag>
+#include <QGuiApplication>
+#include <QMimeData>
+#include <QMouseEvent>
+
+using namespace ScxmlEditor::Common;
+
+DragShapeButton::DragShapeButton(QWidget *parent)
+ : QToolButton(parent)
+{
+ setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+ setMinimumSize(75, 75);
+ setMaximumSize(75, 75);
+ setIconSize(QSize(45, 45));
+ QFont f = font();
+ f.setPointSize(8);
+ setFont(f);
+}
+
+void DragShapeButton::setShapeInfo(int groupIndex, int shapeIndex)
+{
+ m_groupIndex = groupIndex;
+ m_shapeIndex = shapeIndex;
+}
+
+void DragShapeButton::mousePressEvent(QMouseEvent *event)
+{
+ if (!(event->buttons() & Qt::LeftButton))
+ return;
+
+ auto drag = new QDrag(this);
+ auto mimeData = new QMimeData;
+ mimeData->setData("dragType", "Shape");
+ mimeData->setData("groupIndex", QString::number(m_groupIndex).toLatin1());
+ mimeData->setData("shapeIndex", QString::number(m_shapeIndex).toLatin1());
+ drag->setMimeData(mimeData);
+ drag->setPixmap(icon().pixmap(iconSize()));
+
+ drag->exec();
+}
diff --git a/src/plugins/scxmleditor/common/dragshapebutton.h b/src/plugins/scxmleditor/common/dragshapebutton.h
new file mode 100644
index 00000000000..979f9222000
--- /dev/null
+++ b/src/plugins/scxmleditor/common/dragshapebutton.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QToolButton>
+
+QT_FORWARD_DECLARE_CLASS(QMouseEvent)
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+class DragShapeButton : public QToolButton
+{
+public:
+ explicit DragShapeButton(QWidget *parent = nullptr);
+
+ void setShapeInfo(int groupIndex, int shapeIndex);
+
+protected:
+ void mousePressEvent(QMouseEvent *e) override;
+
+private:
+ int m_groupIndex = 0;
+ int m_shapeIndex = 0;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/graphicsview.cpp b/src/plugins/scxmleditor/common/graphicsview.cpp
new file mode 100644
index 00000000000..425e4823bfd
--- /dev/null
+++ b/src/plugins/scxmleditor/common/graphicsview.cpp
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "graphicsview.h"
+#include "baseitem.h"
+#include "connectableitem.h"
+#include "graphicsscene.h"
+#include "sceneutils.h"
+#include "scxmluifactory.h"
+#include "shapeprovider.h"
+
+#include <QDebug>
+#include <QMessageBox>
+#include <QMimeData>
+#include <QPainter>
+#include <QScrollBar>
+#include <QWheelEvent>
+
+
+using namespace ScxmlEditor::PluginInterface;
+using namespace ScxmlEditor::Common;
+
+GraphicsView::GraphicsView(QWidget *parent)
+ : QGraphicsView(parent)
+{
+ setTransformationAnchor(AnchorUnderMouse);
+ setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
+ setDragMode(RubberBandDrag);
+ setRubberBandSelectionMode(Qt::ContainsItemShape);
+ setBackgroundBrush(QBrush(QColor(0xef, 0xef, 0xef)));
+ setAcceptDrops(true);
+
+ connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &GraphicsView::updateView);
+ connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &GraphicsView::updateView);
+}
+
+void GraphicsView::initLayoutItem()
+{
+ if (!scene())
+ return;
+
+ QRectF r = rect();
+ if (!m_layoutItem) {
+ m_layoutItem = new LayoutItem(r);
+ scene()->addItem(m_layoutItem);
+ } else {
+ m_layoutItem->setBoundingRect(r);
+ }
+}
+
+void GraphicsView::setGraphicsScene(ScxmlEditor::PluginInterface::GraphicsScene *s)
+{
+ // Disconnect old connections
+ if (scene())
+ scene()->disconnect(this);
+
+ setScene(s);
+
+ if (scene())
+ connect(scene(), &PluginInterface::GraphicsScene::sceneRectChanged, this, &GraphicsView::sceneRectHasChanged);
+
+ initLayoutItem();
+}
+
+void GraphicsView::sceneRectHasChanged(const QRectF &r)
+{
+ m_minZoomValue = qMin(rect().width() / r.width(), rect().height() / r.height());
+ updateView();
+}
+
+void GraphicsView::updateView()
+{
+ emit viewChanged(mapToScene(rect()));
+ emit zoomPercentChanged(qBound(0, int((transform().m11() - m_minZoomValue) / (m_maxZoomValue - m_minZoomValue) * 100), 100));
+ if (auto graphicsScene = qobject_cast<GraphicsScene*>(scene()))
+ graphicsScene->checkItemsVisibility(transform().m11());
+}
+
+void GraphicsView::resizeEvent(QResizeEvent *event)
+{
+ QRect r(QPoint(0, 0), event->size());
+
+ // Init layout item if necessary
+ initLayoutItem();
+ updateView();
+}
+
+void GraphicsView::zoomTo(int value)
+{
+ // Scale to zoom
+ qreal targetScale = m_minZoomValue + (m_maxZoomValue - m_minZoomValue) * ((qreal)value / 100.0);
+ qreal scaleFactor = targetScale / transform().m11();
+ scale(scaleFactor, scaleFactor);
+ if (auto graphicsScene = qobject_cast<GraphicsScene*>(scene()))
+ graphicsScene->checkItemsVisibility(transform().m11());
+}
+
+void GraphicsView::zoomIn()
+{
+ if (transform().m11() < m_maxZoomValue) {
+ scale(1.1, 1.1);
+ updateView();
+ }
+}
+
+void GraphicsView::zoomOut()
+{
+ if (transform().m11() > m_minZoomValue) {
+ scale(1.0 / 1.1, 1.0 / 1.1);
+ updateView();
+ }
+}
+
+void GraphicsView::wheelEvent(QWheelEvent *event)
+{
+ if (Qt::ControlModifier & event->modifiers()) {
+ if (event->delta() > 0)
+ zoomIn();
+ else
+ zoomOut();
+ } else
+ QGraphicsView::wheelEvent(event);
+}
+
+void GraphicsView::setPanning(bool pan)
+{
+ setDragMode(pan ? ScrollHandDrag : RubberBandDrag);
+}
+
+void GraphicsView::magnifierClicked(double zoomLevel, const QPointF &p)
+{
+ emit magnifierChanged(false);
+ qreal scaleFactor = zoomLevel / transform().m11();
+ scale(scaleFactor, scaleFactor);
+ centerOn(p);
+ updateView();
+}
+
+QImage GraphicsView::grabView()
+{
+ return grab(rect().adjusted(0, 0, -10, -10)).toImage();
+}
+
+void GraphicsView::keyReleaseEvent(QKeyEvent *event)
+{
+ emit panningChanged(event->modifiers() == Qt::ShiftModifier);
+ emit magnifierChanged(event->modifiers() == Qt::AltModifier);
+ QGraphicsView::keyReleaseEvent(event);
+}
+
+void GraphicsView::keyPressEvent(QKeyEvent *event)
+{
+ emit panningChanged(event->modifiers() == Qt::ShiftModifier);
+ emit magnifierChanged(event->modifiers() == Qt::AltModifier);
+ QGraphicsView::keyPressEvent(event);
+}
+
+void GraphicsView::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (event->mimeData()->data("dragType") == "Shape")
+ event->accept();
+ else
+ event->ignore();
+}
+
+void GraphicsView::dragMoveEvent(QDragMoveEvent *event)
+{
+ if (m_shapeProvider && m_document && event->mimeData()->data("dragType") == "Shape") {
+
+ int groupIndex = event->mimeData()->data("groupIndex").toInt();
+ int shapeIndex = event->mimeData()->data("shapeIndex").toInt();
+
+ ScxmlTag *targetTag = nullptr;
+
+ QList<QGraphicsItem*> parentItems = items(event->pos());
+ QPointF sceneP = mapToScene(event->pos());
+ for (int i = 0; i < parentItems.count(); ++i) {
+ auto item = static_cast<BaseItem*>(parentItems[i]);
+ if (item && item->type() >= TransitionType && item->containsScenePoint(sceneP)) {
+ targetTag = item->tag();
+ break;
+ }
+ }
+
+ if (!targetTag)
+ targetTag = m_document->rootTag();
+
+ if (m_shapeProvider->canDrop(groupIndex, shapeIndex, targetTag))
+ event->accept();
+ else
+ event->ignore();
+ } else {
+ event->ignore();
+ }
+}
+
+void GraphicsView::dropEvent(QDropEvent *event)
+{
+ if (m_shapeProvider && m_document && event->mimeData()->data("dragType") == "Shape") {
+ event->accept();
+
+ int groupIndex = event->mimeData()->data("groupIndex").toInt();
+ int shapeIndex = event->mimeData()->data("shapeIndex").toInt();
+
+ ScxmlTag *targetTag = nullptr;
+ QPointF targetPos = mapToScene(event->pos());
+
+ QList<QGraphicsItem*> parentItems = items(event->pos());
+ for (int i = 0; i < parentItems.count(); ++i) {
+ BaseItem *item = static_cast<BaseItem*>(parentItems[i]);
+ if (item && item->type() >= StateType) {
+ targetPos = item->mapFromScene(targetPos);
+ targetTag = item->tag();
+ break;
+ }
+ }
+
+ if (!targetTag)
+ targetTag = m_document->rootTag();
+
+ if (m_shapeProvider->canDrop(groupIndex, shapeIndex, targetTag)) {
+ if (auto graphicsScene = qobject_cast<GraphicsScene*>(scene()))
+ graphicsScene->unselectAll();
+ m_document->setCurrentTag(targetTag);
+ QByteArray scxmlData = m_shapeProvider->scxmlCode(groupIndex, shapeIndex, targetTag);
+ if (!scxmlData.isEmpty() && !m_document->pasteData(scxmlData, targetPos, targetPos))
+ QMessageBox::warning(0, tr("SCXML Generation Failed"), m_document->lastError());
+ }
+ } else {
+ event->ignore();
+ }
+}
+
+void GraphicsView::paintEvent(QPaintEvent *event)
+{
+ if (m_drawingEnabled) {
+ QGraphicsView::paintEvent(event);
+ } else {
+ QPainter painter(viewport());
+ painter.save();
+ painter.drawText(rect(), Qt::AlignCenter, tr("Loading document..."));
+ painter.restore();
+ }
+}
+
+void GraphicsView::setDrawingEnabled(bool enabled)
+{
+ setHorizontalScrollBarPolicy(enabled ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(enabled ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
+ m_drawingEnabled = enabled;
+}
+
+void GraphicsView::setUiFactory(ScxmlUiFactory *factory)
+{
+ if (factory)
+ m_shapeProvider = static_cast<ShapeProvider*>(factory->object("shapeProvider"));
+}
+
+void GraphicsView::setDocument(ScxmlDocument *document)
+{
+ m_document = document;
+}
+
+void GraphicsView::fitSceneToView()
+{
+ if (!scene())
+ return;
+
+ fitInView(scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
+ updateView();
+}
+
+void GraphicsView::zoomToItem(QGraphicsItem *item)
+{
+ if (!item)
+ return;
+
+ qreal scaleFactor = 1.0 / transform().m11();
+ scale(scaleFactor, scaleFactor);
+ centerOn(item);
+ updateView();
+}
+
+void GraphicsView::centerToItem(QGraphicsItem *item)
+{
+ centerOn(item);
+ updateView();
+}
+
+void GraphicsView::moveToPoint(const QPointF &p)
+{
+ centerOn(p);
+ updateView();
+}
+
+double GraphicsView::minZoomValue() const
+{
+ return m_minZoomValue;
+}
+
+double GraphicsView::maxZoomValue() const
+{
+ return m_maxZoomValue;
+}
diff --git a/src/plugins/scxmleditor/common/graphicsview.h b/src/plugins/scxmleditor/common/graphicsview.h
new file mode 100644
index 00000000000..5dd39cd4b73
--- /dev/null
+++ b/src/plugins/scxmleditor/common/graphicsview.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "layoutitem.h"
+
+#include <QGraphicsView>
+#include <QPointer>
+
+QT_FORWARD_DECLARE_CLASS(QImage)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+class ShapeProvider;
+class GraphicsScene;
+class ScxmlUiFactory;
+class ScxmlDocument;
+} // namespace PluginInterface
+
+namespace Common {
+
+class GraphicsView : public QGraphicsView
+{
+ Q_OBJECT
+
+public:
+ explicit GraphicsView(QWidget *parent = nullptr);
+
+ void setGraphicsScene(PluginInterface::GraphicsScene *scene);
+
+ double minZoomValue() const;
+ double maxZoomValue() const;
+ void setDrawingEnabled(bool enabled);
+ void setUiFactory(PluginInterface::ScxmlUiFactory *factory);
+ void setDocument(PluginInterface::ScxmlDocument *document);
+ void moveToPoint(const QPointF &p);
+ void fitSceneToView();
+ void zoomIn();
+ void zoomOut();
+ void zoomTo(int value);
+ void zoomToItem(QGraphicsItem *item);
+ void setPanning(bool pan);
+ void magnifierClicked(double zoomLevel, const QPointF &p);
+ QImage grabView();
+
+signals:
+ void viewChanged(const QPolygonF &mainView);
+ void zoomPercentChanged(int value);
+ void panningChanged(bool panning);
+ void magnifierChanged(bool magnifier);
+
+protected:
+ void wheelEvent(QWheelEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void dragMoveEvent(QDragMoveEvent *event) override;
+ void dropEvent(QDropEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ void centerToItem(QGraphicsItem *item);
+ void sceneRectHasChanged(const QRectF &r);
+ void updateView();
+ void initLayoutItem();
+
+ bool m_drawingEnabled = true;
+ double m_minZoomValue = 0.1;
+ double m_maxZoomValue = 1.5;
+ PluginInterface::ShapeProvider *m_shapeProvider = nullptr;
+ QPointer<PluginInterface::ScxmlDocument> m_document;
+ QPointer<PluginInterface::LayoutItem> m_layoutItem;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/images/adjust_height.png b/src/plugins/scxmleditor/common/images/adjust_height.png
new file mode 100644
index 00000000000..3a838838519
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/adjust_height.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/adjust_size.png b/src/plugins/scxmleditor/common/images/adjust_size.png
new file mode 100644
index 00000000000..b95d8737a85
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/adjust_size.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/adjust_width.png b/src/plugins/scxmleditor/common/images/adjust_width.png
new file mode 100644
index 00000000000..89e509878c4
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/adjust_width.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/align_bottom.png b/src/plugins/scxmleditor/common/images/align_bottom.png
new file mode 100644
index 00000000000..f827993a211
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/align_bottom.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/align_horizontal.png b/src/plugins/scxmleditor/common/images/align_horizontal.png
new file mode 100644
index 00000000000..876a38fdf59
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/align_horizontal.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/align_left.png b/src/plugins/scxmleditor/common/images/align_left.png
new file mode 100644
index 00000000000..e015bc93e8f
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/align_left.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/align_right.png b/src/plugins/scxmleditor/common/images/align_right.png
new file mode 100644
index 00000000000..c52bb1498b5
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/align_right.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/align_top.png b/src/plugins/scxmleditor/common/images/align_top.png
new file mode 100644
index 00000000000..0366ae86867
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/align_top.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/align_vertical.png b/src/plugins/scxmleditor/common/images/align_vertical.png
new file mode 100644
index 00000000000..036f8d40b73
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/align_vertical.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/arrow_down.png b/src/plugins/scxmleditor/common/images/arrow_down.png
new file mode 100644
index 00000000000..f94254fa266
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/arrow_down.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/arrow_right.png b/src/plugins/scxmleditor/common/images/arrow_right.png
new file mode 100644
index 00000000000..9e8d3a2a2a7
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/arrow_right.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/colorthemes.png b/src/plugins/scxmleditor/common/images/colorthemes.png
new file mode 100644
index 00000000000..3fc3bc373e7
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/colorthemes.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/final.png b/src/plugins/scxmleditor/common/images/final.png
new file mode 100644
index 00000000000..8c0686f7a74
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/final.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/font_color.png b/src/plugins/scxmleditor/common/images/font_color.png
new file mode 100644
index 00000000000..51298edc174
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/font_color.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/fullnamespace.png b/src/plugins/scxmleditor/common/images/fullnamespace.png
new file mode 100644
index 00000000000..ee890ccf5b6
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/fullnamespace.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/history.png b/src/plugins/scxmleditor/common/images/history.png
new file mode 100644
index 00000000000..1c0bfd75f61
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/history.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-close.png b/src/plugins/scxmleditor/common/images/icon-close.png
new file mode 100644
index 00000000000..b2233f150ec
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-close.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-copy.png b/src/plugins/scxmleditor/common/images/icon-copy.png
new file mode 100644
index 00000000000..7d1e9455bf4
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-copy.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-cut.png b/src/plugins/scxmleditor/common/images/icon-cut.png
new file mode 100644
index 00000000000..640bb76d04c
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-cut.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-export-canvas.png b/src/plugins/scxmleditor/common/images/icon-export-canvas.png
new file mode 100644
index 00000000000..4c0eca55602
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-export-canvas.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-filter.png b/src/plugins/scxmleditor/common/images/icon-filter.png
new file mode 100644
index 00000000000..9ed69c43409
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-filter.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-fit-screen.png b/src/plugins/scxmleditor/common/images/icon-fit-screen.png
new file mode 100644
index 00000000000..d5d5a339563
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-fit-screen.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-pan.png b/src/plugins/scxmleditor/common/images/icon-pan.png
new file mode 100644
index 00000000000..e157880417d
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-pan.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-paste.png b/src/plugins/scxmleditor/common/images/icon-paste.png
new file mode 100644
index 00000000000..e281dbd3f2f
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-paste.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-redo.png b/src/plugins/scxmleditor/common/images/icon-redo.png
new file mode 100644
index 00000000000..c8e81ab96f6
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-redo.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-search.png b/src/plugins/scxmleditor/common/images/icon-search.png
new file mode 100644
index 00000000000..39239be15d7
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-search.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-undo.png b/src/plugins/scxmleditor/common/images/icon-undo.png
new file mode 100644
index 00000000000..aaf47a0b7eb
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-undo.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-zoom-in.png b/src/plugins/scxmleditor/common/images/icon-zoom-in.png
new file mode 100644
index 00000000000..b8679e7ec5d
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-zoom-in.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/icon-zoom-out.png b/src/plugins/scxmleditor/common/images/icon-zoom-out.png
new file mode 100644
index 00000000000..d492a2e8c4a
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/icon-zoom-out.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/initial.png b/src/plugins/scxmleditor/common/images/initial.png
new file mode 100644
index 00000000000..03e8fd6416b
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/initial.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/more_colors.png b/src/plugins/scxmleditor/common/images/more_colors.png
new file mode 100644
index 00000000000..2833e1f03e8
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/more_colors.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/navigator.png b/src/plugins/scxmleditor/common/images/navigator.png
new file mode 100644
index 00000000000..d4629fd8964
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/navigator.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/parallel.png b/src/plugins/scxmleditor/common/images/parallel.png
new file mode 100644
index 00000000000..8b46d99fca0
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/parallel.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/parallel_icon.png b/src/plugins/scxmleditor/common/images/parallel_icon.png
new file mode 100644
index 00000000000..91be7d892a5
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/parallel_icon.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/properties.png b/src/plugins/scxmleditor/common/images/properties.png
new file mode 100644
index 00000000000..c3393520991
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/properties.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/screenshot.png b/src/plugins/scxmleditor/common/images/screenshot.png
new file mode 100644
index 00000000000..1f2378ab38d
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/screenshot.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/search.png b/src/plugins/scxmleditor/common/images/search.png
new file mode 100644
index 00000000000..6b0d4b20ca8
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/search.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/state.png b/src/plugins/scxmleditor/common/images/state.png
new file mode 100644
index 00000000000..ab44fd1a63c
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/state.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/state_color.png b/src/plugins/scxmleditor/common/images/state_color.png
new file mode 100644
index 00000000000..8785c4cb2d2
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/state_color.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/statistics.png b/src/plugins/scxmleditor/common/images/statistics.png
new file mode 100644
index 00000000000..a98210386ff
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/statistics.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/images/structure.png b/src/plugins/scxmleditor/common/images/structure.png
new file mode 100644
index 00000000000..10afed67f43
--- /dev/null
+++ b/src/plugins/scxmleditor/common/images/structure.png
Binary files differ
diff --git a/src/plugins/scxmleditor/common/magnifier.cpp b/src/plugins/scxmleditor/common/magnifier.cpp
new file mode 100644
index 00000000000..e3bed577d49
--- /dev/null
+++ b/src/plugins/scxmleditor/common/magnifier.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "magnifier.h"
+#include "graphicsscene.h"
+#include "graphicsview.h"
+
+#include <QMouseEvent>
+
+using namespace ScxmlEditor::Common;
+
+Magnifier::Magnifier(QWidget *parent)
+ : QWidget(parent)
+{
+ m_ui.setupUi(this);
+ setMouseTracking(true);
+ m_ui.m_graphicsView->setEnabled(false);
+}
+
+void Magnifier::setTopLeft(const QPoint &topLeft)
+{
+ m_topLeft = topLeft;
+}
+
+void Magnifier::resizeEvent(QResizeEvent *e)
+{
+ QWidget::resizeEvent(e);
+
+ qreal radius = width() / 2;
+ m_gradientBrush.setCenter(radius, radius);
+ m_gradientBrush.setFocalPoint(radius, radius);
+ m_gradientBrush.setRadius(radius);
+ m_gradientBrush.setColorAt(1.0, QColor(255, 255, 255, 0));
+ m_gradientBrush.setColorAt(0.0, QColor(0, 0, 0, 255));
+
+ int cap = radius * 0.1;
+ m_ui.m_graphicsView->setMask(QRegion(rect().adjusted(cap, cap, -cap, -cap), QRegion::Ellipse));
+}
+
+void Magnifier::showEvent(QShowEvent *e)
+{
+ QWidget::showEvent(e);
+ grabMouse();
+}
+
+void Magnifier::hideEvent(QHideEvent *e)
+{
+ QWidget::hideEvent(e);
+ releaseMouse();
+}
+
+void Magnifier::mousePressEvent(QMouseEvent *e)
+{
+ QWidget::mousePressEvent(e);
+ if (m_mainView)
+ m_mainView->magnifierClicked(m_ui.m_graphicsView->transform().m11(),
+ m_ui.m_graphicsView->mapToScene(e->pos() - m_topLeft + rect().center()));
+}
+
+void Magnifier::mouseMoveEvent(QMouseEvent *e)
+{
+ QWidget::mouseMoveEvent(e);
+ move(pos() + (e->pos() - rect().center()));
+}
+
+void Magnifier::wheelEvent(QWheelEvent *e)
+{
+ QWidget::wheelEvent(e);
+
+ if (e->delta() > 0)
+ m_ui.m_graphicsView->scale(1.1, 1.1);
+ else
+ m_ui.m_graphicsView->scale(1.0 / 1.1, 1.0 / 1.1);
+
+ if (m_mainView)
+ m_ui.m_graphicsView->centerOn(m_mainView->mapToScene(pos() - m_topLeft + rect().center()));
+}
+
+void Magnifier::moveEvent(QMoveEvent *e)
+{
+ QWidget::moveEvent(e);
+
+ if (m_mainView)
+ m_ui.m_graphicsView->centerOn(m_mainView->mapToScene(e->pos() - m_topLeft + rect().center()));
+}
+
+void Magnifier::setCurrentView(GraphicsView *view)
+{
+ m_mainView = view;
+}
+
+void Magnifier::setCurrentScene(ScxmlEditor::PluginInterface::GraphicsScene *scene)
+{
+ m_ui.m_graphicsView->setScene(scene);
+}
+
+void Magnifier::paintEvent(QPaintEvent *e)
+{
+ QWidget::paintEvent(e);
+
+ QPainter p(this);
+
+ p.setPen(Qt::NoPen);
+ p.setBrush(m_gradientBrush);
+ p.drawRect(rect());
+}
diff --git a/src/plugins/scxmleditor/common/magnifier.h b/src/plugins/scxmleditor/common/magnifier.h
new file mode 100644
index 00000000000..2fa9fd23a32
--- /dev/null
+++ b/src/plugins/scxmleditor/common/magnifier.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QGraphicsView>
+#include <QPointer>
+
+#include "ui_magnifier.h"
+
+QT_FORWARD_DECLARE_CLASS(QMouseEvent)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface { class GraphicsScene; }
+
+namespace Common {
+
+class GraphicsView;
+
+class Magnifier : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Magnifier(QWidget *parent = nullptr);
+
+ void setCurrentView(GraphicsView *mainView);
+ void setCurrentScene(PluginInterface::GraphicsScene *scene);
+ void setTopLeft(const QPoint &topLeft);
+
+signals:
+ void clicked(double zoomLevel);
+
+protected:
+ void resizeEvent(QResizeEvent *e) override;
+ void paintEvent(QPaintEvent *e) override;
+ void showEvent(QShowEvent *e) override;
+ void hideEvent(QHideEvent *e) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void moveEvent(QMoveEvent *e) override;
+ void wheelEvent(QWheelEvent *e) override;
+
+private:
+ QPoint m_topLeft;
+ QPointer<GraphicsView> m_mainView;
+ QRadialGradient m_gradientBrush;
+ Ui::Magnifier m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/magnifier.ui b/src/plugins/scxmleditor/common/magnifier.ui
new file mode 100644
index 00000000000..98f7821ea19
--- /dev/null
+++ b/src/plugins/scxmleditor/common/magnifier.ui
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::Magnifier</class>
+ <widget class="QWidget" name="ScxmlEditor::Common::Magnifier">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGraphicsView" name="m_graphicsView">
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="interactive">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/mainwidget.cpp b/src/plugins/scxmleditor/common/mainwidget.cpp
new file mode 100644
index 00000000000..63d2c0ec184
--- /dev/null
+++ b/src/plugins/scxmleditor/common/mainwidget.cpp
@@ -0,0 +1,789 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "mainwidget.h"
+#include "actionhandler.h"
+#include "colorthemes.h"
+#include "colortoolbutton.h"
+#include "errorwidget.h"
+#include "graphicsscene.h"
+#include "magnifier.h"
+#include "navigator.h"
+#include "scxmltagutils.h"
+#include "scxmleditorconstants.h"
+#include "search.h"
+#include "stateitem.h"
+#include "stateview.h"
+#include "statisticsdialog.h"
+#include "undocommands.h"
+#include "warning.h"
+#include "warningprovider.h"
+
+#include <QAction>
+#include <QClipboard>
+#include <QGuiApplication>
+#include <QMenu>
+#include <QMimeData>
+
+#include <QComboBox>
+#include <QDateTime>
+#include <QDebug>
+#include <QFile>
+#include <QFileDialog>
+#include <QFileInfo>
+#include <QImage>
+#include <QImageWriter>
+#include <QItemEditorFactory>
+#include <QMessageBox>
+#include <QPainter>
+#include <QProgressBar>
+#include <QProgressDialog>
+#include <QStandardPaths>
+#include <QXmlStreamWriter>
+
+#include "scxmluifactory.h"
+
+#include <QCoreApplication>
+#include <app/app_version.h>
+#include <iostream>
+
+#include <coreplugin/icore.h>
+#include <utils/algorithm.h>
+
+using namespace ScxmlEditor::PluginInterface;
+using namespace ScxmlEditor::Common;
+using namespace ScxmlEditor::OutputPane;
+
+void msgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+ QString strOutput;
+ QString prefix;
+ switch (type) {
+ case QtDebugMsg:
+ prefix = "D";
+ break;
+ case QtWarningMsg:
+ prefix = "W";
+ break;
+ case QtCriticalMsg:
+ prefix = "C";
+ break;
+ case QtFatalMsg:
+ prefix = "F";
+ break;
+ default:
+ break;
+ }
+
+ strOutput = QString::fromLatin1("[%1] [%2]: (%3:%4): %5").arg(QDateTime::currentDateTime().toString("yyyy/MM/dd HH:mm:ss")).arg(prefix).arg(QLatin1String(context.file)).arg(context.line).arg(msg);
+ std::cerr << strOutput.toStdString() << std::endl;
+
+ QFile file(QString::fromLatin1("%1/sceditor_log.txt").arg(QCoreApplication::applicationDirPath()));
+ if (!file.open(QIODevice::Append | QIODevice::Text)) {
+ std::cerr << "cannot write file" << std::endl;
+ return;
+ }
+
+ QTextStream out(&file);
+ out << strOutput << "\n";
+ file.close();
+
+ if (type == QtFatalMsg)
+ abort();
+}
+
+MainWidget::MainWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ init();
+ addStateView();
+ m_uiFactory->documentChanged(NewDocument, m_document);
+ documentChanged();
+}
+
+MainWidget::~MainWidget()
+{
+ clear();
+ delete m_document;
+}
+
+QAction *MainWidget::action(ActionType type)
+{
+ if (type == ActionColorTheme)
+ return m_colorThemes->modifyThemeAction();
+
+ if (type >= ActionZoomIn && type < ActionLast)
+ return m_actionHandler->action(type);
+
+ return nullptr;
+}
+
+QToolButton *MainWidget::toolButton(ToolButtonType type)
+{
+ if (type == ToolButtonColorTheme)
+ return m_colorThemes->themeToolButton();
+
+ if (type >= ToolButtonStateColor && type < ToolButtonLast)
+ return m_toolButtons[type];
+
+ return nullptr;
+}
+
+void MainWidget::init()
+{
+ m_ui.setupUi(this);
+
+ m_uiFactory = new ScxmlUiFactory(this);
+ m_ui.m_stateProperties->setUIFactory(m_uiFactory);
+ m_colorThemes = new ColorThemes(this);
+ m_ui.m_shapesFrame->setUIFactory(m_uiFactory);
+
+ m_navigator = new Navigator(this);
+ m_navigator->setVisible(false);
+
+ m_magnifier = new Magnifier(this);
+ m_magnifier->setVisible(false);
+
+ // Create, init and connect Error-pane
+ m_errorPane = new ErrorWidget;
+ m_ui.m_outputPaneWindow->addPane(m_errorPane);
+
+ connect(m_ui.m_outputPaneWindow, &OutputTabWidget::visibilityChanged, this, &MainWidget::handleTabVisibilityChanged);
+
+ // Init warningProvider
+ auto provider = static_cast<WarningProvider*>(m_uiFactory->object(Constants::C_OBJECTNAME_WARNINGPROVIDER));
+ if (provider)
+ provider->init(m_errorPane->warningModel());
+
+ connect(m_errorPane, &ErrorWidget::mouseExited, this, [this]() {
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->unhighlightAll();
+ });
+
+ connect(m_errorPane, &ErrorWidget::warningEntered, [this](Warning *w) {
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->highlightWarningItem(w);
+ });
+
+ connect(m_errorPane, &ErrorWidget::warningSelected, [this](Warning *w) {
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->selectWarningItem(w);
+ });
+
+ connect(m_errorPane, &ErrorWidget::warningDoubleClicked, [this](Warning *w) {
+ StateView *view = m_views.last();
+ if (view)
+ view->view()->zoomToItem(view->scene()->findItem(view->scene()->tagByWarning(w)));
+ });
+
+ // Create and init Error-pane
+ m_searchPane = new Search;
+ m_ui.m_outputPaneWindow->addPane(m_searchPane);
+
+ m_document = new ScxmlDocument;
+ connect(m_document, &ScxmlDocument::endTagChange, this, &MainWidget::endTagChange);
+ connect(m_document, &ScxmlDocument::documentChanged, this, &MainWidget::dirtyChanged);
+
+ connect(m_ui.stackedWidget, &QStackedWidget::currentChanged, this, &MainWidget::initView);
+
+ m_actionHandler = new ActionHandler(this);
+
+ // Connect actions
+ connect(m_actionHandler->action(ActionZoomIn), &QAction::triggered, this, [this]() {
+ StateView *view = m_views.last();
+ if (view)
+ view->view()->zoomIn();
+ });
+ connect(m_actionHandler->action(ActionZoomOut), &QAction::triggered, this, [this]() {
+ StateView *view = m_views.last();
+ if (view)
+ view->view()->zoomOut();
+ });
+ connect(m_actionHandler->action(ActionFitToView), &QAction::triggered, this, &MainWidget::fitToView);
+ connect(m_actionHandler->action(ActionPan), &QAction::toggled, this, [this](bool toggled) {
+ StateView *view = m_views.last();
+ if (view)
+ view->view()->setPanning(toggled);
+ });
+ connect(m_actionHandler->action(ActionMagnifier), &QAction::toggled, this, &MainWidget::setMagnifier);
+
+ connect(m_navigator, &Navigator::hideFrame, this, [this]() { m_actionHandler->action(ActionNavigator)->setChecked(false); });
+ connect(m_actionHandler->action(ActionNavigator), &QAction::toggled, m_navigator, &Navigator::setVisible);
+
+ connect(m_actionHandler->action(ActionCopy), &QAction::triggered, this, [this]() {
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->copy();
+ });
+
+ connect(m_actionHandler->action(ActionCut), &QAction::triggered, this, [this]() {
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->cut();
+ });
+
+ connect(m_actionHandler->action(ActionPaste), &QAction::triggered, this, [this]() {
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->paste(view->view()->mapToScene(QPoint(30, 30)));
+ });
+
+ connect(m_actionHandler->action(ActionExportToImage), &QAction::triggered, this, &MainWidget::exportToImage);
+ connect(m_actionHandler->action(ActionScreenshot), &QAction::triggered, this, &MainWidget::saveScreenShot);
+
+ connect(m_errorPane->warningModel(), &WarningModel::warningsChanged, this, [this]() {
+ m_actionHandler->action(ActionFullNamespace)->setEnabled(m_errorPane->warningModel()->count(Warning::ErrorType) <= 0);
+ });
+
+ connect(m_actionHandler->action(ActionFullNamespace), &QAction::triggered, this, [this](bool checked) {
+ m_document->setUseFullNameSpace(checked);
+ });
+
+ connect(m_actionHandler->action(ActionAlignLeft), &QAction::triggered, this, [this]() { alignButtonClicked(ActionAlignLeft); });
+ connect(m_actionHandler->action(ActionAlignRight), &QAction::triggered, this, [this]() { alignButtonClicked(ActionAlignRight); });
+ connect(m_actionHandler->action(ActionAlignTop), &QAction::triggered, this, [this]() { alignButtonClicked(ActionAlignTop); });
+ connect(m_actionHandler->action(ActionAlignBottom), &QAction::triggered, this, [this]() { alignButtonClicked(ActionAlignBottom); });
+ connect(m_actionHandler->action(ActionAlignHorizontal), &QAction::triggered, this, [this]() { alignButtonClicked(ActionAlignHorizontal); });
+ connect(m_actionHandler->action(ActionAlignVertical), &QAction::triggered, this, [this]() { alignButtonClicked(ActionAlignVertical); });
+ connect(m_actionHandler->action(ActionAdjustWidth), &QAction::triggered, this, [this]() { adjustButtonClicked(ActionAdjustWidth); });
+ connect(m_actionHandler->action(ActionAdjustHeight), &QAction::triggered, this, [this]() { adjustButtonClicked(ActionAdjustHeight); });
+ connect(m_actionHandler->action(ActionAdjustSize), &QAction::triggered, this, [this]() { adjustButtonClicked(ActionAdjustSize); });
+
+ connect(m_actionHandler->action(ActionStatistics), &QAction::triggered, this, [this]() {
+ StatisticsDialog dialog;
+ dialog.setDocument(m_document);
+ dialog.exec();
+ });
+
+ // Init ToolButtons
+ auto stateColorButton = new ColorToolButton("StateColor", ":/scxmleditor/images/state_color.png", tr("State Color"));
+ auto fontColorButton = new ColorToolButton("FontColor", ":/scxmleditor/images/font_color.png", tr("Font Color"));
+ QToolButton *alignToolButton = createToolButton(":/scxmleditor/images/align_left.png", tr("Align Left"), QToolButton::MenuButtonPopup);
+ QToolButton *adjustToolButton = createToolButton(":/scxmleditor/images/adjust_width.png", tr("Adjust Width"), QToolButton::MenuButtonPopup);
+
+ // Connect state color change
+ connect(stateColorButton, &ColorToolButton::colorSelected, [this](const QString &color) {
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->setEditorInfo(Constants::C_SCXML_EDITORINFO_STATECOLOR, color);
+ });
+
+ // Connect font color change
+ connect(fontColorButton, &ColorToolButton::colorSelected, [this](const QString &color) {
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->setEditorInfo(Constants::C_SCXML_EDITORINFO_FONTCOLOR, color);
+ });
+
+ // Connect alignment change
+ alignToolButton->setProperty("currentAlignment", ActionAlignLeft);
+ connect(alignToolButton, &QToolButton::clicked, this, [=] {
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->alignStates(alignToolButton->property("currentAlignment").toInt());
+ });
+
+ // Connect alignment change
+ adjustToolButton->setProperty("currentAdjustment", ActionAdjustWidth);
+ connect(adjustToolButton, &QToolButton::clicked, this, [=] {
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->adjustStates(adjustToolButton->property("currentAdjustment").toInt());
+ });
+
+ auto alignmentMenu = new QMenu(tr("Alignment"), this);
+ for (int i = ActionAlignLeft; i <= ActionAlignVertical; ++i)
+ alignmentMenu->addAction(m_actionHandler->action(ActionType(i)));
+ alignToolButton->setMenu(alignmentMenu);
+
+ auto adjustmentMenu = new QMenu(tr("Adjustment"), this);
+ for (int i = ActionAdjustWidth; i <= ActionAdjustSize; ++i)
+ adjustmentMenu->addAction(m_actionHandler->action(ActionType(i)));
+ adjustToolButton->setMenu(adjustmentMenu);
+
+ m_toolButtons << stateColorButton << fontColorButton << alignToolButton << adjustToolButton;
+
+ // Init other ui:s
+ m_ui.m_horSplitter->setStretchFactor(1, 1);
+ m_ui.m_horSplitter->setStretchFactor(0, 0);
+ m_ui.m_horSplitter->setStretchFactor(2, 0);
+
+ const QSettings *s = Core::ICore::settings();
+ m_ui.m_horSplitter->restoreState(s->value(Constants::C_SETTINGS_SPLITTER).toByteArray());
+
+ m_actionHandler->action(ActionPaste)->setEnabled(false);
+
+ connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested,
+ this, &MainWidget::saveSettings);
+}
+
+void MainWidget::endTagChange(ScxmlDocument::TagChange change, const ScxmlTag *tag, const QVariant &value)
+{
+ Q_UNUSED(tag)
+
+ switch (change) {
+ case ScxmlDocument::TagChangeFullNameSpace:
+ m_actionHandler->action(ActionFullNamespace)->setChecked(value.toBool());
+ break;
+ default:
+ break;
+ }
+}
+
+QString saveImageFileFilter()
+{
+ const auto imageFormats = QImageWriter::supportedImageFormats();
+ const QByteArrayList supportedFormats = Utils::transform(imageFormats, [](const QByteArray &in)
+ { return QByteArray("*.") + in; });
+ return MainWidget::tr("Images (%1)").arg(QString::fromUtf8(supportedFormats.join(' ')));
+}
+
+void MainWidget::exportToImage()
+{
+ StateView *view = m_views.last();
+ if (!view)
+ return;
+
+ QString suggestedFileName = QFileInfo(m_document->fileName()).baseName();
+ if (suggestedFileName.isEmpty())
+ suggestedFileName = tr("Untitled");
+
+ QSettings *s = Core::ICore::settings();
+ const QString documentsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
+ const QString lastFolder = s->value(
+ Constants::C_SETTINGS_LASTEXPORTFOLDER, documentsLocation).toString();
+ suggestedFileName = QString::fromLatin1("%1/%2_%3.png")
+ .arg(lastFolder)
+ .arg(suggestedFileName)
+ .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"));
+ const QString selectedFileName = QFileDialog::getSaveFileName(this,
+ tr("Export Canvas To Image"),
+ suggestedFileName,
+ saveImageFileFilter());
+ if (!selectedFileName.isEmpty()) {
+ const QRectF r = view->scene()->itemsBoundingRect();
+ QImage image(r.size().toSize(), QImage::Format_ARGB32);
+ image.fill(QColor(0xef, 0xef, 0xef));
+
+ QPainter painter(&image);
+ view->scene()->render(&painter, QRectF(), r);
+
+ if (image.save(selectedFileName)) {
+ s->setValue(Constants::C_SETTINGS_LASTEXPORTFOLDER,
+ QFileInfo(selectedFileName).absolutePath());
+ } else {
+ QMessageBox::warning(this, tr("Export Failed"), tr("Could not export to image."));
+ }
+ }
+}
+
+void MainWidget::saveScreenShot()
+{
+ StateView *view = m_views.last();
+ if (!view)
+ return;
+
+ QSettings *s = Core::ICore::settings();
+ const QString documentsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
+ const QString lastFolder =
+ s->value(Constants::C_SETTINGS_LASTSAVESCREENSHOTFOLDER, documentsLocation).toString();
+ const QString filename = QFileDialog::getSaveFileName(this,
+ tr("Save Screenshot"),
+ lastFolder + "/scxml_screenshot.png",
+ saveImageFileFilter());
+ if (!filename.isEmpty()) {
+ const QImage image = view->view()->grabView();
+
+ if (image.save(filename)) {
+ s->setValue(Constants::C_SETTINGS_LASTSAVESCREENSHOTFOLDER,
+ QFileInfo(filename).absolutePath());
+ } else {
+ QMessageBox::warning(this, tr("Saving Failed"), tr("Could not save the screenshot."));
+ }
+ }
+}
+
+void MainWidget::saveSettings()
+{
+ QSettings *s = Core::ICore::settings();
+ s->setValue(Constants::C_SETTINGS_SPLITTER, m_ui.m_horSplitter->saveState());
+}
+
+void MainWidget::addStateView(BaseItem *item)
+{
+ auto view = new StateView(qgraphicsitem_cast<StateItem*>(item));
+
+ view->scene()->setActionHandler(m_actionHandler);
+ view->scene()->setWarningModel(m_errorPane->warningModel());
+ view->setUiFactory(m_uiFactory);
+
+ connect(view, &QObject::destroyed, this, [=] {
+ // TODO: un-lambdafy
+ m_views.removeAll(view);
+ m_document->popRootTag();
+ m_searchPane->setDocument(m_document);
+ m_ui.m_structure->setDocument(m_document);
+ m_ui.m_stateProperties->setDocument(m_document);
+ m_colorThemes->setDocument(m_document);
+ StateItem *it = view->parentState();
+ if (it) {
+ it->updateEditorInfo(true);
+ it->shrink();
+
+ // Update transitions
+ auto scene = static_cast<GraphicsScene*>(it->scene());
+ if (scene) {
+ QVector<ScxmlTag*> childTransitionTags;
+ TagUtils::findAllTransitionChildren(it->tag(), childTransitionTags);
+ for (int i = 0; i < childTransitionTags.count(); ++i) {
+ BaseItem *item = scene->findItem(childTransitionTags[i]);
+ if (item)
+ item->updateEditorInfo();
+ }
+ }
+ }
+ });
+ connect(view->view(), &GraphicsView::panningChanged, m_actionHandler->action(ActionPan), &QAction::setChecked);
+ connect(view->view(), &GraphicsView::magnifierChanged, m_actionHandler->action(ActionMagnifier), &QAction::setChecked);
+ connect(view->scene(), &GraphicsScene::openStateView, this, &MainWidget::addStateView, Qt::QueuedConnection);
+ connect(view->scene(), &GraphicsScene::selectedStateCountChanged, this, [this](int count) {
+ bool currentView = sender() == m_views.last()->scene();
+
+ // Enable/disable alignments
+ for (int i = ActionAlignLeft; i <= ActionAdjustSize; ++i)
+ m_actionHandler->action(ActionType(i))->setEnabled(currentView && count >= 2);
+ m_toolButtons[ToolButtonAlignment]->setEnabled(currentView && count >= 2);
+ m_toolButtons[ToolButtonAdjustment]->setEnabled(currentView && count >= 2);
+ });
+
+ // Enable/disable color buttons
+ connect(view->scene(), &GraphicsScene::selectedBaseItemCountChanged, this, [this](int count) {
+ m_toolButtons[ToolButtonStateColor]->setEnabled(count > 0);
+ m_toolButtons[ToolButtonFontColor]->setEnabled(count > 0);
+ });
+
+ connect(view->scene(), &GraphicsScene::pasteAvailable, this, [this](bool para) {
+ bool currentView = sender() == m_views.last()->scene();
+ m_actionHandler->action(ActionPaste)->setEnabled(currentView && para);
+ });
+
+ if (m_views.count() > 0)
+ m_views.last()->scene()->unselectAll();
+
+ if (item) {
+ m_document->pushRootTag(item->tag());
+ view->setDocument(m_document);
+ m_searchPane->setDocument(m_document);
+ m_ui.m_structure->setDocument(m_document);
+ m_ui.m_stateProperties->setDocument(m_document);
+ m_colorThemes->setDocument(m_document);
+ }
+ m_views << view;
+
+ m_ui.stackedWidget->setCurrentIndex(m_ui.stackedWidget->addWidget(view));
+}
+
+void MainWidget::initView(int id)
+{
+ for (int i = 0; i < m_views.count(); ++i)
+ m_views[i]->scene()->setTopMostScene(m_views[i] == m_views.last());
+
+ // Init and connect current view
+ auto view = qobject_cast<StateView*>(m_ui.stackedWidget->widget(id));
+ if (!view)
+ return;
+
+ m_searchPane->setGraphicsScene(view->scene());
+ m_ui.m_structure->setGraphicsScene(view->scene());
+ m_navigator->setCurrentView(view->view());
+ m_navigator->setCurrentScene(view->scene());
+ m_magnifier->setCurrentView(view->view());
+ m_magnifier->setCurrentScene(view->scene());
+ view->scene()->unselectAll();
+}
+
+void MainWidget::newDocument()
+{
+ clear();
+ addStateView();
+ m_document->setFileName(QString());
+ m_uiFactory->documentChanged(NewDocument, m_document);
+ documentChanged();
+}
+
+void MainWidget::clear()
+{
+ // Clear and delete all stateviews
+ while (m_views.count() > 0) {
+ m_views.last()->clear();
+ delete m_views.takeLast();
+ }
+
+ if (m_document)
+ m_document->clear();
+}
+
+void MainWidget::handleTabVisibilityChanged(bool visible)
+{
+ QLayout *layout = m_ui.m_mainContentWidget->layout();
+ if (visible) {
+ // Ensure that old widget is not splitter
+ if (!qobject_cast<QSplitter*>(layout->itemAt(0)->widget())) {
+ auto splitter = new QSplitter(Qt::Vertical);
+ splitter->setHandleWidth(1);
+ splitter->setChildrenCollapsible(false);
+ while (layout->count() > 0) {
+ QWidget *w = layout->takeAt(0)->widget();
+ if (w)
+ splitter->addWidget(w);
+ }
+ layout->addWidget(splitter);
+ }
+ } else {
+ // Ensure that old widget is splitter
+ if (qobject_cast<QSplitter*>(layout->itemAt(0)->widget())) {
+ auto splitter = static_cast<QSplitter*>(layout->takeAt(0)->widget());
+ auto newLayout = new QVBoxLayout;
+ newLayout->setContentsMargins(0, 0, 0, 0);
+ if (splitter) {
+ newLayout->addWidget(splitter->widget(0));
+ newLayout->addWidget(splitter->widget(1));
+ splitter->deleteLater();
+ }
+ delete layout;
+ m_ui.m_mainContentWidget->setLayout(newLayout);
+ }
+ }
+}
+
+bool MainWidget::load(const QString &fileName)
+{
+ clear();
+ addStateView();
+ m_document->load(fileName);
+ m_uiFactory->documentChanged(AfterLoad, m_document);
+ documentChanged();
+ return !m_document->hasError();
+}
+
+void MainWidget::documentChanged()
+{
+ StateView *view = m_views.last();
+
+ view->view()->setDrawingEnabled(false);
+ view->view()->update();
+
+ setEnabled(false);
+
+ m_ui.m_structure->setDocument(m_document);
+ m_searchPane->setDocument(m_document);
+ m_ui.m_stateProperties->setDocument(m_document);
+ m_colorThemes->setDocument(m_document);
+ view->setDocument(m_document);
+
+ if (!m_document->hasLayouted())
+ view->scene()->runAutomaticLayout();
+
+ view->view()->setDrawingEnabled(true);
+ view->view()->fitSceneToView();
+
+ undoStack()->clear();
+ undoStack()->setClean();
+
+ setEnabled(true);
+ emit dirtyChanged(false);
+
+ m_actionHandler->action(ActionFullNamespace)->setChecked(m_document->useFullNameSpace());
+}
+
+void MainWidget::showEvent(QShowEvent *e)
+{
+ QWidget::showEvent(e);
+ if (m_autoFit) {
+ fitToView();
+ m_autoFit = false;
+ }
+}
+
+void MainWidget::resizeEvent(QResizeEvent *e)
+{
+ QWidget::resizeEvent(e);
+
+ QRect r(QPoint(0, 0), e->size());
+ QRect navigatorRect(m_navigator->pos(), m_navigator->size());
+
+ if (!r.contains(navigatorRect)) {
+ m_navigator->move(qBound(0, m_navigator->pos().x(), r.width() - navigatorRect.width() + 1),
+ qBound(0, m_navigator->pos().y(), r.height() - navigatorRect.height() + 1));
+ }
+
+ int s = qMin(r.width(), r.height()) / 2;
+ m_magnifier->setFixedSize(s, s);
+ m_magnifier->setTopLeft(QPoint(m_ui.m_shapesFrame->width(), 0));
+}
+
+void MainWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ if (m_magnifier->isVisible()) {
+ QPoint p = event->pos() - m_magnifier->rect().center();
+ p.setX(qBound(m_ui.stackedWidget->x(), p.x(), m_ui.stackedWidget->x() + m_ui.stackedWidget->width()));
+ p.setY(qBound(m_ui.stackedWidget->y(), p.y(), m_ui.stackedWidget->y() + m_ui.stackedWidget->height()));
+ m_magnifier->move(p);
+ }
+
+ QWidget::mouseMoveEvent(event);
+}
+
+void MainWidget::keyPressEvent(QKeyEvent *e)
+{
+ if (e->modifiers() == Qt::ControlModifier) {
+ if (e->key() == Qt::Key_F)
+ m_ui.m_outputPaneWindow->showPane(m_searchPane);
+ }
+
+ QWidget::keyPressEvent(e);
+}
+
+void MainWidget::refresh()
+{
+ m_uiFactory->refresh();
+}
+
+WarningModel *MainWidget::warningModel() const
+{
+ return m_errorPane->warningModel();
+}
+
+ScxmlUiFactory *MainWidget::uiFactory() const
+{
+ return m_uiFactory;
+}
+
+void MainWidget::setMagnifier(bool m)
+{
+ m_magnifier->setVisible(m);
+ if (m) {
+ QPoint p = mapFromGlobal(QCursor::pos());
+ m_magnifier->move(p - m_magnifier->rect().center());
+ }
+}
+
+QToolButton *MainWidget::createToolButton(const QString &iconName, const QString &tooltip, QToolButton::ToolButtonPopupMode mode)
+{
+ auto button = new QToolButton;
+ button->setIcon(QIcon(iconName));
+ button->setToolTip(tooltip);
+ button->setPopupMode(mode);
+
+ return button;
+}
+
+void MainWidget::alignButtonClicked(ActionType alignType)
+{
+ if (alignType >= ActionAlignLeft && alignType <= ActionAlignVertical) {
+ m_toolButtons[ToolButtonAlignment]->setIcon(m_actionHandler->action(alignType)->icon());
+ m_toolButtons[ToolButtonAlignment]->setToolTip(m_actionHandler->action(alignType)->toolTip());
+ m_toolButtons[ToolButtonAlignment]->setProperty("currentAlignment", alignType);
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->alignStates(alignType);
+ }
+}
+
+void MainWidget::adjustButtonClicked(ActionType adjustType)
+{
+ if (adjustType >= ActionAdjustWidth && adjustType <= ActionAdjustSize) {
+ m_toolButtons[ToolButtonAdjustment]->setIcon(m_actionHandler->action(adjustType)->icon());
+ m_toolButtons[ToolButtonAdjustment]->setToolTip(m_actionHandler->action(adjustType)->toolTip());
+ m_toolButtons[ToolButtonAdjustment]->setProperty("currentAdjustment", adjustType);
+ StateView *view = m_views.last();
+ if (view)
+ view->scene()->adjustStates(adjustType);
+ }
+}
+
+QString MainWidget::contents() const
+{
+ return QLatin1String(m_document->content());
+}
+
+QUndoStack *MainWidget::undoStack() const
+{
+ return m_document->undoStack();
+}
+
+QString MainWidget::errorMessage() const
+{
+ return m_document->lastError();
+}
+
+QString MainWidget::fileName() const
+{
+ return m_document->fileName();
+}
+
+void MainWidget::setFileName(const QString &filename)
+{
+ m_document->setFileName(filename);
+}
+
+bool MainWidget::isDirty() const
+{
+ return m_document->changed();
+}
+
+void MainWidget::fitToView()
+{
+ StateView *view = m_views.last();
+ if (view)
+ view->view()->fitSceneToView();
+}
+
+bool MainWidget::save()
+{
+ m_uiFactory->documentChanged(BeginSave, m_document);
+ bool ok = m_document->save();
+ m_uiFactory->documentChanged(AfterSave, m_document);
+
+ return ok;
+}
+
+bool MainWidget::event(QEvent *e)
+{
+ if (e->type() == QEvent::WindowBlocked)
+ m_windowBlocked = true;
+
+ if (e->type() == QEvent::WindowActivate) {
+ if (m_windowBlocked)
+ m_windowBlocked = false;
+ else
+ refresh();
+ }
+
+ return QWidget::event(e);
+}
diff --git a/src/plugins/scxmleditor/common/mainwidget.h b/src/plugins/scxmleditor/common/mainwidget.h
new file mode 100644
index 00000000000..104130f1723
--- /dev/null
+++ b/src/plugins/scxmleditor/common/mainwidget.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "mytypes.h"
+#include "ui_mainwidget.h"
+
+#include <QToolButton>
+#include <QWidget>
+
+QT_FORWARD_DECLARE_CLASS(QFileInfo)
+QT_FORWARD_DECLARE_CLASS(QResizeEvent)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+class ActionHandler;
+class BaseItem;
+class ScxmlDocument;
+class ScxmlUiFactory;
+} // namespace PluginInterface
+
+namespace OutputPane {
+class ErrorWidget;
+class WarningModel;
+} // namespace OutputPane
+
+namespace Common {
+
+class StateView;
+class Navigator;
+class Magnifier;
+class Search;
+class ColorThemes;
+
+/**
+ * @brief The MainWidget class is the main widget of the application.
+ *
+ * The responsibility of this class is to init all necessary widgets,
+ * connect them each other and handle save-logic of the current document.
+ *
+ * When current document changes, the signal scxmlDocumentChanged(ScxmlDocument*) will be emitted.
+ */
+class MainWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit MainWidget(QWidget *parent = nullptr);
+ ~MainWidget() override;
+
+ QAction *action(PluginInterface::ActionType act);
+ QToolButton *toolButton(PluginInterface::ToolButtonType type);
+
+ QString fileName() const;
+ void setFileName(const QString &filename);
+ QString errorMessage() const;
+ QString contents() const;
+ QUndoStack *undoStack() const;
+ bool isDirty() const;
+ void newDocument();
+ void refresh();
+ OutputPane::WarningModel *warningModel() const;
+ PluginInterface::ScxmlUiFactory *uiFactory() const;
+ bool load(const QString &fileName);
+ bool save();
+ void addStateView(PluginInterface::BaseItem *item = nullptr);
+ void initView(int id);
+ void fitToView();
+
+protected:
+ void showEvent(QShowEvent*) override;
+ void resizeEvent(QResizeEvent *e) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void keyPressEvent(QKeyEvent *e) override;
+ bool event(QEvent *e) override;
+
+signals:
+ void dirtyChanged(bool dirty);
+
+private:
+ QToolButton *createToolButton(const QString &iconName, const QString &tooltip, QToolButton::ToolButtonPopupMode mode);
+ void documentChanged();
+ void init();
+ void clear();
+ void handleTabVisibilityChanged(bool visible);
+ void setMagnifier(bool m);
+ void alignButtonClicked(PluginInterface::ActionType align);
+ void adjustButtonClicked(PluginInterface::ActionType alignType);
+ void endTagChange(PluginInterface::ScxmlDocument::TagChange change, const PluginInterface::ScxmlTag *tag, const QVariant &value);
+ void exportToImage();
+ void saveScreenShot();
+ void saveSettings();
+
+ Navigator *m_navigator = nullptr;
+ Magnifier *m_magnifier = nullptr;
+ OutputPane::ErrorWidget *m_errorPane = nullptr;
+ Search *m_searchPane = nullptr;
+ ColorThemes *m_colorThemes = nullptr;
+
+ PluginInterface::ScxmlDocument *m_document = nullptr;
+ PluginInterface::ScxmlUiFactory *m_uiFactory = nullptr;
+ QVector<QToolButton*> m_toolButtons;
+ QVector<StateView*> m_views;
+ bool m_autoFit = true;
+ bool m_windowBlocked = false;
+ PluginInterface::ActionHandler *m_actionHandler = nullptr;
+ Ui::MainWidget m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/mainwidget.ui b/src/plugins/scxmleditor/common/mainwidget.ui
new file mode 100644
index 00000000000..b266df9c7ff
--- /dev/null
+++ b/src/plugins/scxmleditor/common/mainwidget.ui
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::MainWidget</class>
+ <widget class="QWidget" name="ScxmlEditor::Common::MainWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>770</width>
+ <height>611</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QSplitter" name="m_horSplitter">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="handleWidth">
+ <number>5</number>
+ </property>
+ <widget class="ScxmlEditor::Common::ShapesToolbox" name="m_shapesFrame">
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ <widget class="QWidget" name="m_mainContentWidget" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QStackedWidget" name="stackedWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="m_startPage">
+ <layout class="QHBoxLayout" name="horizontalLayout_2"/>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="ScxmlEditor::OutputPane::OutputTabWidget" name="m_outputPaneWindow">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QSplitter" name="m_verSplitter">
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="opaqueResize">
+ <bool>true</bool>
+ </property>
+ <property name="handleWidth">
+ <number>1</number>
+ </property>
+ <property name="childrenCollapsible">
+ <bool>false</bool>
+ </property>
+ <widget class="ScxmlEditor::Common::Structure" name="m_structure">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ <widget class="ScxmlEditor::Common::StateProperties" name="m_stateProperties">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ShapesToolbox</class>
+ <extends>QFrame</extends>
+ <header>shapestoolbox.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>Structure</class>
+ <extends>QFrame</extends>
+ <header>structure.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>StateProperties</class>
+ <extends>QFrame</extends>
+ <header>stateproperties.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>OutputTabWidget</class>
+ <extends>QFrame</extends>
+ <header>outputtabwidget.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/movableframe.cpp b/src/plugins/scxmleditor/common/movableframe.cpp
new file mode 100644
index 00000000000..e50ebe53306
--- /dev/null
+++ b/src/plugins/scxmleditor/common/movableframe.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "movableframe.h"
+#include <QMouseEvent>
+
+using namespace ScxmlEditor::Common;
+
+MovableFrame::MovableFrame(QWidget *parent)
+ : QFrame(parent)
+{
+ setContentsMargins(0, 0, 0, 0);
+ setFrameShape(QFrame::NoFrame);
+ setMouseTracking(true);
+}
+
+void MovableFrame::mousePressEvent(QMouseEvent *e)
+{
+ QFrame::mousePressEvent(e);
+ m_startPoint = e->pos();
+ m_mouseDown = true;
+}
+
+void MovableFrame::mouseMoveEvent(QMouseEvent *e)
+{
+ QFrame::mouseMoveEvent(e);
+
+ if (m_mouseDown) {
+ QPoint p = mapToParent(e->pos()) - m_startPoint;
+ move(qBound(1, p.x(), parentWidget()->width() - width() - 1), qBound(1, p.y(), parentWidget()->height() - height() - 1));
+ }
+}
+
+void MovableFrame::mouseReleaseEvent(QMouseEvent *e)
+{
+ QFrame::mouseReleaseEvent(e);
+ m_mouseDown = false;
+}
diff --git a/src/plugins/scxmleditor/common/movableframe.h b/src/plugins/scxmleditor/common/movableframe.h
new file mode 100644
index 00000000000..b739532cb6f
--- /dev/null
+++ b/src/plugins/scxmleditor/common/movableframe.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QFrame>
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+/**
+ * @brief The MovableFrame class is the base class for movable frames inside the parent widget.
+ *
+ * For example Navigator and Warnings are the movable frames.
+ */
+class MovableFrame : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit MovableFrame(QWidget *parent = nullptr);
+
+signals:
+ void hideFrame();
+
+protected:
+ void mousePressEvent(QMouseEvent *e) override;
+ void mouseMoveEvent(QMouseEvent *e) override;
+ void mouseReleaseEvent(QMouseEvent *e) override;
+
+private:
+ void checkCursor(const QPoint &p);
+
+ QPoint m_startPoint;
+ bool m_mouseDown = false;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/navigator.cpp b/src/plugins/scxmleditor/common/navigator.cpp
new file mode 100644
index 00000000000..c6fa76169b1
--- /dev/null
+++ b/src/plugins/scxmleditor/common/navigator.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "navigator.h"
+#include "graphicsscene.h"
+#include "graphicsview.h"
+#include "sizegrip.h"
+
+#include <QResizeEvent>
+
+using namespace ScxmlEditor::Common;
+
+Navigator::Navigator(QWidget *parent)
+ : MovableFrame(parent)
+{
+ m_ui.setupUi(this);
+ connect(m_ui.m_closeButton, &QToolButton::clicked, this, &Navigator::hideFrame);
+ m_sizeGrip = new SizeGrip(this);
+ m_sizeGrip->setGeometry(0, 0, 18, 18);
+}
+
+void Navigator::setCurrentView(GraphicsView *view)
+{
+ if (m_currentView) {
+ m_currentView->disconnect(m_ui.m_navigatorView);
+ m_ui.m_navigatorView->disconnect(m_currentView);
+ m_currentView->disconnect(m_ui.m_navigatorSlider);
+ m_ui.m_navigatorSlider->disconnect(m_currentView);
+ }
+
+ m_currentView = view;
+
+ if (m_currentView) {
+ connect(m_currentView, &GraphicsView::viewChanged, m_ui.m_navigatorView, &NavigatorGraphicsView::setMainViewPolygon);
+ connect(m_currentView, &GraphicsView::zoomPercentChanged, m_ui.m_navigatorSlider, &NavigatorSlider::setSliderValue);
+
+ connect(m_ui.m_navigatorSlider, &NavigatorSlider::valueChanged, m_currentView, &GraphicsView::zoomTo);
+ connect(m_ui.m_navigatorView, &NavigatorGraphicsView::moveMainViewTo, m_currentView, &GraphicsView::moveToPoint);
+ connect(m_ui.m_navigatorView, &NavigatorGraphicsView::zoomIn, m_currentView, &GraphicsView::zoomIn);
+ connect(m_ui.m_navigatorView, &NavigatorGraphicsView::zoomOut, m_currentView, &GraphicsView::zoomOut);
+ }
+}
+
+void Navigator::setCurrentScene(ScxmlEditor::PluginInterface::GraphicsScene *scene)
+{
+ m_ui.m_navigatorView->setGraphicsScene(scene);
+}
+
+void Navigator::resizeEvent(QResizeEvent *e)
+{
+ MovableFrame::resizeEvent(e);
+ m_sizeGrip->move(e->size().width() - m_sizeGrip->width(), e->size().height() - m_sizeGrip->height());
+}
diff --git a/src/plugins/scxmleditor/common/navigator.h b/src/plugins/scxmleditor/common/navigator.h
new file mode 100644
index 00000000000..380847d315f
--- /dev/null
+++ b/src/plugins/scxmleditor/common/navigator.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "movableframe.h"
+#include "ui_navigator.h"
+#include <QPointer>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface { class GraphicsScene; }
+
+namespace Common {
+
+class SizeGrip;
+class GraphicsView;
+
+/**
+ * @brief The Navigator class is the "minimap" widget for navigate and zoom in the scene.
+ */
+class Navigator : public MovableFrame
+{
+ Q_OBJECT
+
+public:
+ explicit Navigator(QWidget *parent = nullptr);
+
+ void setCurrentView(GraphicsView *view);
+ void setCurrentScene(PluginInterface::GraphicsScene *scene);
+
+protected:
+ void resizeEvent(QResizeEvent *e) override;
+
+private:
+ SizeGrip *m_sizeGrip;
+ QPointer<GraphicsView> m_currentView;
+ Ui::Navigator m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/navigator.ui b/src/plugins/scxmleditor/common/navigator.ui
new file mode 100644
index 00000000000..cedf2069b1f
--- /dev/null
+++ b/src/plugins/scxmleditor/common/navigator.ui
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::Navigator</class>
+ <widget class="QFrame" name="ScxmlEditor::Common::Navigator">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>300</width>
+ <height>230</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Frame</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="m_titleFrame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="m_title">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Navigator</string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_closeButton">
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="common.qrc">
+ <normaloff>:/scxmleditor/images/icon-close.png</normaloff>:/scxmleditor/images/icon-close.png</iconset>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="ScxmlEditor::Common::NavigatorGraphicsView" name="m_navigatorView">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ScxmlEditor::Common::NavigatorSlider" name="m_navigatorSlider">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ScxmlEditor::Common::NavigatorGraphicsView</class>
+ <extends>QGraphicsView</extends>
+ <header>navigatorgraphicsview.h</header>
+ </customwidget>
+ <customwidget>
+ <class>ScxmlEditor::Common::NavigatorSlider</class>
+ <extends>QFrame</extends>
+ <header>navigatorslider.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="common.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/navigatorgraphicsview.cpp b/src/plugins/scxmleditor/common/navigatorgraphicsview.cpp
new file mode 100644
index 00000000000..9334aa824c9
--- /dev/null
+++ b/src/plugins/scxmleditor/common/navigatorgraphicsview.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "navigatorgraphicsview.h"
+#include "graphicsscene.h"
+#include "graphicsview.h"
+
+#include <QWheelEvent>
+
+using namespace ScxmlEditor::Common;
+
+NavigatorGraphicsView::NavigatorGraphicsView(QWidget *parent)
+ : QGraphicsView(parent)
+{
+ setInteractive(false);
+ setViewportUpdateMode(FullViewportUpdate);
+}
+
+void NavigatorGraphicsView::setGraphicsScene(ScxmlEditor::PluginInterface::GraphicsScene *s)
+{
+ if (scene())
+ scene()->disconnect(this);
+
+ setScene(s);
+ if (s)
+ connect(s, &PluginInterface::GraphicsScene::sceneRectChanged, this, &NavigatorGraphicsView::updateView);
+}
+
+void NavigatorGraphicsView::setMainViewPolygon(const QPolygonF &pol)
+{
+ m_mainViewPolygon = mapFromScene(pol);
+ updateView();
+}
+
+void NavigatorGraphicsView::updateView()
+{
+ fitInView(sceneRect()); //, Qt::KeepAspectRatio);
+ update();
+}
+
+void NavigatorGraphicsView::paintEvent(QPaintEvent *e)
+{
+ QGraphicsView::paintEvent(e);
+
+ QPainter p(viewport());
+ p.save();
+ p.setBrush(Qt::NoBrush);
+ p.setPen(Qt::red);
+ p.drawPolygon(m_mainViewPolygon);
+ p.restore();
+}
+
+void NavigatorGraphicsView::wheelEvent(QWheelEvent *event)
+{
+ if (Qt::ControlModifier & event->modifiers()) {
+ if (event->delta() > 0)
+ emit zoomIn();
+ else
+ emit zoomOut();
+
+ emit moveMainViewTo(mapToScene(event->pos()));
+ } else
+ QGraphicsView::wheelEvent(event);
+}
+
+void NavigatorGraphicsView::mousePressEvent(QMouseEvent *event)
+{
+ m_mouseDown = true;
+ emit moveMainViewTo(mapToScene(event->pos()));
+ QGraphicsView::mousePressEvent(event);
+}
+
+void NavigatorGraphicsView::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_UNUSED(event)
+ m_mouseDown = false;
+}
+
+void NavigatorGraphicsView::mouseMoveEvent(QMouseEvent *event)
+{
+ if (m_mouseDown)
+ emit moveMainViewTo(mapToScene(event->pos()));
+}
diff --git a/src/plugins/scxmleditor/common/navigatorgraphicsview.h b/src/plugins/scxmleditor/common/navigatorgraphicsview.h
new file mode 100644
index 00000000000..21c93d3903b
--- /dev/null
+++ b/src/plugins/scxmleditor/common/navigatorgraphicsview.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QGraphicsView>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface { class GraphicsScene; }
+
+namespace Common {
+
+class NavigatorGraphicsView : public QGraphicsView
+{
+ Q_OBJECT
+
+public:
+ explicit NavigatorGraphicsView(QWidget *parent = nullptr);
+
+ void setGraphicsScene(PluginInterface::GraphicsScene *scene);
+ void updateView();
+ void setMainViewPolygon(const QPolygonF &pol);
+
+signals:
+ void moveMainViewTo(const QPointF &point);
+ void zoomIn();
+ void zoomOut();
+
+protected:
+ void paintEvent(QPaintEvent*) override;
+ void wheelEvent(QWheelEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+
+private:
+ QPolygonF m_mainViewPolygon;
+ bool m_mouseDown = false;
+ double m_minZoomValue = 1.0;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/navigatorslider.cpp b/src/plugins/scxmleditor/common/navigatorslider.cpp
new file mode 100644
index 00000000000..53ed72cdfc4
--- /dev/null
+++ b/src/plugins/scxmleditor/common/navigatorslider.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "navigatorslider.h"
+
+using namespace ScxmlEditor::Common;
+
+NavigatorSlider::NavigatorSlider(QWidget *parent)
+ : QFrame(parent)
+{
+ m_ui.setupUi(this);
+
+ connect(m_ui.m_zoomOut, &QToolButton::clicked, this, &NavigatorSlider::zoomOut);
+ connect(m_ui.m_zoomIn, &QToolButton::clicked, this, &NavigatorSlider::zoomIn);
+ connect(m_ui.m_slider, &QSlider::valueChanged, this, [=](int newValue){
+ emit valueChanged(newValue);
+ });
+}
+
+void NavigatorSlider::zoomIn()
+{
+ m_ui.m_slider->setValue(m_ui.m_slider->value() + 1);
+}
+
+void NavigatorSlider::zoomOut()
+{
+ m_ui.m_slider->setValue(m_ui.m_slider->value() - 1);
+}
+
+void NavigatorSlider::setSliderValue(int val)
+{
+ m_ui.m_slider->blockSignals(true);
+ m_ui.m_slider->setValue(val);
+ m_ui.m_slider->blockSignals(false);
+}
diff --git a/src/plugins/scxmleditor/common/navigatorslider.h b/src/plugins/scxmleditor/common/navigatorslider.h
new file mode 100644
index 00000000000..7d89f838717
--- /dev/null
+++ b/src/plugins/scxmleditor/common/navigatorslider.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_navigatorslider.h"
+
+#include <QFrame>
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+class NavigatorSlider : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit NavigatorSlider(QWidget *parent = nullptr);
+
+ int value() const
+ {
+ return m_ui.m_slider->value();
+ }
+
+ void setSliderValue(int val);
+ void zoomIn();
+ void zoomOut();
+
+signals:
+ void valueChanged(int);
+
+private:
+ Ui::NavigatorSlider m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/navigatorslider.ui b/src/plugins/scxmleditor/common/navigatorslider.ui
new file mode 100644
index 00000000000..aa961aa351a
--- /dev/null
+++ b/src/plugins/scxmleditor/common/navigatorslider.ui
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::NavigatorSlider</class>
+ <widget class="QFrame" name="ScxmlEditor::Common::NavigatorSlider">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>240</width>
+ <height>40</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Frame</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="m_innerFrame">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QToolButton" name="m_zoomOut">
+ <property name="text">
+ <string>-</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/scxmleditor/images/minus.png</normaloff>:/scxmleditor/images/minus.png</iconset>
+ </property>
+ <property name="autoRepeat">
+ <bool>true</bool>
+ </property>
+ <property name="autoRepeatDelay">
+ <number>200</number>
+ </property>
+ <property name="autoRepeatInterval">
+ <number>10</number>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextOnly</enum>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSlider" name="m_slider">
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ <property name="sliderPosition">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="invertedAppearance">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_zoomIn">
+ <property name="text">
+ <string>+</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normaloff>:/scxmleditor/images/plus.png</normaloff>:/scxmleditor/images/plus.png</iconset>
+ </property>
+ <property name="autoRepeat">
+ <bool>true</bool>
+ </property>
+ <property name="autoRepeatDelay">
+ <number>200</number>
+ </property>
+ <property name="autoRepeatInterval">
+ <number>10</number>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextOnly</enum>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/search.cpp b/src/plugins/scxmleditor/common/search.cpp
new file mode 100644
index 00000000000..37273a8ef9e
--- /dev/null
+++ b/src/plugins/scxmleditor/common/search.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "search.h"
+#include "graphicsscene.h"
+#include "scxmldocument.h"
+#include "searchmodel.h"
+
+#include <QSortFilterProxyModel>
+
+using namespace ScxmlEditor::PluginInterface;
+using namespace ScxmlEditor::Common;
+
+Search::Search(QWidget *parent)
+ : OutputPane(parent)
+{
+ m_ui.setupUi(this);
+
+ m_model = new SearchModel(this);
+ m_proxyModel = new QSortFilterProxyModel(this);
+
+ m_proxyModel->setFilterKeyColumn(-1);
+ m_proxyModel->setFilterRole(SearchModel::FilterRole);
+ m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ m_proxyModel->setSourceModel(m_model);
+ m_proxyModel->setDynamicSortFilter(false);
+ m_proxyModel->setFilterWildcard("xxxxxxxx");
+
+ m_ui.m_searchView->setModel(m_proxyModel);
+
+ connect(m_ui.m_searchEdit, &QLineEdit::textChanged, this, &Search::setSearchText);
+ connect(m_ui.m_searchView, &ScxmlEditor::OutputPane::TableView::pressed, this, &Search::rowActivated);
+ connect(m_ui.m_searchView, &ScxmlEditor::OutputPane::TableView::entered, this, &Search::rowEntered);
+}
+
+void Search::setPaneFocus()
+{
+ m_ui.m_searchEdit->setFocus();
+}
+
+void Search::setSearchText(const QString &text)
+{
+ m_model->setFilter(text);
+ m_proxyModel->setFilterWildcard(text.isEmpty() ? "xxxxxxxx" : text);
+}
+
+void Search::setDocument(ScxmlDocument *document)
+{
+ m_document = document;
+ m_model->setDocument(document);
+}
+
+void Search::setGraphicsScene(GraphicsScene *scene)
+{
+ m_scene = scene;
+ connect(m_ui.m_searchView, &ScxmlEditor::OutputPane::TableView::mouseExited, m_scene, &GraphicsScene::unhighlightAll);
+}
+
+void Search::rowEntered(const QModelIndex &index)
+{
+ if (m_scene) {
+ ScxmlTag *tag = m_model->tag(m_proxyModel->mapToSource(index));
+ if (tag)
+ m_scene->highlightItems(QVector<ScxmlTag*>() << tag);
+ else
+ m_scene->unhighlightAll();
+ }
+}
+
+void Search::rowActivated(const QModelIndex &index)
+{
+ if (m_scene)
+ m_scene->unselectAll();
+
+ if (m_document)
+ m_document->setCurrentTag(m_model->tag(m_proxyModel->mapToSource(index)));
+}
diff --git a/src/plugins/scxmleditor/common/search.h b/src/plugins/scxmleditor/common/search.h
new file mode 100644
index 00000000000..c424ac736c6
--- /dev/null
+++ b/src/plugins/scxmleditor/common/search.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "outputpane.h"
+#include "ui_search.h"
+
+#include <utils/utilsicons.h>
+
+#include <QFrame>
+#include <QPointer>
+
+QT_FORWARD_DECLARE_CLASS(QSortFilterProxyModel)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+class GraphicsScene;
+class ScxmlDocument;
+} // namespace PluginInterface
+
+namespace Common {
+
+class SearchModel;
+
+/**
+ * @brief The Search class provides the way to search/find items.
+ */
+class Search : public OutputPane::OutputPane
+{
+ Q_OBJECT
+
+public:
+ explicit Search(QWidget *parent = nullptr);
+
+ QString title() const override
+ {
+ return tr("Search");
+ }
+
+ QIcon icon() const override
+ {
+ return Utils::Icons::MAGNIFIER.icon();
+ }
+
+ void setPaneFocus() override;
+ void setDocument(PluginInterface::ScxmlDocument *document);
+ void setGraphicsScene(PluginInterface::GraphicsScene *scene);
+
+private:
+ void setSearchText(const QString &text);
+ void rowEntered(const QModelIndex &index);
+ void rowActivated(const QModelIndex &index);
+
+ QPointer<PluginInterface::GraphicsScene> m_scene;
+ SearchModel *m_model;
+ QSortFilterProxyModel *m_proxyModel;
+ QPointer<PluginInterface::ScxmlDocument> m_document;
+ Ui::Search m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/search.ui b/src/plugins/scxmleditor/common/search.ui
new file mode 100644
index 00000000000..ab7bf3f9a2a
--- /dev/null
+++ b/src/plugins/scxmleditor/common/search.ui
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::Search</class>
+ <widget class="QWidget" name="ScxmlEditor::Common::Search">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="m_subTitleFrame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>31</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="sizeIncrement">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLineEdit" name="m_searchEdit">
+ <property name="placeholderText">
+ <string>Enter search term</string>
+ </property>
+ <property name="clearButtonEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="m_paneInnerFrame">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>1</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="ScxmlEditor::OutputPane::TableView" name="m_searchView">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="editTriggers">
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ <property name="showDropIndicator" stdset="0">
+ <bool>true</bool>
+ </property>
+ <property name="dragEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="verticalScrollMode">
+ <enum>QAbstractItemView::ScrollPerPixel</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="gridStyle">
+ <enum>Qt::NoPen</enum>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderDefaultSectionSize">
+ <number>80</number>
+ </attribute>
+ <attribute name="horizontalHeaderMinimumSectionSize">
+ <number>50</number>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="verticalHeaderDefaultSectionSize">
+ <number>19</number>
+ </attribute>
+ <attribute name="verticalHeaderMinimumSectionSize">
+ <number>19</number>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>TableView</class>
+ <extends>QTableView</extends>
+ <header>tableview.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/searchmodel.cpp b/src/plugins/scxmleditor/common/searchmodel.cpp
new file mode 100644
index 00000000000..a7b6c6ecb41
--- /dev/null
+++ b/src/plugins/scxmleditor/common/searchmodel.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "searchmodel.h"
+#include "scxmltagutils.h"
+
+using namespace ScxmlEditor::PluginInterface;
+using namespace ScxmlEditor::Common;
+
+SearchModel::SearchModel(QObject *parent)
+ : QAbstractTableModel(parent)
+{
+}
+
+QVariant SearchModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ return section == 0 ? tr("Type") : tr("Name");
+
+ return QVariant();
+}
+
+QVariant SearchModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() < 0 || index.row() >= m_allTags.count())
+ return QVariant();
+
+ ScxmlTag *tag = m_allTags[index.row()];
+ switch (role) {
+ case FilterRole: {
+ if (index.column() == 0)
+ return tag->tagName(true);
+ else {
+ QStringList keys = tag->attributeNames();
+ QStringList values = tag->attributeValues();
+ QStringList val;
+ for (int i = 0; i < keys.count(); ++i)
+ val << QString::fromLatin1("%1=%2").arg(keys[i]).arg(values[i]);
+ return val.join(";");
+ }
+ }
+ case Qt::DisplayRole: {
+ if (index.column() == 0)
+ return tag->tagName(true);
+ else {
+ QStringList keys = tag->attributeNames();
+ QStringList values = tag->attributeValues();
+ QStringList val;
+ for (int i = 0; i < values.count(); ++i) {
+ if (keys[i].contains(m_strFilter, Qt::CaseInsensitive) || values[i].contains(m_strFilter, Qt::CaseInsensitive))
+ val << QString::fromLatin1("%1=%2").arg(keys[i]).arg(values[i]);
+ }
+ return val.join(";");
+ }
+ }
+ default:
+ break;
+ }
+
+ return QVariant();
+}
+
+void SearchModel::setDocument(ScxmlDocument *document)
+{
+ if (m_document)
+ m_document->disconnect(this);
+
+ m_document = document;
+ resetModel();
+
+ if (m_document)
+ connect(m_document, &ScxmlDocument::endTagChange, this, &SearchModel::tagChange);
+}
+
+void SearchModel::resetModel()
+{
+ beginResetModel();
+ m_allTags.clear();
+ if (m_document && m_document->rootTag()) {
+ m_allTags << m_document->rootTag();
+ TagUtils::findAllChildren(m_document->rootTag(), m_allTags);
+ }
+ endResetModel();
+ emit layoutChanged();
+}
+
+void SearchModel::tagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value)
+{
+ Q_UNUSED(tag)
+ Q_UNUSED(value)
+
+ switch (change) {
+ case ScxmlDocument::TagAddChild:
+ case ScxmlDocument::TagRemoveChild:
+ case ScxmlDocument::TagChangeParentAddChild:
+ case ScxmlDocument::TagChangeParentRemoveChild:
+ resetModel();
+ break;
+ default:
+ break;
+ }
+}
+
+int SearchModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return 2;
+}
+int SearchModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return m_allTags.count();
+}
+
+ScxmlTag *SearchModel::tag(const QModelIndex &ind)
+{
+ if (ind.row() >= 0 && ind.row() < m_allTags.count())
+ return m_allTags[ind.row()];
+
+ return nullptr;
+}
+
+void SearchModel::setFilter(const QString &filter)
+{
+ m_strFilter = filter;
+}
diff --git a/src/plugins/scxmleditor/common/searchmodel.h b/src/plugins/scxmleditor/common/searchmodel.h
new file mode 100644
index 00000000000..c29b88786ce
--- /dev/null
+++ b/src/plugins/scxmleditor/common/searchmodel.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmldocument.h"
+#include "scxmltag.h"
+
+#include <QAbstractTableModel>
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+class SearchModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ enum AttributeRole {
+ FilterRole = Qt::UserRole + 1
+ };
+
+ SearchModel(QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ PluginInterface::ScxmlTag *tag(const QModelIndex &ind);
+ void setFilter(const QString &filter);
+ void setDocument(PluginInterface::ScxmlDocument *document);
+
+private:
+ void tagChange(PluginInterface::ScxmlDocument::TagChange change, PluginInterface::ScxmlTag *tag, const QVariant &value);
+ void resetModel();
+
+ PluginInterface::ScxmlDocument *m_document = nullptr;
+ QVector<PluginInterface::ScxmlTag*> m_allTags;
+ QString m_strFilter;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/shapegroupwidget.cpp b/src/plugins/scxmleditor/common/shapegroupwidget.cpp
new file mode 100644
index 00000000000..6ec8a84ef1c
--- /dev/null
+++ b/src/plugins/scxmleditor/common/shapegroupwidget.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "shapegroupwidget.h"
+#include "dragshapebutton.h"
+#include "shapeprovider.h"
+
+#include <utils/flowlayout.h>
+
+using namespace ScxmlEditor::PluginInterface;
+using namespace ScxmlEditor::Common;
+
+ShapeGroupWidget::ShapeGroupWidget(ShapeProvider *shapeProvider, int groupIndex, QWidget *parent)
+ : QWidget(parent)
+{
+ m_ui.setupUi(this);
+ auto layout = new Utils::FlowLayout;
+ layout->setContentsMargins(0, 0, 0, 0);
+
+ m_ui.m_title->setText(shapeProvider->groupTitle(groupIndex));
+
+ for (int i = 0; i < shapeProvider->shapeCount(groupIndex); ++i) {
+ auto button = new DragShapeButton(this);
+ button->setText(shapeProvider->shapeTitle(groupIndex, i));
+ button->setIcon(shapeProvider->shapeIcon(groupIndex, i));
+ button->setShapeInfo(groupIndex, i);
+
+ layout->addWidget(button);
+ }
+
+ connect(m_ui.m_closeButton, &QToolButton::clicked, this, [this]() {
+ m_ui.m_content->setVisible(!m_ui.m_content->isVisible());
+ m_ui.m_closeButton->setIcon(QIcon(QLatin1String(m_ui.m_content->isVisible() ? ":/scxmleditor/images/arrow_down.png" : ":/scxmleditor/images/arrow_right.png")));
+ });
+
+ m_ui.m_content->setLayout(layout);
+}
diff --git a/src/plugins/scxmleditor/common/shapegroupwidget.h b/src/plugins/scxmleditor/common/shapegroupwidget.h
new file mode 100644
index 00000000000..e8a82149c1f
--- /dev/null
+++ b/src/plugins/scxmleditor/common/shapegroupwidget.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_shapegroupwidget.h"
+
+#include <QWidget>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface { class ShapeProvider; }
+
+namespace Common {
+
+class ShapeGroupWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ShapeGroupWidget(PluginInterface::ShapeProvider *shapeProvider, int groupIndex, QWidget *parent = nullptr);
+
+private:
+ Ui::ShapeGroupWidget m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/shapegroupwidget.ui b/src/plugins/scxmleditor/common/shapegroupwidget.ui
new file mode 100644
index 00000000000..f01bc9fccc2
--- /dev/null
+++ b/src/plugins/scxmleditor/common/shapegroupwidget.ui
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::ShapeGroupWidget</class>
+ <widget class="QWidget" name="ScxmlEditor::Common::ShapeGroupWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>113</width>
+ <height>184</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="m_titleFrame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="sizeIncrement">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="m_title">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Shape title</string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_closeButton">
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="common.qrc">
+ <normaloff>:/scxmleditor/images/arrow_down.png</normaloff>:/scxmleditor/images/arrow_down.png</iconset>
+ </property>
+ <property name="autoRepeat">
+ <bool>false</bool>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ <property name="arrowType">
+ <enum>Qt::NoArrow</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QWidget" name="m_content" native="true"/>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="common.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/shapestoolbox.cpp b/src/plugins/scxmleditor/common/shapestoolbox.cpp
new file mode 100644
index 00000000000..a9489c44c15
--- /dev/null
+++ b/src/plugins/scxmleditor/common/shapestoolbox.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "shapestoolbox.h"
+#include "baseitem.h"
+#include "scxmluifactory.h"
+#include "shapegroupwidget.h"
+#include "shapeprovider.h"
+#include "ui_shapestoolbox.h"
+
+#include <QDebug>
+#include <QResizeEvent>
+#include <QShowEvent>
+
+#include <utils/qtcassert.h>
+
+using namespace ScxmlEditor::Common;
+
+ShapesToolbox::ShapesToolbox(QWidget *parent)
+ : QFrame(parent)
+{
+ m_ui.setupUi(this);
+}
+
+void ShapesToolbox::setUIFactory(ScxmlEditor::PluginInterface::ScxmlUiFactory *factory)
+{
+ QTC_ASSERT(factory, return);
+
+ m_shapeProvider = static_cast<PluginInterface::ShapeProvider*>(factory->object("shapeProvider"));
+ connect(m_shapeProvider, &PluginInterface::ShapeProvider::changed, this, &ShapesToolbox::initView);
+ initView();
+}
+
+void ShapesToolbox::initView()
+{
+ // Delete old widgets
+ while (m_widgets.count() > 0)
+ delete m_widgets.takeLast();
+
+ // Create new widgets
+ if (m_shapeProvider) {
+ for (int i = 0; i < m_shapeProvider->groupCount(); ++i) {
+ auto widget = new ShapeGroupWidget(m_shapeProvider, i);
+ m_widgets << widget;
+ m_ui.m_shapeGrouplayout->addWidget(widget);
+ }
+ }
+
+ m_ui.m_shapeGrouplayout->update();
+ update();
+}
diff --git a/src/plugins/scxmleditor/common/shapestoolbox.h b/src/plugins/scxmleditor/common/shapestoolbox.h
new file mode 100644
index 00000000000..d48c2d30e43
--- /dev/null
+++ b/src/plugins/scxmleditor/common/shapestoolbox.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_shapestoolbox.h"
+
+#include <QFrame>
+#include <QPointer>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+class ScxmlUiFactory;
+class ShapeProvider;
+} // namespace PluginInterface
+
+namespace Common {
+
+/**
+ * @brief The ShapesToolbox class provides all draggable state-items.
+ */
+class ShapesToolbox : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit ShapesToolbox(QWidget *parent = nullptr);
+
+ void setUIFactory(PluginInterface::ScxmlUiFactory *uifactory);
+ void initView();
+
+private:
+ QPointer<PluginInterface::ShapeProvider> m_shapeProvider;
+ QList<QWidget*> m_widgets;
+ Ui::ShapesToolbox m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/shapestoolbox.ui b/src/plugins/scxmleditor/common/shapestoolbox.ui
new file mode 100644
index 00000000000..10a50288392
--- /dev/null
+++ b/src/plugins/scxmleditor/common/shapestoolbox.ui
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::ShapesToolbox</class>
+ <widget class="QFrame" name="ScxmlEditor::Common::ShapesToolbox">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>321</width>
+ <height>665</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Frame</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QScrollArea" name="scrollArea">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>321</width>
+ <height>665</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,1">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="m_shapeGrouplayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/sizegrip.cpp b/src/plugins/scxmleditor/common/sizegrip.cpp
new file mode 100644
index 00000000000..8d5eb8c81b3
--- /dev/null
+++ b/src/plugins/scxmleditor/common/sizegrip.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "sizegrip.h"
+#include <QMouseEvent>
+#include <QPainter>
+
+using namespace ScxmlEditor::Common;
+
+SizeGrip::SizeGrip(QWidget *parent)
+ : QWidget(parent)
+{
+ setMouseTracking(true);
+}
+
+void SizeGrip::resizeEvent(QResizeEvent *e)
+{
+ QWidget::resizeEvent(e);
+ QRect r = rect().adjusted(2, 2, -2, -2);
+ m_pol = QPolygon() << r.topRight() << r.bottomRight() << r.bottomLeft();
+}
+
+void SizeGrip::mousePressEvent(QMouseEvent *e)
+{
+ QWidget::mousePressEvent(e);
+ m_startPoint = e->globalPos();
+ m_startRect = parentWidget()->rect();
+ m_mouseDown = true;
+ checkCursor(e->pos());
+}
+
+void SizeGrip::mouseMoveEvent(QMouseEvent *e)
+{
+ if (m_mouseDown) {
+ QPoint p = e->globalPos() - m_startPoint;
+ parentWidget()->resize(m_startRect.width() + p.x(), m_startRect.height() + p.y());
+ } else {
+ checkCursor(e->pos());
+ }
+}
+
+void SizeGrip::mouseReleaseEvent(QMouseEvent *e)
+{
+ QWidget::mouseReleaseEvent(e);
+ m_mouseDown = false;
+}
+
+void SizeGrip::paintEvent(QPaintEvent *e)
+{
+ QWidget::paintEvent(e);
+
+ QRect r = rect();
+ int c = r.width() * 0.33;
+
+ QPainter p(this);
+ p.setPen(Qt::gray);
+ p.drawLine(r.bottomLeft() + QPoint(0, -2), r.topRight() + QPoint(-2, 0));
+ p.drawLine(r.bottomLeft() + QPoint(c, -2), r.topRight() + QPoint(-2, c));
+ p.drawLine(r.bottomLeft() + QPoint(2 * c, -2), r.topRight() + QPoint(-2, 2 * c));
+}
+
+void SizeGrip::leaveEvent(QEvent *e)
+{
+ QWidget::leaveEvent(e);
+ unsetCursor();
+}
+
+void SizeGrip::checkCursor(const QPoint &p)
+{
+ if (m_pol.containsPoint(p, Qt::OddEvenFill))
+ setCursor(Qt::SizeFDiagCursor);
+ else
+ unsetCursor();
+}
diff --git a/src/plugins/scxmleditor/common/sizegrip.h b/src/plugins/scxmleditor/common/sizegrip.h
new file mode 100644
index 00000000000..82919225fa1
--- /dev/null
+++ b/src/plugins/scxmleditor/common/sizegrip.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QWidget>
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+class SizeGrip : public QWidget
+{
+public:
+ SizeGrip(QWidget *parent = nullptr);
+
+protected:
+ void resizeEvent(QResizeEvent *e) override;
+ void mousePressEvent(QMouseEvent *e) override;
+ void mouseMoveEvent(QMouseEvent *e) override;
+ void mouseReleaseEvent(QMouseEvent *e) override;
+ void paintEvent(QPaintEvent *e) override;
+ void leaveEvent(QEvent *e) override;
+
+private:
+ void checkCursor(const QPoint &p);
+
+ QPolygon m_pol;
+ QPoint m_startPoint;
+ QRect m_startRect;
+ bool m_mouseDown = false;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/stateproperties.cpp b/src/plugins/scxmleditor/common/stateproperties.cpp
new file mode 100644
index 00000000000..2741500cee5
--- /dev/null
+++ b/src/plugins/scxmleditor/common/stateproperties.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "stateproperties.h"
+#include "attributeitemdelegate.h"
+#include "attributeitemmodel.h"
+#include "scxmleditorconstants.h"
+#include "scxmluifactory.h"
+
+using namespace ScxmlEditor::PluginInterface;
+using namespace ScxmlEditor::Common;
+
+StateProperties::StateProperties(QWidget *parent)
+ : QFrame(parent)
+{
+ m_ui.setupUi(this);
+ m_ui.m_contentFrame->setVisible(false);
+ m_ui.m_currentTagName->setText(QString());
+
+ m_contentTimer.setInterval(500);
+ m_contentTimer.setSingleShot(true);
+ connect(m_ui.m_contentEdit, &QTextEdit::textChanged, &m_contentTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
+ connect(&m_contentTimer, &QTimer::timeout, this, &StateProperties::timerTimeout);
+}
+
+void StateProperties::setCurrentTagName(const QString &tagName)
+{
+ QFontMetrics fontMetrics(font());
+ m_ui.m_currentTagName->setText(fontMetrics.elidedText(tagName, Qt::ElideRight, 100));
+}
+
+void StateProperties::updateName()
+{
+ QString tagName;
+ if (m_tag) {
+ if (m_tag->hasAttribute(Constants::C_SCXMLTAG_ATTRIBUTE_ID))
+ tagName = m_tag->attribute(Constants::C_SCXMLTAG_ATTRIBUTE_ID);
+ else if (m_tag->hasAttribute(Constants::C_SCXMLTAG_ATTRIBUTE_EVENT))
+ tagName = m_tag->attribute(Constants::C_SCXMLTAG_ATTRIBUTE_EVENT);
+ else
+ tagName = m_tag->tagName();
+ }
+ setCurrentTagName(tagName);
+}
+
+void StateProperties::tagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value)
+{
+ Q_UNUSED(value)
+
+ switch (change) {
+ case ScxmlDocument::TagEditorInfoChanged:
+ case ScxmlDocument::TagAttributesChanged:
+ case ScxmlDocument::TagContentChanged:
+ if (tag != m_tag)
+ return;
+ case ScxmlDocument::TagCurrentChanged:
+ setTag(tag);
+ break;
+ default:
+ break;
+ }
+}
+
+void StateProperties::setDocument(ScxmlDocument *document)
+{
+ if (m_document)
+ disconnect(m_document, 0, this, 0);
+
+ m_document = document;
+ if (m_document) {
+ m_tag = m_document->rootTag();
+ connect(m_document, &ScxmlDocument::endTagChange, this, &StateProperties::tagChange);
+ } else {
+ setTag(0);
+ }
+}
+
+void StateProperties::setUIFactory(ScxmlUiFactory *factory)
+{
+ m_uiFactory = factory;
+ if (m_uiFactory) {
+ m_attributeModel = static_cast<AttributeItemModel*>(m_uiFactory->object("attributeItemModel"));
+ m_attributeDelegate = static_cast<AttributeItemDelegate*>(m_uiFactory->object("attributeItemDelegate"));
+
+ m_ui.m_tableView->setItemDelegate(m_attributeDelegate);
+ m_ui.m_tableView->setModel(m_attributeModel);
+ }
+}
+
+void StateProperties::setTag(ScxmlTag *tag)
+{
+ m_tag = tag;
+ m_attributeDelegate->setTag(m_tag);
+ m_attributeModel->setTag(m_tag);
+ setContentVisibility(m_tag && m_tag->info()->canIncludeContent);
+ updateName();
+}
+
+void StateProperties::timerTimeout()
+{
+ if (m_tag && m_document && m_tag->info()->canIncludeContent && m_tag->content() != m_ui.m_contentEdit->toPlainText())
+ m_document->setContent(m_tag, m_ui.m_contentEdit->toPlainText());
+}
+
+QString StateProperties::content() const
+{
+ if (m_tag && m_tag->info()->canIncludeContent)
+ return m_tag->content();
+
+ return QString();
+}
+
+void StateProperties::setContentVisibility(bool visible)
+{
+ m_ui.m_contentFrame->setVisible(visible);
+ updateContent();
+}
+
+void StateProperties::updateContent()
+{
+ if (!m_ui.m_contentEdit->hasFocus()) {
+ m_ui.m_contentEdit->blockSignals(true);
+ m_ui.m_contentEdit->setPlainText(content());
+ m_ui.m_contentEdit->blockSignals(false);
+ }
+}
diff --git a/src/plugins/scxmleditor/common/stateproperties.h b/src/plugins/scxmleditor/common/stateproperties.h
new file mode 100644
index 00000000000..90146c3f6aa
--- /dev/null
+++ b/src/plugins/scxmleditor/common/stateproperties.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmldocument.h"
+#include "scxmltag.h"
+#include "ui_stateproperties.h"
+
+#include <QFrame>
+#include <QTimer>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+class AttributeItemModel;
+class AttributeItemDelegate;
+class ScxmlUiFactory;
+} // namespace PluginInterface
+
+namespace Common {
+
+/**
+ * @brief The StateProperties class provides the view to show/edit attributes of the selected tag.
+ */
+class StateProperties : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit StateProperties(QWidget *parent = nullptr);
+
+ void setDocument(PluginInterface::ScxmlDocument *document);
+ void setUIFactory(PluginInterface::ScxmlUiFactory *factory);
+
+private:
+ void setCurrentTagName(const QString &state);
+ void tagChange(PluginInterface::ScxmlDocument::TagChange change, PluginInterface::ScxmlTag *tag, const QVariant &value);
+ void timerTimeout();
+ void setContentVisibility(bool visible);
+ void updateContent();
+ void updateName();
+ void setTag(PluginInterface::ScxmlTag *tag);
+ QString content() const;
+
+ PluginInterface::AttributeItemModel *m_attributeModel = nullptr;
+ PluginInterface::AttributeItemDelegate *m_attributeDelegate = nullptr;
+ PluginInterface::ScxmlDocument *m_document = nullptr;
+ PluginInterface::ScxmlTag *m_tag = nullptr;
+ QTimer m_contentTimer;
+ QPointer<PluginInterface::ScxmlUiFactory> m_uiFactory;
+ Ui::StateProperties m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/stateproperties.ui b/src/plugins/scxmleditor/common/stateproperties.ui
new file mode 100644
index 00000000000..4575bc1b738
--- /dev/null
+++ b/src/plugins/scxmleditor/common/stateproperties.ui
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::StateProperties</class>
+ <widget class="QFrame" name="ScxmlEditor::Common::StateProperties">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>419</width>
+ <height>383</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>55</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Frame</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <layout class="QVBoxLayout" name="mainLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="m_titleFrame">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1,0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="m_icon">
+ <property name="minimumSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="common.qrc">:/scxmleditor/images/properties.png</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_title">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Attributes</string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_currentTagName">
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="m_paneInnerFrame">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>1</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QSplitter" name="splitter">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <widget class="QTableView" name="m_tableView">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="editTriggers">
+ <set>QAbstractItemView::AllEditTriggers</set>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="gridStyle">
+ <enum>Qt::NoPen</enum>
+ </property>
+ <attribute name="horizontalHeaderMinimumSectionSize">
+ <number>40</number>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="verticalHeaderDefaultSectionSize">
+ <number>25</number>
+ </attribute>
+ <attribute name="verticalHeaderMinimumSectionSize">
+ <number>25</number>
+ </attribute>
+ </widget>
+ <widget class="QFrame" name="m_contentFrame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Content</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTextEdit" name="m_contentEdit">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="common.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/stateview.cpp b/src/plugins/scxmleditor/common/stateview.cpp
new file mode 100644
index 00000000000..9ddcde871f2
--- /dev/null
+++ b/src/plugins/scxmleditor/common/stateview.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "stateview.h"
+#include "graphicsscene.h"
+#include "scxmldocument.h"
+#include "scxmluifactory.h"
+#include "stateitem.h"
+
+using namespace ScxmlEditor::PluginInterface;
+using namespace ScxmlEditor::Common;
+
+StateView::StateView(StateItem *state, QWidget *parent)
+ : QWidget(parent)
+ , m_parentState(state)
+{
+ m_ui.setupUi(this);
+
+ m_isMainView = m_parentState == nullptr;
+
+ connect(m_ui.m_btnClose, &QPushButton::clicked, this, &StateView::closeView);
+ if (!m_isMainView)
+ m_ui.m_stateName->setText(m_parentState->itemId());
+ m_ui.m_titleFrame->setVisible(!m_isMainView);
+
+ initScene();
+}
+
+StateView::StateView(QWidget *parent)
+ : StateView(nullptr, parent)
+{
+}
+
+StateView::~StateView()
+{
+ clear();
+}
+
+void StateView::clear()
+{
+ m_scene->clearAllTags();
+ m_scene->setBlockUpdates(true);
+ m_scene->clear();
+}
+
+void StateView::initScene()
+{
+ // Init scene
+ m_scene = new GraphicsScene(this);
+ m_ui.m_graphicsView->setGraphicsScene(m_scene);
+}
+
+void StateView::closeView()
+{
+ deleteLater();
+}
+
+void StateView::setUiFactory(ScxmlUiFactory *factory)
+{
+ m_scene->setUiFactory(factory);
+ m_ui.m_graphicsView->setUiFactory(factory);
+}
+
+void StateView::setDocument(ScxmlDocument *doc)
+{
+ // Set document to scene
+ m_scene->setDocument(doc);
+ m_ui.m_graphicsView->setDocument(doc);
+ if (doc)
+ connect(doc, SIGNAL(colorThemeChanged()), m_scene, SLOT(invalidate()));
+}
+
+StateItem *StateView::parentState() const
+{
+ return m_parentState;
+}
+
+GraphicsScene *StateView::scene() const
+{
+ return m_scene;
+}
+
+GraphicsView *StateView::view() const
+{
+ return m_ui.m_graphicsView;
+}
diff --git a/src/plugins/scxmleditor/common/stateview.h b/src/plugins/scxmleditor/common/stateview.h
new file mode 100644
index 00000000000..1e02638dbc6
--- /dev/null
+++ b/src/plugins/scxmleditor/common/stateview.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_stateview.h"
+
+#include <QWidget>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+class GraphicsScene;
+class StateItem;
+class ScxmlDocument;
+class ScxmlUiFactory;
+} // namespace PluginInterface
+
+namespace Common {
+
+class StateView : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit StateView(PluginInterface::StateItem *state, QWidget *parent = nullptr);
+ explicit StateView(QWidget *parent = nullptr);
+ ~StateView() override;
+
+ PluginInterface::StateItem *parentState() const;
+ PluginInterface::GraphicsScene *scene() const;
+ GraphicsView *view() const;
+ void setUiFactory(PluginInterface::ScxmlUiFactory *uifactory);
+ void setDocument(PluginInterface::ScxmlDocument *doc);
+ void clear();
+
+private:
+ void closeView();
+ void init();
+ void initScene();
+
+ PluginInterface::StateItem *m_parentState = nullptr;
+ PluginInterface::GraphicsScene *m_scene = nullptr;
+ bool m_isMainView;
+ Ui::StateView m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/stateview.ui b/src/plugins/scxmleditor/common/stateview.ui
new file mode 100644
index 00000000000..8947ca875b2
--- /dev/null
+++ b/src/plugins/scxmleditor/common/stateview.ui
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::StateView</class>
+ <widget class="QWidget" name="ScxmlEditor::Common::StateView">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="m_titleFrame">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="m_btnClose">
+ <property name="text">
+ <string>Back</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normalon>:/scxmleditor/images/icon-undo.png</normalon>
+ </iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_stateName">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="ScxmlEditor::Common::GraphicsView" name="m_graphicsView">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>GraphicsView</class>
+ <extends>QGraphicsView</extends>
+ <header>graphicsview.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/statistics.cpp b/src/plugins/scxmleditor/common/statistics.cpp
new file mode 100644
index 00000000000..d24fb15754a
--- /dev/null
+++ b/src/plugins/scxmleditor/common/statistics.cpp
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmldocument.h"
+#include "scxmltag.h"
+#include "statistics.h"
+#include "warningmodel.h"
+
+#include <QDateTime>
+#include <QSortFilterProxyModel>
+
+using namespace ScxmlEditor::PluginInterface;
+using namespace ScxmlEditor::Common;
+
+StatisticsModel::StatisticsModel(QObject *parent)
+ : QAbstractTableModel(parent)
+{
+}
+
+void StatisticsModel::calculateStats(ScxmlTag *tag)
+{
+ // Calculate depth
+ int level = -1;
+ ScxmlTag *levelTag = tag;
+ if (levelTag->tagType() != State && levelTag->tagType() != Parallel)
+ levelTag = levelTag->parentTag();
+ while (levelTag) {
+ level++;
+ levelTag = levelTag->parentTag();
+ }
+ if (level > m_levels)
+ m_levels = level;
+
+ // Calculate statistics
+ QString tagName = tag->tagName();
+ if (m_names.contains(tagName))
+ m_counts[m_names.indexOf(tagName)]++;
+ else {
+ m_names << tagName;
+ m_counts << 1;
+ }
+
+ for (int i = 0; i < tag->childCount(); ++i)
+ calculateStats(tag->child(i));
+}
+
+void StatisticsModel::setDocument(ScxmlDocument *document)
+{
+ beginResetModel();
+ m_names.clear();
+ m_counts.clear();
+ m_levels = 0;
+
+ if (document)
+ calculateStats(document->scxmlRootTag());
+
+ endResetModel();
+}
+
+int StatisticsModel::levels() const
+{
+ return m_levels;
+}
+int StatisticsModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return m_names.count();
+}
+
+int StatisticsModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return 2;
+}
+
+QVariant StatisticsModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch (section) {
+ case 0:
+ return tr("Tag");
+ case 1:
+ return tr("Count");
+ default:
+ break;
+ }
+ }
+
+ return QVariant();
+}
+
+QVariant StatisticsModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || role != Qt::DisplayRole)
+ return QVariant();
+
+ int row = index.row();
+ if (row >= 0 && row < m_names.count()) {
+ switch (index.column()) {
+ case 0:
+ return m_names[row];
+ case 1:
+ return m_counts[row];
+ default:
+ break;
+ }
+ }
+
+ return QVariant();
+}
+
+Statistics::Statistics(QWidget *parent)
+ : QFrame(parent)
+{
+ m_ui.setupUi(this);
+
+ m_model = new StatisticsModel(this);
+
+ m_proxyModel = new QSortFilterProxyModel(this);
+ m_proxyModel->setFilterKeyColumn(-1);
+ m_proxyModel->setSourceModel(m_model);
+
+ m_ui.m_statisticsView->setModel(m_proxyModel);
+ m_ui.m_timeLabel->setText(QDateTime::currentDateTime().toString(tr("yyyy/MM/dd hh:mm:ss")));
+}
+
+void Statistics::setDocument(ScxmlDocument *doc)
+{
+ m_ui.m_fileNameLabel->setText(doc->fileName());
+ m_model->setDocument(doc);
+ m_proxyModel->invalidate();
+ m_proxyModel->sort(1, Qt::DescendingOrder);
+ m_ui.m_statisticsView->resizeColumnsToContents();
+ m_ui.m_levels->setText(QString::fromLatin1("%1").arg(m_model->levels()));
+}
diff --git a/src/plugins/scxmleditor/common/statistics.h b/src/plugins/scxmleditor/common/statistics.h
new file mode 100644
index 00000000000..605e2cad1c5
--- /dev/null
+++ b/src/plugins/scxmleditor/common/statistics.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_statistics.h"
+
+#include <QAbstractTableModel>
+#include <QFrame>
+
+QT_FORWARD_DECLARE_CLASS(QSortFilterProxyModel)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+class ScxmlDocument;
+class ScxmlTag;
+} // namespace PluginInterface
+
+namespace Common {
+
+class StatisticsModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ StatisticsModel(QObject *parent = nullptr);
+ void setDocument(PluginInterface::ScxmlDocument *document);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ int levels() const;
+
+private:
+ void calculateStats(PluginInterface::ScxmlTag *tag);
+
+ QStringList m_names;
+ QVector<int> m_counts;
+ int m_levels = 0;
+};
+
+class Statistics : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit Statistics(QWidget *parent = nullptr);
+
+ void setDocument(PluginInterface::ScxmlDocument *doc);
+
+private:
+ StatisticsModel *m_model;
+ QSortFilterProxyModel *m_proxyModel;
+ Ui::Statistics m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/statistics.ui b/src/plugins/scxmleditor/common/statistics.ui
new file mode 100644
index 00000000000..190ee9d5994
--- /dev/null
+++ b/src/plugins/scxmleditor/common/statistics.ui
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::Statistics</class>
+ <widget class="QWidget" name="ScxmlEditor::Common::Statistics">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>375</width>
+ <height>258</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>15</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>6</number>
+ </property>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Time</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="m_timeLabel">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="m_levels">
+ <property name="text">
+ <string>0</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>File</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="m_fileNameLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" colspan="2">
+ <widget class="QTableView" name="m_statisticsView">
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="verticalScrollMode">
+ <enum>QAbstractItemView::ScrollPerPixel</enum>
+ </property>
+ <property name="horizontalScrollMode">
+ <enum>QAbstractItemView::ScrollPerPixel</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <attribute name="horizontalHeaderCascadingSectionResizes">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Max. levels</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QFrame" name="frame">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>5</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>5</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/statisticsdialog.cpp b/src/plugins/scxmleditor/common/statisticsdialog.cpp
new file mode 100644
index 00000000000..00760f3ef3e
--- /dev/null
+++ b/src/plugins/scxmleditor/common/statisticsdialog.cpp
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmldocument.h"
+#include "statisticsdialog.h"
+
+using namespace ScxmlEditor::Common;
+
+StatisticsDialog::StatisticsDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ m_ui.setupUi(this);
+ setWindowTitle(tr("Document Statistics"));
+ connect(m_ui.m_okButton, &QPushButton::clicked, this, &StatisticsDialog::accept);
+}
+
+void StatisticsDialog::setDocument(ScxmlEditor::PluginInterface::ScxmlDocument *doc)
+{
+ m_ui.m_statistics->setDocument(doc);
+}
diff --git a/src/plugins/scxmleditor/common/statisticsdialog.h b/src/plugins/scxmleditor/common/statisticsdialog.h
new file mode 100644
index 00000000000..8263e1c324a
--- /dev/null
+++ b/src/plugins/scxmleditor/common/statisticsdialog.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_statisticsdialog.h"
+
+#include <QDialog>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface { class ScxmlDocument; }
+
+namespace Common {
+
+class StatisticsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit StatisticsDialog(QWidget *parent = nullptr);
+
+ void setDocument(PluginInterface::ScxmlDocument *doc);
+
+private:
+ Ui::StatisticsDialog m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/statisticsdialog.ui b/src/plugins/scxmleditor/common/statisticsdialog.ui
new file mode 100644
index 00000000000..b9fc8908613
--- /dev/null
+++ b/src/plugins/scxmleditor/common/statisticsdialog.ui
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::StatisticsDialog</class>
+ <widget class="QDialog" name="ScxmlEditor::Common::StatisticsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="ScxmlEditor::Common::Statistics" name="m_statistics">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_okButton">
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>Statistics</class>
+ <extends>QFrame</extends>
+ <header>statistics.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/structure.cpp b/src/plugins/scxmleditor/common/structure.cpp
new file mode 100644
index 00000000000..ee575ed7560
--- /dev/null
+++ b/src/plugins/scxmleditor/common/structure.cpp
@@ -0,0 +1,294 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "structure.h"
+#include "actionhandler.h"
+#include "actionprovider.h"
+#include "graphicsscene.h"
+#include "sceneutils.h"
+#include "scxmleditorconstants.h"
+#include "scxmldocument.h"
+#include "scxmlnamespace.h"
+#include "scxmltagutils.h"
+#include "scxmluifactory.h"
+#include "structuremodel.h"
+
+#include <QCheckBox>
+#include <QKeyEvent>
+#include <QLineEdit>
+#include <QRegExp>
+#include <QRegExpValidator>
+#include <QUndoStack>
+
+#include <utils/qtcassert.h>
+
+using namespace ScxmlEditor::PluginInterface;
+using namespace ScxmlEditor::Common;
+
+TreeItemDelegate::TreeItemDelegate(QObject *parent)
+ : QStyledItemDelegate(parent)
+{
+}
+
+QWidget *TreeItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ if (index.isValid()) {
+ auto edit = new QLineEdit(parent);
+ edit->setFocusPolicy(Qt::StrongFocus);
+ QRegExp rx("^(?!xml)[_a-z][a-z0-9-._]*$");
+ rx.setCaseSensitivity(Qt::CaseInsensitive);
+ edit->setValidator(new QRegExpValidator(rx, parent));
+ return edit;
+ }
+ return QStyledItemDelegate::createEditor(parent, option, index);
+}
+
+StructureSortFilterProxyModel::StructureSortFilterProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+{
+}
+
+void StructureSortFilterProxyModel::setVisibleTags(const QVector<TagType> &visibleTags)
+{
+ m_visibleTags = visibleTags;
+ if (!m_visibleTags.contains(Scxml))
+ m_visibleTags << Scxml;
+ invalidateFilter();
+}
+
+void StructureSortFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
+{
+ m_sourceModel = static_cast<StructureModel*>(sourceModel);
+ QSortFilterProxyModel::setSourceModel(sourceModel);
+}
+
+bool StructureSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ if (m_sourceModel) {
+ ScxmlTag *tag = m_sourceModel->getItem(source_parent, source_row);
+ if (tag) {
+ ScxmlNamespace *ns = tag->document()->scxmlNamespace(tag->prefix());
+ bool nsBool = (!ns) || ns->isTagVisible(tag->tagName(false));
+ return m_visibleTags.contains(tag->tagType()) && nsBool;
+ }
+ }
+
+ return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
+}
+
+Structure::Structure(QWidget *parent)
+ : QFrame(parent)
+{
+ m_ui.setupUi(this);
+
+ addCheckbox(tr("Common states"), State);
+ addCheckbox(tr("Metadata"), Metadata);
+ addCheckbox(tr("Other tags"), OnEntry);
+ addCheckbox(tr("Unknown tags"), UnknownTag);
+
+ m_ui.m_tagVisibilityFrame->setVisible(false);
+ connect(m_ui.m_checkboxButton, &QToolButton::toggled, m_ui.m_tagVisibilityFrame, &QFrame::setVisible);
+
+ m_model = new StructureModel(this);
+
+ m_proxyModel = new StructureSortFilterProxyModel(this);
+ m_proxyModel->setSourceModel(m_model);
+ m_proxyModel->setDynamicSortFilter(false);
+
+ // Default set of the visible tags
+ QVector<TagType> visibleTags;
+ for (int i = 0; i < Finalize; ++i)
+ visibleTags << (TagType)i;
+ m_proxyModel->setVisibleTags(visibleTags);
+
+ m_ui.m_structureView->setModel(m_proxyModel);
+ m_ui.m_structureView->setItemDelegate(new TreeItemDelegate(this));
+
+ connect(m_ui.m_structureView, &TreeView::pressed, this, &Structure::rowActivated);
+ connect(m_ui.m_structureView, &TreeView::rightButtonClicked, this, &Structure::showMenu);
+ connect(m_ui.m_structureView, &TreeView::entered, this, &Structure::rowEntered);
+
+ connect(m_model, &StructureModel::selectIndex, this, &Structure::currentTagChanged);
+ connect(m_model, &StructureModel::childAdded, this, &Structure::childAdded);
+}
+
+void Structure::addCheckbox(const QString &name, TagType type)
+{
+ auto box = new QCheckBox;
+ box->setText(name);
+ box->setProperty(Constants::C_SCXMLTAG_TAGTYPE, type);
+ box->setCheckable(true);
+ box->setChecked(true);
+ connect(box, &QCheckBox::clicked, this, &Structure::updateCheckBoxes);
+ m_ui.m_checkboxLayout->addWidget(box);
+ m_checkboxes << box;
+}
+
+void Structure::updateCheckBoxes()
+{
+ QVector<TagType> visibleTags;
+ foreach (QCheckBox *box, m_checkboxes) {
+ if (box->isChecked()) {
+ switch ((TagType)box->property(Constants::C_SCXMLTAG_TAGTYPE).toInt()) {
+ case State:
+ visibleTags << Initial << Final << History << State << Parallel << Transition << InitialTransition;
+ break;
+ case Metadata:
+ visibleTags << Metadata << MetadataItem;
+ break;
+ case OnEntry:
+ visibleTags << OnEntry << OnExit << Raise << If << ElseIf << Else
+ << Foreach << Log << DataModel << Data << Assign << Donedata
+ << Content << Param << Script << Send << Cancel << Invoke << Finalize;
+ break;
+ case UnknownTag:
+ visibleTags << UnknownTag;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ m_proxyModel->setVisibleTags(visibleTags);
+}
+
+void Structure::setDocument(ScxmlDocument *document)
+{
+ m_currentDocument = document;
+ m_model->setDocument(document);
+ m_proxyModel->invalidate();
+ m_ui.m_structureView->expandAll();
+}
+
+void Structure::setGraphicsScene(GraphicsScene *scene)
+{
+ m_scene = scene;
+ connect(m_ui.m_structureView, &TreeView::mouseExited, m_scene, &GraphicsScene::unhighlightAll);
+}
+
+void Structure::rowEntered(const QModelIndex &index)
+{
+ QTC_ASSERT(m_scene, return);
+
+ QModelIndex ind = m_proxyModel->mapToSource(index);
+ auto tag = static_cast<ScxmlTag*>(ind.internalPointer());
+ if (tag)
+ m_scene->highlightItems(QVector<ScxmlTag*>() << tag);
+ else
+ m_scene->unhighlightAll();
+}
+
+void Structure::rowActivated(const QModelIndex &index)
+{
+ if (m_scene)
+ m_scene->unselectAll();
+
+ if (m_currentDocument) {
+ QModelIndex ind = m_proxyModel->mapToSource(index);
+ auto tag = static_cast<ScxmlTag*>(ind.internalPointer());
+ if (tag)
+ m_currentDocument->setCurrentTag(tag);
+ }
+}
+
+void Structure::currentTagChanged(const QModelIndex &sourceIndex)
+{
+ QModelIndex ind = m_proxyModel->mapFromSource(sourceIndex);
+ if (ind.isValid())
+ m_ui.m_structureView->setCurrentIndex(ind);
+}
+
+void Structure::childAdded(const QModelIndex &childIndex)
+{
+ m_proxyModel->invalidate();
+ QModelIndex ind = m_proxyModel->mapFromSource(childIndex);
+ if (ind.isValid()) {
+ m_ui.m_structureView->setCurrentIndex(ind);
+ m_ui.m_structureView->expand(ind.parent());
+ }
+}
+
+void Structure::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key() == Qt::Key_Delete) {
+ QModelIndex ind = m_proxyModel->mapToSource(m_ui.m_structureView->currentIndex());
+ auto tag = static_cast<ScxmlTag*>(ind.internalPointer());
+ if (tag && m_currentDocument) {
+ m_currentDocument->undoStack()->beginMacro(tr("Remove items"));
+ m_currentDocument->removeTag(tag);
+ m_currentDocument->undoStack()->endMacro();
+ }
+ }
+ QFrame::keyPressEvent(e);
+}
+
+void Structure::showMenu(const QModelIndex &index, const QPoint &globalPos)
+{
+ if (index.isValid()) {
+ QModelIndex ind = m_proxyModel->mapToSource(index);
+ auto tag = static_cast<ScxmlTag*>(ind.internalPointer());
+ if (tag) {
+ auto menu = new QMenu;
+ menu->addAction(tr("Expand All"), m_ui.m_structureView, &TreeView::expandAll);
+ menu->addAction(tr("Collapse All"), m_ui.m_structureView, &TreeView::collapseAll);
+ menu->addSeparator();
+ menu->addAction(m_scene->actionHandler()->action(ActionCopy));
+ menu->addAction(m_scene->actionHandler()->action(ActionPaste));
+ menu->addSeparator();
+ ScxmlUiFactory *uiFactory = m_scene->uiFactory();
+ if (uiFactory) {
+ auto actionProvider = static_cast<ActionProvider*>(uiFactory->object(Constants::C_UI_FACTORY_OBJECT_ACTIONPROVIDER));
+ if (actionProvider) {
+ actionProvider->initStateMenu(tag, menu);
+ menu->addSeparator();
+ }
+ }
+
+ TagUtils::createChildMenu(tag, menu);
+ QAction *selectedAction = menu->exec(globalPos);
+ if (selectedAction) {
+ QVariantMap data = selectedAction->data().toMap();
+ int actionType = data.value(Constants::C_SCXMLTAG_ACTIONTYPE, -1).toInt();
+ if (actionType == TagUtils::Remove) {
+ m_currentDocument->undoStack()->beginMacro(tr("Remove items"));
+ m_currentDocument->setCurrentTag(tag);
+ m_currentDocument->removeTag(tag);
+ m_currentDocument->setCurrentTag(0);
+ m_currentDocument->undoStack()->endMacro();
+ } else if (actionType == TagUtils::AddChild) {
+ tag->document()->undoStack()->beginMacro(tr("Add child"));
+ ScxmlTag *childTag = SceneUtils::addChild(tag, data, m_scene);
+ if (childTag && childTag->tagType() <= MetadataItem)
+ m_ui.m_structureView->edit(m_ui.m_structureView->currentIndex());
+ tag->document()->undoStack()->endMacro();
+ }
+ }
+
+ m_proxyModel->invalidate();
+ menu->deleteLater();
+ }
+ }
+}
diff --git a/src/plugins/scxmleditor/common/structure.h b/src/plugins/scxmleditor/common/structure.h
new file mode 100644
index 00000000000..3bad0bed7d9
--- /dev/null
+++ b/src/plugins/scxmleditor/common/structure.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_structure.h"
+#include <QFrame>
+#include <QPointer>
+#include <QSortFilterProxyModel>
+#include <QStyledItemDelegate>
+
+QT_FORWARD_DECLARE_CLASS(QCheckBox)
+QT_FORWARD_DECLARE_CLASS(QKeyEvent)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+class ScxmlDocument;
+class GraphicsScene;
+}
+
+namespace Common {
+
+class StructureModel;
+
+class TreeItemDelegate : public QStyledItemDelegate
+{
+public:
+ TreeItemDelegate(QObject *parent = nullptr);
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+};
+
+class StructureSortFilterProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ explicit StructureSortFilterProxyModel(QObject *parent = nullptr);
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
+ void setSourceModel(QAbstractItemModel *sourceModel) override;
+ void setVisibleTags(const QVector<PluginInterface::TagType> &visibleTags);
+
+private:
+ QPointer<StructureModel> m_sourceModel;
+ QVector<PluginInterface::TagType> m_visibleTags;
+};
+
+/**
+ * @brief The Structure class provides the tree-view of the whole SCXML-document.
+ */
+class Structure : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit Structure(QWidget *parent = nullptr);
+
+ void setDocument(PluginInterface::ScxmlDocument *document);
+ void setGraphicsScene(PluginInterface::GraphicsScene *scene);
+
+protected:
+ void keyPressEvent(QKeyEvent *e) override;
+
+private:
+ void rowActivated(const QModelIndex &index);
+ void rowEntered(const QModelIndex &index);
+ void childAdded(const QModelIndex &index);
+ void currentTagChanged(const QModelIndex &sourceIndex);
+ void showMenu(const QModelIndex &index, const QPoint &globalPos);
+ void updateCheckBoxes();
+ void addCheckbox(const QString &name, PluginInterface::TagType type);
+
+ StructureSortFilterProxyModel *m_proxyModel;
+ StructureModel *m_model;
+ PluginInterface::ScxmlDocument *m_currentDocument = nullptr;
+ PluginInterface::GraphicsScene *m_scene = nullptr;
+ QVector<QCheckBox*> m_checkboxes;
+ TreeItemDelegate *m_customDelegate = nullptr;
+ Ui::Structure m_ui;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/structure.ui b/src/plugins/scxmleditor/common/structure.ui
new file mode 100644
index 00000000000..fc7c5e47453
--- /dev/null
+++ b/src/plugins/scxmleditor/common/structure.ui
@@ -0,0 +1,331 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::Common::Structure</class>
+ <widget class="QFrame" name="ScxmlEditor::Common::Structure">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>328</width>
+ <height>425</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Frame</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="m_titleFrame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="sizeIncrement">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="m_icon">
+ <property name="minimumSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>28</width>
+ <height>28</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="common.qrc">:/scxmleditor/images/structure.png</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_title">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Structure</string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_checkboxButton">
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="common.qrc">
+ <normaloff>:/scxmleditor/images/icon-filter.png</normaloff>:/scxmleditor/images/icon-filter.png</iconset>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="autoRepeat">
+ <bool>false</bool>
+ </property>
+ <property name="autoRaise">
+ <bool>true</bool>
+ </property>
+ <property name="arrowType">
+ <enum>Qt::NoArrow</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="m_paneInnerFrame">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>1</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="ScxmlEditor::Common::TreeView" name="m_structureView">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="dragEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="dragDropOverwriteMode">
+ <bool>false</bool>
+ </property>
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::DragDrop</enum>
+ </property>
+ <property name="defaultDropAction">
+ <enum>Qt::MoveAction</enum>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="indentation">
+ <number>20</number>
+ </property>
+ <attribute name="headerVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="headerMinimumSectionSize">
+ <number>50</number>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="m_tagVisibilityFrame">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>1</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="m_visibleTagsTitle">
+ <property name="text">
+ <string>Visible Tags</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QFrame" name="m_checkboxFrame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QVBoxLayout" name="m_checkboxLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <extends>QTreeView</extends>
+ <header>treeview.h</header>
+ <class>ScxmlEditor::Common::TreeView</class>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="common.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/common/structuremodel.cpp b/src/plugins/scxmleditor/common/structuremodel.cpp
new file mode 100644
index 00000000000..3618583d821
--- /dev/null
+++ b/src/plugins/scxmleditor/common/structuremodel.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "structuremodel.h"
+#include "scxmldocument.h"
+#include "scxmltag.h"
+
+#include <QMimeData>
+#include <QUndoStack>
+
+using namespace ScxmlEditor::PluginInterface;
+using namespace ScxmlEditor::Common;
+
+StructureModel::StructureModel(QObject *parent)
+ : QAbstractItemModel(parent)
+{
+ m_icons.addIcon(State, QIcon(":/scxmleditor/images/state.png"));
+ m_icons.addIcon(Parallel, QIcon(":/scxmleditor/images/parallel.png"));
+ m_icons.addIcon(Initial, QIcon(":/scxmleditor/images/initial.png"));
+ m_icons.addIcon(Final, QIcon(":/scxmleditor/images/final.png"));
+}
+
+void StructureModel::setDocument(ScxmlDocument *document)
+{
+ beginResetModel();
+ if (m_document)
+ disconnect(m_document, 0, this, 0);
+
+ m_document = document;
+ if (m_document) {
+ connect(m_document, &ScxmlDocument::beginTagChange, this, &StructureModel::beginTagChange);
+ connect(m_document, &ScxmlDocument::endTagChange, this, &StructureModel::endTagChange);
+ }
+ endResetModel();
+}
+
+int StructureModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return 1;
+}
+
+bool StructureModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (role != Qt::EditRole || value.toString().isEmpty())
+ return false;
+
+ ScxmlTag *tag = getItem(index);
+ if (tag == nullptr || tag->tagType() > MetadataItem)
+ return false;
+
+ tag->setTagName(value.toString());
+ emit dataChanged(index, index);
+ m_document->setCurrentTag(tag);
+
+ return true;
+}
+
+QVariant StructureModel::data(const QModelIndex &index, int role) const
+{
+ ScxmlTag *tag = getItem(index);
+ if (!tag)
+ return QVariant();
+
+ switch (role) {
+ case TagTypeRole:
+ return tag->tagType();
+ case Qt::DecorationRole:
+ return m_icons.icon(tag->tagType());
+ case Qt::DisplayRole: {
+ switch (tag->tagType()) {
+ case State:
+ case Parallel:
+ case Initial:
+ case Final:
+ if (tag->hasAttribute("id"))
+ return tag->attribute("id");
+ break;
+ case Transition:
+ if (tag->hasAttribute("event"))
+ return tag->attribute("event");
+ break;
+ default:
+ break;
+ }
+
+ return tag->tagName();
+ }
+ case Qt::EditRole:
+ return tag->tagName(false);
+
+ default:
+ break;
+ }
+
+ return QVariant();
+}
+
+ScxmlTag *StructureModel::getItem(const QModelIndex &parent, int source_row) const
+{
+ const ScxmlTag *tag = getItem(parent);
+ if (tag)
+ return tag->child(source_row);
+
+ return nullptr;
+}
+
+ScxmlTag *StructureModel::getItem(const QModelIndex &index) const
+{
+ if (index.isValid()) {
+ auto tag = static_cast<ScxmlTag*>(index.internalPointer());
+ if (tag)
+ return tag;
+ }
+ return m_document ? m_document->rootTag() : nullptr;
+}
+
+QModelIndex StructureModel::index(int row, int column, const QModelIndex &index) const
+{
+ if (!index.isValid() && m_document)
+ return createIndex(0, 0, m_document->rootTag());
+
+ const ScxmlTag *tag = getItem(index);
+ if (tag) {
+ ScxmlTag *childTag = tag->child(row);
+ if (childTag)
+ return createIndex(row, column, childTag);
+ }
+
+ return QModelIndex();
+}
+
+QModelIndex StructureModel::parent(const QModelIndex &index) const
+{
+ if (!m_document)
+ return QModelIndex();
+
+ if (!index.isValid())
+ return QModelIndex();
+
+ const ScxmlTag *child = getItem(index);
+ if (child != m_document->rootTag()) {
+ ScxmlTag *parentTag = child->parentTag();
+ if (parentTag)
+ return createIndex(parentTag->index(), 0, parentTag);
+ }
+
+ return QModelIndex();
+}
+
+int StructureModel::rowCount(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return m_document ? 1 : 0;
+
+ const ScxmlTag *tag = getItem(index);
+
+ if (tag)
+ return tag->childCount();
+
+ return 0;
+}
+
+Qt::DropActions StructureModel::supportedDropActions() const
+{
+ return Qt::MoveAction;
+}
+
+QStringList StructureModel::mimeTypes() const
+{
+ return QAbstractItemModel::mimeTypes();
+}
+
+QMimeData *StructureModel::mimeData(const QModelIndexList &indexes) const
+{
+ if (indexes.count() == 1)
+ m_dragTag = getItem(indexes.first());
+
+ return QAbstractItemModel::mimeData(indexes);
+}
+
+bool StructureModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &p) const
+{
+ Q_UNUSED(data)
+ Q_UNUSED(action)
+ Q_UNUSED(row)
+ Q_UNUSED(column)
+
+ const ScxmlTag *tag = getItem(p);
+ return tag && m_dragTag && (tag->tagType() == State || tag->tagType() == Parallel || tag->tagType() == Scxml);
+}
+
+bool StructureModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &p)
+{
+ Q_UNUSED(data)
+ Q_UNUSED(action)
+ Q_UNUSED(row)
+ Q_UNUSED(column)
+
+ ScxmlTag *tag = getItem(p);
+ if (tag && m_dragTag && tag != m_dragTag && (tag->tagType() == State || tag->tagType() == Parallel || tag->tagType() == Scxml)) {
+ m_document->undoStack()->beginMacro(tr("Change parent"));
+ m_document->changeParent(m_dragTag, tag);
+ m_document->undoStack()->endMacro();
+ m_dragTag = nullptr;
+ return true;
+ }
+
+ m_dragTag = nullptr;
+ return false;
+}
+
+Qt::ItemFlags StructureModel::flags(const QModelIndex &index) const
+{
+ Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
+
+ const ScxmlTag *tag = getItem(index);
+ if (index.isValid() && tag) {
+ switch (tag->tagType()) {
+ case State:
+ case Parallel:
+ case Initial:
+ case Final:
+ case History:
+ defaultFlags |= Qt::ItemIsDragEnabled;
+ case Scxml:
+ defaultFlags |= Qt::ItemIsDropEnabled;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (tag) {
+ if (tag->tagType() == UnknownTag || tag->tagType() == MetadataItem)
+ defaultFlags |= Qt::ItemIsEditable;
+ }
+
+ return defaultFlags;
+}
+
+void StructureModel::updateData()
+{
+ emit dataChanged(QModelIndex(), QModelIndex());
+}
+
+void StructureModel::beginTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value)
+{
+ switch (change) {
+ case ScxmlDocument::TagAddChild:
+ case ScxmlDocument::TagChangeParentAddChild:
+ beginInsertRows(createIndex(tag->index(), 0, tag), value.toInt(), value.toInt());
+ break;
+ case ScxmlDocument::TagRemoveChild:
+ case ScxmlDocument::TagChangeParentRemoveChild:
+ beginRemoveRows(createIndex(tag->index(), 0, tag), value.toInt(), value.toInt());
+ break;
+ case ScxmlDocument::TagChangeOrder: {
+ int r1 = tag->index();
+ int r2 = value.toInt();
+ if (r2 > r1)
+ r2++;
+
+ QModelIndex ind = createIndex(r1, 0, tag);
+ beginMoveRows(ind, r1, r1, ind, r2);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void StructureModel::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value)
+{
+ switch (change) {
+ case ScxmlDocument::TagAttributesChanged: {
+ emit dataChanged(QModelIndex(), QModelIndex());
+ break;
+ }
+ case ScxmlDocument::TagAddChild:
+ case ScxmlDocument::TagChangeParentAddChild: {
+ endInsertRows();
+ emit childAdded(createIndex(0, 0, tag->child(value.toInt())));
+ break;
+ }
+ case ScxmlDocument::TagChangeParentRemoveChild: {
+ endRemoveRows();
+ break;
+ }
+ case ScxmlDocument::TagRemoveChild: {
+ endRemoveRows();
+ break;
+ }
+ case ScxmlDocument::TagChangeOrder: {
+ endMoveRows();
+ break;
+ }
+ case ScxmlDocument::TagCurrentChanged: {
+ if (tag)
+ emit selectIndex(createIndex(tag->index(), 0, tag));
+ break;
+ }
+ default:
+ break;
+ }
+}
diff --git a/src/plugins/scxmleditor/common/structuremodel.h b/src/plugins/scxmleditor/common/structuremodel.h
new file mode 100644
index 00000000000..03545676ef7
--- /dev/null
+++ b/src/plugins/scxmleditor/common/structuremodel.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmldocument.h"
+#include "scxmltypes.h"
+
+#include <QAbstractItemModel>
+#include <QIcon>
+#include <QPointer>
+
+QT_FORWARD_DECLARE_CLASS(QModelIndex)
+QT_FORWARD_DECLARE_CLASS(QVariant)
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+class Icons
+{
+public:
+ void addIcon(PluginInterface::TagType type, const QIcon &icon)
+ {
+ m_iconIndices << type;
+ m_icons << icon;
+ }
+
+ QIcon icon(PluginInterface::TagType type) const
+ {
+ int ind = m_iconIndices.indexOf(type);
+ if (ind >= 0 && ind < m_icons.count())
+ return m_icons[ind];
+
+ return m_emptyIcon;
+ }
+
+private:
+ QIcon m_emptyIcon;
+ QVector<PluginInterface::TagType> m_iconIndices;
+ QVector<QIcon> m_icons;
+};
+
+class StructureModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ StructureModel(QObject *parent = nullptr);
+
+ enum StructureItemDataRole {
+ TagTypeRole = Qt::UserRole + 1
+ };
+
+ QVariant data(const QModelIndex &index, int role) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex &index) const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ PluginInterface::ScxmlTag *getItem(const QModelIndex &parent, int source_row) const;
+ PluginInterface::ScxmlTag *getItem(const QModelIndex &index) const;
+
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ Qt::DropActions supportedDropActions() const override;
+ QStringList mimeTypes() const override;
+ QMimeData *mimeData(const QModelIndexList &indexes) const override;
+ bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override;
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
+ void updateData();
+ void setDocument(PluginInterface::ScxmlDocument *document);
+
+signals:
+ void childAdded(const QModelIndex &childIndex);
+ void selectIndex(const QModelIndex &index);
+
+private:
+ void beginTagChange(PluginInterface::ScxmlDocument::TagChange change, PluginInterface::ScxmlTag *tag, const QVariant &value);
+ void endTagChange(PluginInterface::ScxmlDocument::TagChange change, PluginInterface::ScxmlTag *tag, const QVariant &value);
+
+ QPointer<PluginInterface::ScxmlDocument> m_document;
+ Icons m_icons;
+ mutable QPointer<PluginInterface::ScxmlTag> m_dragTag;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/common/treeview.cpp b/src/plugins/scxmleditor/common/treeview.cpp
new file mode 100644
index 00000000000..28bbe4fb2d8
--- /dev/null
+++ b/src/plugins/scxmleditor/common/treeview.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "treeview.h"
+#include "structuremodel.h"
+
+#include <QMimeData>
+#include <QMouseEvent>
+
+using namespace ScxmlEditor::Common;
+
+TreeView::TreeView(QWidget *parent)
+ : QTreeView(parent)
+{
+ setDragEnabled(true);
+ setAcceptDrops(true);
+ setDropIndicatorShown(true);
+ setDragDropMode(QAbstractItemView::DragDrop);
+ setMouseTracking(true);
+}
+
+void TreeView::leaveEvent(QEvent *e)
+{
+ emit mouseExited();
+ QTreeView::leaveEvent(e);
+}
+
+void TreeView::currentChanged(const QModelIndex &index, const QModelIndex &prev)
+{
+ QTreeView::currentChanged(index, prev);
+ emit currentIndexChanged(index);
+}
+
+void TreeView::mousePressEvent(QMouseEvent *event)
+{
+ QTreeView::mousePressEvent(event);
+ if (event->button() == Qt::RightButton)
+ emit rightButtonClicked(currentIndex(), event->globalPos());
+}
diff --git a/src/plugins/scxmleditor/common/treeview.h b/src/plugins/scxmleditor/common/treeview.h
new file mode 100644
index 00000000000..0feab991f62
--- /dev/null
+++ b/src/plugins/scxmleditor/common/treeview.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmltypes.h"
+
+#include <QTreeView>
+
+QT_FORWARD_DECLARE_CLASS(QEvent)
+QT_FORWARD_DECLARE_CLASS(QMouseEvent)
+
+namespace ScxmlEditor {
+
+namespace Common {
+
+/**
+ * @brief The TreeView class provides a default model/view implementation of a tree view.
+ *
+ * It extend the normal QTreeView by adding the signals mouseExited, currentIndexChanged and rightButtonClicked.
+ */
+class TreeView : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ TreeView(QWidget *parent = nullptr);
+
+signals:
+ void mouseExited();
+ void currentIndexChanged(const QModelIndex &index);
+ void rightButtonClicked(const QModelIndex &index, const QPoint &globalPos);
+
+protected:
+ void leaveEvent(QEvent *e) override;
+ void currentChanged(const QModelIndex &index, const QModelIndex &prev) override;
+ void mousePressEvent(QMouseEvent *event) override;
+};
+
+} // namespace Common
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/outputpane/errorwidget.cpp b/src/plugins/scxmleditor/outputpane/errorwidget.cpp
new file mode 100644
index 00000000000..00949426d75
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/errorwidget.cpp
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "errorwidget.h"
+#include "scxmleditorconstants.h"
+
+#include <QFile>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QSortFilterProxyModel>
+#include <QTextStream>
+
+#include <coreplugin/icore.h>
+#include <utils/utilsicons.h>
+
+using namespace ScxmlEditor::OutputPane;
+
+ErrorWidget::ErrorWidget(QWidget *parent)
+ : OutputPane(parent)
+ , m_warningModel(new WarningModel(this))
+ , m_proxyModel(new QSortFilterProxyModel(this))
+{
+ m_ui.setupUi(this);
+
+ m_ui.m_clean->setIcon(Utils::Icons::CLEAN.icon());
+ m_ui.m_exportWarnings->setIcon(Utils::Icons::SAVEFILE.icon());
+ m_ui.m_showErrors->setIcon(Utils::Icons::ERROR.icon());
+ m_ui.m_showWarnings->setIcon(Utils::Icons::WARNING.icon());
+ m_ui.m_showInfos->setIcon(Utils::Icons::INFO.icon());
+
+ m_proxyModel->setFilterRole(WarningModel::FilterRole);
+ m_proxyModel->setSourceModel(m_warningModel);
+ m_proxyModel->setFilterFixedString(Constants::C_WARNINGMODEL_FILTER_ACTIVE);
+
+ m_ui.m_errorsTable->setModel(m_proxyModel);
+ m_ui.m_errorsTable->horizontalHeader()->setSectionsMovable(true);
+
+ connect(m_ui.m_errorsTable, &TableView::entered, [this](const QModelIndex &ind) {
+ if (ind.isValid())
+ emit warningEntered(m_warningModel->getWarning(m_proxyModel->mapToSource(ind)));
+ });
+
+ connect(m_ui.m_errorsTable, &TableView::pressed, [this](const QModelIndex &ind) {
+ if (ind.isValid())
+ emit warningSelected(m_warningModel->getWarning(m_proxyModel->mapToSource(ind)));
+ });
+
+ connect(m_ui.m_errorsTable, &TableView::doubleClicked, [this](const QModelIndex &ind) {
+ if (ind.isValid())
+ emit warningDoubleClicked(m_warningModel->getWarning(m_proxyModel->mapToSource(ind)));
+ });
+
+ connect(m_ui.m_errorsTable, &TableView::mouseExited, this, [this](){
+ emit mouseExited();
+ });
+
+ connect(m_ui.m_showErrors, &QToolButton::toggled, [this](bool show) {
+ m_warningModel->setShowWarnings(Warning::ErrorType, show);
+ });
+
+ connect(m_ui.m_showWarnings, &QToolButton::toggled, [this](bool show) {
+ m_warningModel->setShowWarnings(Warning::WarningType, show);
+ });
+
+ connect(m_ui.m_showInfos, &QToolButton::toggled, [this](bool show) {
+ m_warningModel->setShowWarnings(Warning::InfoType, show);
+ });
+
+ connect(m_ui.m_clean, &QToolButton::clicked, m_warningModel, &WarningModel::clear);
+ connect(m_ui.m_exportWarnings, &QToolButton::clicked, this, &ErrorWidget::exportWarnings);
+ connect(m_warningModel, &WarningModel::warningsChanged, this, &ErrorWidget::updateWarnings);
+ connect(m_warningModel, &WarningModel::countChanged, this, &ErrorWidget::warningCountChanged);
+
+ const QSettings *s = Core::ICore::settings();
+ m_ui.m_errorsTable->restoreGeometry(s->value(Constants::C_SETTINGS_ERRORPANE_GEOMETRY).toByteArray());
+ m_ui.m_showErrors->setChecked(s->value(Constants::C_SETTINGS_ERRORPANE_SHOWERRORS, true).toBool());
+ m_ui.m_showWarnings->setChecked(s->value(Constants::C_SETTINGS_ERRORPANE_SHOWWARNINGS, true).toBool());
+ m_ui.m_showInfos->setChecked(s->value(Constants::C_SETTINGS_ERRORPANE_SHOWINFOS, true).toBool());
+
+ updateWarnings();
+}
+
+ErrorWidget::~ErrorWidget()
+{
+ QSettings *s = Core::ICore::settings();
+ s->setValue(Constants::C_SETTINGS_ERRORPANE_GEOMETRY, m_ui.m_errorsTable->saveGeometry());
+ s->setValue(Constants::C_SETTINGS_ERRORPANE_SHOWERRORS, m_ui.m_showErrors->isChecked());
+ s->setValue(Constants::C_SETTINGS_ERRORPANE_SHOWWARNINGS, m_ui.m_showWarnings->isChecked());
+ s->setValue(Constants::C_SETTINGS_ERRORPANE_SHOWINFOS, m_ui.m_showInfos->isChecked());
+}
+
+void ErrorWidget::setPaneFocus()
+{
+}
+
+void ErrorWidget::updateWarnings()
+{
+ int errorCount = m_warningModel->count(Warning::ErrorType);
+ int warningCount = m_warningModel->count(Warning::WarningType);
+ int infoCount = m_warningModel->count(Warning::InfoType);
+
+ m_title = tr("Errors(%1) / Warnings(%2) / Info(%3)").arg(errorCount).arg(warningCount).arg(infoCount);
+ if (errorCount > 0)
+ m_icon = m_ui.m_showInfos->icon();
+ else if (warningCount > 0)
+ m_icon = m_ui.m_showWarnings->icon();
+ else if (infoCount > 0)
+ m_icon = m_ui.m_showErrors->icon();
+ else
+ m_icon = QIcon();
+
+ emit titleChanged();
+ emit iconChanged();
+}
+
+QColor ErrorWidget::alertColor() const
+{
+ if (m_warningModel->count(Warning::ErrorType) > 0)
+ return QColor(0xff, 0x77, 0x77);
+ else if (m_warningModel->count(Warning::WarningType))
+ return QColor(0xfd, 0x88, 0x21);
+ else
+ return QColor(0x29, 0xb6, 0xff);
+}
+
+void ErrorWidget::warningCountChanged(int c)
+{
+ if (c > 0)
+ emit dataChanged();
+}
+
+QString ErrorWidget::modifyExportedValue(const QString &val)
+{
+ QString value(val);
+
+ if (value.contains(",") || value.startsWith(" ") || value.endsWith(" "))
+ value = QString::fromLatin1("\"%1\"").arg(value);
+
+ return value;
+}
+
+void ErrorWidget::exportWarnings()
+{
+ QString fileName = QFileDialog::getSaveFileName(this, tr("Export To File"), QString(), tr("CSV files (*.csv)"));
+ if (fileName.isEmpty())
+ return;
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ QMessageBox::warning(this, tr("Export Failed"), tr("Cannot open file %.").arg(fileName));
+ file.close();
+ return;
+ }
+
+ QTextStream out(&file);
+
+ // Headerdata
+ QStringList values;
+ for (int c = 0; c < m_proxyModel->columnCount(); ++c)
+ values << modifyExportedValue(m_proxyModel->headerData(m_ui.m_errorsTable->horizontalHeader()->visualIndex(c), Qt::Horizontal, Qt::DisplayRole).toString());
+ out << values.join(",") << "\n";
+
+ for (int r = 0; r < m_proxyModel->rowCount(); ++r) {
+ values.clear();
+ for (int c = 0; c < m_proxyModel->columnCount(); ++c)
+ values << modifyExportedValue(m_proxyModel->data(m_proxyModel->index(r, m_ui.m_errorsTable->horizontalHeader()->visualIndex(c)), Qt::DisplayRole).toString());
+ out << values.join(",") << "\n";
+ }
+}
diff --git a/src/plugins/scxmleditor/outputpane/errorwidget.h b/src/plugins/scxmleditor/outputpane/errorwidget.h
new file mode 100644
index 00000000000..e16f475142d
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/errorwidget.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "outputpane.h"
+#include "ui_errorwidget.h"
+#include "warningmodel.h"
+
+QT_FORWARD_DECLARE_CLASS(QSortFilterProxyModel)
+
+namespace ScxmlEditor {
+
+namespace OutputPane {
+
+class ErrorWidget : public OutputPane
+{
+ Q_OBJECT
+
+public:
+ explicit ErrorWidget(QWidget *parent = nullptr);
+ ~ErrorWidget() override;
+
+ WarningModel *warningModel() const
+ {
+ return m_warningModel;
+ }
+
+ QString title() const override
+ {
+ return m_title;
+ }
+
+ QIcon icon() const override
+ {
+ return m_icon;
+ }
+
+ QColor alertColor() const override;
+ void setPaneFocus() override;
+
+signals:
+ void mouseExited();
+ void warningEntered(Warning *w);
+ void warningSelected(Warning *w);
+ void warningDoubleClicked(Warning *w);
+
+private:
+ void updateWarnings();
+ void exportWarnings();
+ void warningCountChanged(int c);
+ QString modifyExportedValue(const QString &val);
+
+ QIcon m_icon;
+ QString m_title;
+ WarningModel *m_warningModel;
+ QSortFilterProxyModel *m_proxyModel;
+ Ui::ErrorWidget m_ui;
+};
+
+} // namespace OutputPane
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/outputpane/errorwidget.ui b/src/plugins/scxmleditor/outputpane/errorwidget.ui
new file mode 100644
index 00000000000..1c054ff12d8
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/errorwidget.ui
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::OutputPane::ErrorWidget</class>
+ <widget class="QWidget" name="ScxmlEditor::OutputPane::ErrorWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>857</width>
+ <height>326</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="m_subTitleFrame">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QToolButton" name="m_clean"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_exportWarnings"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_showErrors">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_showWarnings">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_showInfos">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>646</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="ScxmlEditor::OutputPane::TableView" name="m_errorsTable">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="textElideMode">
+ <enum>Qt::ElideRight</enum>
+ </property>
+ <property name="verticalScrollMode">
+ <enum>QAbstractItemView::ScrollPerPixel</enum>
+ </property>
+ <property name="horizontalScrollMode">
+ <enum>QAbstractItemView::ScrollPerPixel</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="verticalHeaderCascadingSectionResizes">
+ <bool>true</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ScxmlEditor::OutputPane::TableView</class>
+ <extends>QTableView</extends>
+ <header>tableview.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/outputpane/outputpane.h b/src/plugins/scxmleditor/outputpane/outputpane.h
new file mode 100644
index 00000000000..a11b20e8680
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/outputpane.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QFrame>
+
+namespace ScxmlEditor {
+
+namespace OutputPane {
+
+class OutputPane : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit OutputPane(QWidget *parent = nullptr)
+ : QFrame(parent)
+ {
+ }
+
+ virtual QString title() const = 0;
+ virtual QIcon icon() const = 0;
+ virtual void setPaneFocus() = 0;
+ virtual QColor alertColor() const
+ {
+ return QColor(Qt::red);
+ }
+
+signals:
+ void iconChanged();
+ void titleChanged();
+ void dataChanged();
+};
+
+} // namespace OutputPane
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/outputpane/outputpane.pri b/src/plugins/scxmleditor/outputpane/outputpane.pri
new file mode 100644
index 00000000000..1e024a20aa5
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/outputpane.pri
@@ -0,0 +1,20 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/tableview.h \
+ $$PWD/errorwidget.h \
+ $$PWD/outputtabwidget.h \
+ $$PWD/outputpane.h \
+ $$PWD/warningmodel.h \
+ $$PWD/warning.h
+
+SOURCES += \
+ $$PWD/tableview.cpp \
+ $$PWD/errorwidget.cpp \
+ $$PWD/outputtabwidget.cpp \
+ $$PWD/warningmodel.cpp \
+ $$PWD/warning.cpp
+
+FORMS += \
+ $$PWD/errorwidget.ui \
+ $$PWD/outputtabwidget.ui
diff --git a/src/plugins/scxmleditor/outputpane/outputtabwidget.cpp b/src/plugins/scxmleditor/outputpane/outputtabwidget.cpp
new file mode 100644
index 00000000000..a1e76b0583b
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/outputtabwidget.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "outputtabwidget.h"
+#include "outputpane.h"
+
+#include <utils/qtcassert.h>
+
+#include <QPainter>
+
+using namespace ScxmlEditor::OutputPane;
+
+PanePushButton::PanePushButton(OutputPane *pane, QWidget *parent)
+ : QPushButton(parent)
+{
+ animator.setPropertyName("colorOpacity");
+ animator.setTargetObject(this);
+
+ setObjectName("PanePushButton");
+ setCheckable(true);
+
+ setText(pane->title());
+ setIcon(pane->icon());
+
+ connect(this, &PanePushButton::toggled, this, [this](bool toggled) {
+ if (toggled)
+ stopAlert();
+ });
+
+ connect(&animator, &QAbstractAnimation::finished, this, [this]() {
+ m_animCounter++;
+ if (m_animCounter < 8) {
+ if (m_animCounter % 2 == 1)
+ fadeOut();
+ else
+ fadeIn();
+ }
+ });
+
+ connect(pane, &OutputPane::titleChanged, this, [=]() {
+ setText(pane->title());
+ });
+
+ connect(pane, &OutputPane::iconChanged, this, [=]() {
+ setIcon(pane->icon());
+ });
+}
+
+void PanePushButton::startAlert(const QColor &color)
+{
+ m_color = color;
+ m_animCounter = 0;
+ fadeIn();
+}
+
+void PanePushButton::stopAlert()
+{
+ animator.stop();
+}
+
+void PanePushButton::fadeIn()
+{
+ animator.stop();
+ animator.setDuration(300);
+ animator.setStartValue(0);
+ animator.setEndValue(80);
+ animator.start();
+}
+
+void PanePushButton::fadeOut()
+{
+ animator.stop();
+ animator.setDuration(300);
+ animator.setStartValue(80);
+ animator.setEndValue(0);
+ animator.start();
+}
+
+void PanePushButton::setColorOpacity(int value)
+{
+ m_colorOpacity = value;
+ update();
+}
+
+void PanePushButton::paintEvent(QPaintEvent *e)
+{
+ QPushButton::paintEvent(e);
+
+ QPainter p(this);
+ p.save();
+ if (animator.state() != QAbstractAnimation::Stopped) {
+ QRect r = rect();
+ m_color.setAlpha(m_colorOpacity);
+ p.setBrush(QBrush(m_color));
+ p.setPen(Qt::NoPen);
+ p.drawRect(r);
+ }
+ p.restore();
+}
+
+OutputTabWidget::OutputTabWidget(QWidget *parent)
+ : QFrame(parent)
+{
+ m_ui.setupUi(this);
+ close();
+}
+
+OutputTabWidget::~OutputTabWidget()
+{
+}
+
+int OutputTabWidget::addPane(OutputPane *pane)
+{
+ if (pane) {
+ auto button = new PanePushButton(pane, this);
+ connect(button, &PanePushButton::clicked, this, &OutputTabWidget::buttonClicked);
+ connect(pane, &OutputPane::dataChanged, this, &OutputTabWidget::showAlert);
+
+ m_ui.m_buttonLayout->addWidget(button);
+ m_ui.m_stackedWidget->addWidget(pane);
+
+ m_buttons << button;
+ m_pages << pane;
+
+ return m_pages.count() - 1;
+ }
+
+ return -1;
+}
+
+void OutputTabWidget::showPane(OutputPane *pane)
+{
+ QTC_ASSERT(pane, return);
+
+ m_ui.m_stackedWidget->setCurrentWidget(pane);
+ m_buttons[m_pages.indexOf(pane)]->setChecked(true);
+ pane->setPaneFocus();
+ if (!m_ui.m_stackedWidget->isVisible()) {
+ m_ui.m_stackedWidget->setVisible(true);
+ emit visibilityChanged(true);
+ }
+}
+
+void OutputTabWidget::showPane(int index)
+{
+ showPane(static_cast<OutputPane*>(m_ui.m_stackedWidget->widget(index)));
+}
+
+void OutputTabWidget::close()
+{
+ m_ui.m_stackedWidget->setVisible(false);
+ emit visibilityChanged(false);
+}
+
+void OutputTabWidget::showAlert()
+{
+ int index = m_pages.indexOf(qobject_cast<OutputPane*>(sender()));
+ if (index >= 0 && !m_buttons[index]->isChecked())
+ m_buttons[index]->startAlert(m_pages[index]->alertColor());
+}
+
+void OutputTabWidget::buttonClicked(bool para)
+{
+ int index = m_buttons.indexOf(qobject_cast<PanePushButton*>(sender()));
+ if (index >= 0) {
+ if (para) {
+ for (int i = 0; i < m_buttons.count(); ++i) {
+ if (i != index)
+ m_buttons[i]->setChecked(false);
+ }
+ showPane(index);
+ } else
+ close();
+ }
+}
diff --git a/src/plugins/scxmleditor/outputpane/outputtabwidget.h b/src/plugins/scxmleditor/outputpane/outputtabwidget.h
new file mode 100644
index 00000000000..fd77a038086
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/outputtabwidget.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "ui_outputtabwidget.h"
+
+#include <QFrame>
+#include <QPropertyAnimation>
+#include <QPushButton>
+
+QT_FORWARD_DECLARE_CLASS(QPaintEvent)
+
+namespace ScxmlEditor {
+
+namespace OutputPane {
+
+class OutputPane;
+
+class PanePushButton : public QPushButton
+{
+ Q_OBJECT
+ Q_PROPERTY(int colorOpacity READ colorOpacity WRITE setColorOpacity)
+
+public:
+ PanePushButton(OutputPane *pane, QWidget *parent = nullptr);
+
+ int colorOpacity()
+ {
+ return m_colorOpacity;
+ }
+
+ void setColorOpacity(int value);
+ void startAlert(const QColor &color = QColor(Qt::red));
+ void stopAlert();
+
+protected:
+ void paintEvent(QPaintEvent *e) override;
+
+private:
+ void fadeIn();
+ void fadeOut();
+
+ QPropertyAnimation animator;
+ int m_colorOpacity;
+ QColor m_color;
+ int m_animCounter;
+};
+
+class OutputTabWidget : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit OutputTabWidget(QWidget *parent = nullptr);
+ ~OutputTabWidget();
+
+ int addPane(OutputPane *pane);
+ void showPane(OutputPane *pane);
+ void showPane(int index);
+
+signals:
+ void visibilityChanged(bool visible);
+
+private:
+ void close();
+ void buttonClicked(bool para);
+ void showAlert();
+
+ QVector<OutputPane*> m_pages;
+ QVector<PanePushButton*> m_buttons;
+ Ui::OutputTabWidget m_ui;
+};
+
+} // namespace OutputPane
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/outputpane/outputtabwidget.ui b/src/plugins/scxmleditor/outputpane/outputtabwidget.ui
new file mode 100644
index 00000000000..26a631e6961
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/outputtabwidget.ui
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScxmlEditor::OutputPane::OutputTabWidget</class>
+ <widget class="QWidget" name="ScxmlEditor::OutputPane::OutputTabWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>220</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="m_titleFrame">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="m_buttonWidget" native="true">
+ <layout class="QHBoxLayout" name="m_buttonLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>395</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QStackedWidget" name="m_stackedWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/scxmleditor/outputpane/tableview.cpp b/src/plugins/scxmleditor/outputpane/tableview.cpp
new file mode 100644
index 00000000000..7dacff6bedf
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/tableview.cpp
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "tableview.h"
+
+using namespace ScxmlEditor::OutputPane;
+
+TableView::TableView(QWidget *parent)
+ : QTableView(parent)
+{
+ setMouseTracking(true);
+}
+
+void TableView::leaveEvent(QEvent *e)
+{
+ emit mouseExited();
+ QTableView::leaveEvent(e);
+}
diff --git a/src/plugins/scxmleditor/outputpane/tableview.h b/src/plugins/scxmleditor/outputpane/tableview.h
new file mode 100644
index 00000000000..223ce4df0dd
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/tableview.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QTableView>
+
+namespace ScxmlEditor {
+
+namespace OutputPane {
+
+class TableView : public QTableView
+{
+ Q_OBJECT
+
+public:
+ TableView(QWidget *parent = nullptr);
+
+signals:
+ void mouseExited();
+
+protected:
+ void leaveEvent(QEvent *e) override;
+};
+
+} // namespace OutputPane
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/outputpane/warning.cpp b/src/plugins/scxmleditor/outputpane/warning.cpp
new file mode 100644
index 00000000000..26942338ec6
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/warning.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "warning.h"
+
+using namespace ScxmlEditor::OutputPane;
+
+Warning::Warning(Severity severity, const QString &name, const QString &reason,
+ const QString &description, bool active, QObject *parent)
+ : QObject(parent)
+ , m_severity(severity)
+ , m_typeName(name)
+ , m_reason(reason)
+ , m_description(description)
+ , m_active(active)
+{
+}
+
+void Warning::setSeverity(Warning::Severity sev)
+{
+ if (m_severity != sev) {
+ m_severity = sev;
+ emit dataChanged();
+ }
+}
+void Warning::setTypeName(const QString &name)
+{
+ if (m_typeName != name) {
+ m_typeName = name;
+ emit dataChanged();
+ }
+}
+void Warning::setReason(const QString &reason)
+{
+ if (m_reason != reason) {
+ m_reason = reason;
+ emit dataChanged();
+ }
+}
+void Warning::setDescription(const QString &description)
+{
+ if (m_description != description) {
+ m_description = description;
+ emit dataChanged();
+ }
+}
+void Warning::setActive(bool active)
+{
+ if (m_active != active) {
+ m_active = active;
+ emit dataChanged();
+ }
+}
diff --git a/src/plugins/scxmleditor/outputpane/warning.h b/src/plugins/scxmleditor/outputpane/warning.h
new file mode 100644
index 00000000000..ed668301108
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/warning.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QObject>
+#include <QString>
+
+namespace ScxmlEditor {
+
+namespace OutputPane {
+
+class Warning : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Severity {
+ ErrorType = 0,
+ WarningType,
+ InfoType
+ };
+
+ explicit Warning(Severity severity, const QString &typeName,
+ const QString &reason, const QString &description,
+ bool active, QObject *parent = nullptr);
+
+ Severity severity() const
+ {
+ return m_severity;
+ }
+
+ QString typeName() const
+ {
+ return m_typeName;
+ }
+
+ QString reason() const
+ {
+ return m_reason;
+ }
+
+ QString description() const
+ {
+ return m_description;
+ }
+
+ bool isActive() const
+ {
+ return m_active;
+ }
+
+ void setSeverity(Severity sev);
+ void setTypeName(const QString &name);
+ void setReason(const QString &reason);
+ void setDescription(const QString &description);
+ void setActive(bool active);
+
+signals:
+ void dataChanged();
+
+private:
+ Severity m_severity;
+ QString m_typeName;
+ QString m_reason;
+ QString m_description;
+ bool m_active;
+};
+
+} // namespace OutputPane
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/outputpane/warningmodel.cpp b/src/plugins/scxmleditor/outputpane/warningmodel.cpp
new file mode 100644
index 00000000000..23a406a3230
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/warningmodel.cpp
@@ -0,0 +1,252 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmleditorconstants.h"
+#include "warningmodel.h"
+
+#include <utils/algorithm.h>
+
+#include <QTimer>
+
+using namespace ScxmlEditor::OutputPane;
+
+WarningModel::WarningModel(QObject *parent)
+ : QAbstractTableModel(parent)
+{
+ m_countChecker = new QTimer(this);
+ m_countChecker->setInterval(500);
+ m_countChecker->setSingleShot(true);
+ connect(m_countChecker.data(), &QTimer::timeout, this, [this]() {
+ if (m_warnings.count() != m_oldCount) {
+ m_oldCount = m_warnings.count();
+ emit countChanged(m_oldCount);
+ }
+ });
+}
+
+WarningModel::~WarningModel()
+{
+ delete m_countChecker;
+
+ // Clear warnings
+ clear(false);
+}
+
+void WarningModel::clear(bool sendSignal)
+{
+ emit modelAboutToBeClear();
+
+ foreach (Warning *w, m_warnings)
+ w->disconnect(this);
+
+ beginResetModel();
+ qDeleteAll(m_warnings);
+ m_warnings.clear();
+ endResetModel();
+
+ if (m_countChecker)
+ m_countChecker->start();
+
+ if (sendSignal) {
+ emit warningsChanged();
+ emit modelCleared();
+ }
+}
+
+int WarningModel::count(Warning::Severity type) const
+{
+ return Utils::count(m_warnings, [type](Warning *w) {
+ return w->severity() == type;
+ });
+}
+
+int WarningModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return m_warnings.count();
+}
+
+int WarningModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return 4;
+}
+
+QVariant WarningModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch (section) {
+ case 0:
+ return tr("Severity");
+ case 1:
+ return tr("Type");
+ case 2:
+ return tr("Reason");
+ case 3:
+ return tr("Description");
+ default:
+ break;
+ }
+ }
+
+ return QVariant();
+}
+
+QString WarningModel::severityName(Warning::Severity severity) const
+{
+ switch (severity) {
+ case Warning::ErrorType:
+ return tr("Error");
+ case Warning::WarningType:
+ return tr("Warning");
+ case Warning::InfoType:
+ return tr("Info");
+ default:
+ return tr("Unknown");
+ }
+}
+
+QIcon WarningModel::severityIcon(Warning::Severity severity) const
+{
+ switch (severity) {
+ case Warning::ErrorType: {
+ static const QIcon errorIcon(":/scxmleditor/images/error.png");
+ return errorIcon;
+ }
+ case Warning::WarningType: {
+ static const QIcon warningIcon(":/scxmleditor/images/warning.png");
+ return warningIcon;
+ }
+ case Warning::InfoType: {
+ static const QIcon infoIcon(":/scxmleditor/images/warning_low.png");
+ return infoIcon;
+ }
+ default:
+ return QIcon();
+ }
+}
+
+QVariant WarningModel::data(const QModelIndex &index, int role) const
+{
+ int row = index.row();
+ int col = index.column();
+
+ if (index.isValid() && row >= 0 && row < m_warnings.count()) {
+ Warning *it = m_warnings[row];
+
+ switch (role) {
+ case WarningModel::FilterRole:
+ return it->isActive() ? Constants::C_WARNINGMODEL_FILTER_ACTIVE : Constants::C_WARNINGMODEL_FILTER_NOT;
+ case Qt::DecorationRole: {
+ if (col == 0)
+ return severityIcon(it->severity());
+ break;
+ }
+ case Qt::ToolTipRole: {
+ return tr("Severity:\t%1\nType: \t%2\nReason: \t%3\nDescription:\t%4")
+ .arg(severityName(it->severity()))
+ .arg(it->typeName())
+ .arg(it->reason())
+ .arg(it->description());
+ }
+ case Qt::DisplayRole: {
+ switch (col) {
+ case 0:
+ return severityName(it->severity());
+ case 1:
+ return it->typeName();
+ case 2:
+ return it->reason();
+ case 3:
+ return it->description();
+ default:
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ }
+ return QVariant();
+}
+
+void WarningModel::setShowWarnings(int type, bool show)
+{
+ //beginResetModel();
+ m_warningVisibilities[type] = show;
+ for (int i = 0; i < m_warnings.count(); ++i)
+ m_warnings[i]->setActive(m_warningVisibilities.value(m_warnings[i]->severity(), true));
+ //endResetModel();
+}
+
+Warning *WarningModel::createWarning(Warning::Severity severity, const QString &type, const QString &reason, const QString &description)
+{
+ beginInsertRows(QModelIndex(), m_warnings.count(), m_warnings.count());
+ auto warning = new Warning(severity, type, reason, description, m_warningVisibilities.value(severity, true));
+ connect(warning, &Warning::destroyed, this, &WarningModel::warningDestroyed);
+ connect(warning, &Warning::dataChanged, this, [=] {
+ emit warningsChanged();
+ QModelIndex ind = createIndex(m_warnings.indexOf(warning), 0);
+ emit dataChanged(ind, ind);
+ });
+
+ m_warnings << warning;
+ endInsertRows();
+
+ emit warningsChanged();
+
+ m_countChecker->start();
+ return warning;
+}
+
+Warning *WarningModel::getWarning(int row)
+{
+ if (row >= 0 && row < m_warnings.count())
+ return m_warnings[row];
+
+ return nullptr;
+}
+
+Warning *WarningModel::getWarning(const QModelIndex &ind)
+{
+ if (ind.isValid())
+ return getWarning(ind.row());
+
+ return nullptr;
+}
+
+void WarningModel::warningDestroyed(QObject *ww)
+{
+ auto w = static_cast<Warning*>(ww);
+ if (m_warnings.contains(w)) {
+ int ind = m_warnings.indexOf(w);
+ beginRemoveRows(QModelIndex(), ind, ind);
+ m_warnings.removeAt(ind);
+ endResetModel();
+ }
+
+ m_countChecker->start();
+ emit warningsChanged();
+}
diff --git a/src/plugins/scxmleditor/outputpane/warningmodel.h b/src/plugins/scxmleditor/outputpane/warningmodel.h
new file mode 100644
index 00000000000..910b13c11fb
--- /dev/null
+++ b/src/plugins/scxmleditor/outputpane/warningmodel.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "warning.h"
+
+#include <QAbstractTableModel>
+#include <QIcon>
+#include <QPointer>
+#include <QTimer>
+
+namespace ScxmlEditor {
+
+namespace OutputPane {
+
+class WarningModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ enum WarningRole {
+ FilterRole = Qt::UserRole + 1
+ };
+
+ WarningModel(QObject *parent = nullptr);
+ ~WarningModel() override;
+
+ int count(Warning::Severity type) const;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+
+ void setShowWarnings(int type, bool show);
+ Warning *createWarning(Warning::Severity severity, const QString &type, const QString &reason, const QString &description);
+ Warning *getWarning(const QModelIndex &ind);
+ Warning *getWarning(int row);
+
+ void clear(bool sendSignal = true);
+
+signals:
+ void warningsChanged();
+ void countChanged(int count);
+ void modelAboutToBeClear();
+ void modelCleared();
+
+private:
+ void warningDestroyed(QObject *p);
+ QString severityName(Warning::Severity severity) const;
+ QIcon severityIcon(Warning::Severity severity) const;
+
+ QVector<Warning*> m_warnings;
+ QMap<int, bool> m_warningVisibilities;
+ int m_oldCount;
+ QPointer<QTimer> m_countChecker;
+};
+
+} // namespace OutputPane
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/actionhandler.cpp b/src/plugins/scxmleditor/plugin_interface/actionhandler.cpp
new file mode 100644
index 00000000000..58e94fc6cd6
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/actionhandler.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "actionhandler.h"
+#include "mytypes.h"
+
+#include <QAction>
+
+using namespace ScxmlEditor::PluginInterface;
+
+ActionHandler::ActionHandler(QObject *parent)
+ : QObject(parent)
+{
+ using AH = ActionHandler;
+
+ const struct {
+ const char *icon;
+ QString name;
+ QString tooltip;
+ const char *keyseq;
+ bool checkable;
+ } actionInfos[] = {
+ { ":/scxmleditor/images/icon-zoom-in.png", AH::tr("Zoom In"), AH::tr("Zoom In (Ctrl + + / Ctrl + Wheel)"), "Ctrl++", false },
+ { ":/scxmleditor/images/icon-zoom-out.png", AH::tr("Zoom Out"), AH::tr("Zoom Out (Ctrl + - / Ctrl + Wheel)"), "Ctrl+-", false },
+ { ":/scxmleditor/images/icon-fit-screen.png", AH::tr("Fit to View"), AH::tr("Fit to View (F11)"), "F11", false },
+ { ":/scxmleditor/images/icon-pan.png", AH::tr("Panning"), AH::tr("Panning (Shift)"), "Shift", true },
+
+ { ":/scxmleditor/images/icon-search.png", AH::tr("Magnifier"), AH::tr("Magnifier Tool (Alt)"), "Alt", true },
+ { ":/scxmleditor/images/navigator.png", AH::tr("Navigator"), AH::tr("Navigator (Ctrl+E)"), "Ctrl+E", true },
+
+ { ":/scxmleditor/images/icon-copy.png", AH::tr("Copy"), AH::tr("Copy (Ctrl + C)"), "Ctrl+C", false },
+ { ":/scxmleditor/images/icon-cut.png", AH::tr("Cut"), AH::tr("Cut (Ctrl + X)"), "Ctrl+X", false },
+ { ":/scxmleditor/images/icon-paste.png", AH::tr("Paste"), AH::tr("Paste (Ctrl + V)"), "Ctrl+V", false },
+ { ":/scxmleditor/images/screenshot.png", AH::tr("Screenshot"), AH::tr("Screenshot (Ctrl + Shift + C)"), "Ctrl+Shift+C", false },
+ { ":/scxmleditor/images/icon-export-canvas.png", AH::tr("Export to Image"), AH::tr("Export to Image"), "Ctrl+Shift+E", false },
+ { ":/scxmleditor/images/fullnamespace.png", AH::tr("Toggle Full Namespace"), AH::tr("Toggle Full Namespace"), "Ctrl+Shift+N", true },
+
+ { ":/scxmleditor/images/align_left.png", AH::tr("Align Left"), AH::tr("Align Left (Ctrl+L,1)"), "Ctrl+L,1", false },
+ { ":/scxmleditor/images/align_right.png", AH::tr("Align Right"), AH::tr("Align Right (Ctrl+L,2)"), "Ctrl+L,2", false },
+ { ":/scxmleditor/images/align_top.png", AH::tr("Align Top"), AH::tr("Align Top (Ctrl+L,3)"), "Ctrl+L,3", false },
+ { ":/scxmleditor/images/align_bottom.png", AH::tr("Align Bottom"), AH::tr("Align Bottom (Ctrl+L,4)"), "Ctrl+L,4", false },
+ { ":/scxmleditor/images/align_horizontal.png", AH::tr("Align Horizontal"), AH::tr("Align Horizontal (Ctrl+L,5)"), "Ctrl+L,5", false },
+ { ":/scxmleditor/images/align_vertical.png", AH::tr("Align Vertical"), AH::tr("Align Vertical (Ctrl+L,6)"), "Ctrl+L,6", false },
+ { ":/scxmleditor/images/adjust_width.png", AH::tr("Adjust Width"), AH::tr("Adjust Width (Ctrl+L,7)"), "Ctrl+L,7", false },
+ { ":/scxmleditor/images/adjust_height.png", AH::tr("Adjust Height"), AH::tr("Adjust Height (Ctrl+L,8)"), "Ctrl+L,8", false },
+ { ":/scxmleditor/images/adjust_size.png", AH::tr("Adjust Size"), AH::tr("Adjust Size (Ctrl+L,9)"), "Ctrl+L,9", false },
+
+ { ":/scxmleditor/images/statistics.png", AH::tr("Show Statistics..."), AH::tr("Show Statistics"), "", false }
+ };
+
+ // Init actions
+ for (auto info: actionInfos) {
+ auto action = new QAction(QIcon(QLatin1String(info.icon)), info.name, this);
+ action->setCheckable(info.checkable);
+ action->setToolTip(info.tooltip);
+ action->setShortcut(QKeySequence(QLatin1String(info.keyseq)));
+
+ m_actions << action;
+ }
+}
+
+QAction *ActionHandler::action(ActionType type) const
+{
+ if (type >= ActionZoomIn && type < ActionLast)
+ return m_actions[type];
+
+ return nullptr;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/actionhandler.h b/src/plugins/scxmleditor/plugin_interface/actionhandler.h
new file mode 100644
index 00000000000..51d8fba9d68
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/actionhandler.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "mytypes.h"
+
+#include <QObject>
+
+QT_FORWARD_DECLARE_CLASS(QAction)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ActionHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit ActionHandler(QObject *parent = nullptr);
+
+ QAction *action(ActionType type) const;
+
+private:
+ QVector<QAction*> m_actions;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/actionprovider.h b/src/plugins/scxmleditor/plugin_interface/actionprovider.h
new file mode 100644
index 00000000000..fac5fd7ba45
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/actionprovider.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QObject>
+
+QT_FORWARD_DECLARE_CLASS(QMenu)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ScxmlTag;
+
+class ActionProvider : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit ActionProvider(QObject *parent = nullptr)
+ : QObject(parent)
+ {
+ }
+
+ virtual void initStateMenu(const ScxmlTag *tag, QMenu *menu) = 0;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/attributeitemdelegate.cpp b/src/plugins/scxmleditor/plugin_interface/attributeitemdelegate.cpp
new file mode 100644
index 00000000000..168b8b424b6
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/attributeitemdelegate.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "attributeitemdelegate.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+AttributeItemDelegate::AttributeItemDelegate(QObject *parent)
+ : QStyledItemDelegate(parent)
+ , m_tag(0)
+{
+}
+
+void AttributeItemDelegate::setTag(ScxmlTag *tag)
+{
+ m_tag = tag;
+}
+
+ScxmlTag *AttributeItemDelegate::tag() const
+{
+ return m_tag;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/attributeitemdelegate.h b/src/plugins/scxmleditor/plugin_interface/attributeitemdelegate.h
new file mode 100644
index 00000000000..26027bb1f89
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/attributeitemdelegate.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmltag.h"
+
+#include <QPointer>
+#include <QStyledItemDelegate>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class AttributeItemDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+public:
+ explicit AttributeItemDelegate(QObject *parent = nullptr);
+
+ void setTag(ScxmlTag *tag);
+ ScxmlTag *tag() const;
+
+protected:
+ QPointer<ScxmlTag> m_tag;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/attributeitemmodel.cpp b/src/plugins/scxmleditor/plugin_interface/attributeitemmodel.cpp
new file mode 100644
index 00000000000..8d8e63495c4
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/attributeitemmodel.cpp
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "attributeitemmodel.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+AttributeItemModel::AttributeItemModel(QObject *parent)
+ : QAbstractTableModel(parent)
+{
+}
+
+void AttributeItemModel::setTag(ScxmlTag *tag)
+{
+ beginResetModel();
+ m_tag = tag;
+ m_document = m_tag ? m_tag->document() : 0;
+ endResetModel();
+ emit layoutChanged();
+ emit dataChanged(QModelIndex(), QModelIndex());
+}
+
+ScxmlTag *AttributeItemModel::tag() const
+{
+ return m_tag;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/attributeitemmodel.h b/src/plugins/scxmleditor/plugin_interface/attributeitemmodel.h
new file mode 100644
index 00000000000..c86edf16fbc
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/attributeitemmodel.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmldocument.h"
+#include "scxmltag.h"
+
+#include <QAbstractTableModel>
+#include <QPointer>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class AttributeItemModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ AttributeItemModel(QObject *parent = nullptr);
+
+ virtual void setTag(ScxmlTag *tag);
+ ScxmlTag *tag() const;
+
+protected:
+ QPointer<ScxmlDocument> m_document;
+ QPointer<ScxmlTag> m_tag;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/baseitem.cpp b/src/plugins/scxmleditor/plugin_interface/baseitem.cpp
new file mode 100644
index 00000000000..40bb45b5f1a
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/baseitem.cpp
@@ -0,0 +1,430 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "baseitem.h"
+#include "actionhandler.h"
+#include "actionprovider.h"
+#include "graphicsscene.h"
+#include "sceneutils.h"
+#include "scxmldocument.h"
+#include "scxmleditorconstants.h"
+#include "scxmltagutils.h"
+#include "scxmluifactory.h"
+#include "stateitem.h"
+
+#include <QCoreApplication>
+#include <QGraphicsSceneMouseEvent>
+#include <QKeyEvent>
+#include <QMenu>
+#include <QUndoStack>
+
+using namespace ScxmlEditor::PluginInterface;
+
+BaseItem::BaseItem(BaseItem *parent)
+ : QGraphicsObject(parent)
+{
+ setFlag(ItemIsFocusable, true);
+ setItemBoundingRect(QRectF(-60, -50, 120, 100));
+
+ m_scene = static_cast<GraphicsScene*>(scene());
+ if (m_scene)
+ m_scene->addChild(this);
+}
+
+BaseItem::~BaseItem()
+{
+ if (m_scene)
+ m_scene->removeChild(this);
+}
+
+void BaseItem::checkParentBoundingRect()
+{
+ BaseItem *parentBaseItem = this->parentBaseItem();
+ if (parentBaseItem && type() >= InitialStateType && !parentBaseItem->blockUpdates()) {
+ auto parentStateItem = qgraphicsitem_cast<StateItem*>(parentBaseItem);
+ if (parentStateItem && (parentStateItem->type() >= StateType))
+ parentStateItem->updateBoundingRect();
+ }
+}
+
+void BaseItem::updatePolygon()
+{
+ m_polygon.clear();
+ m_polygon << m_boundingRect.topLeft()
+ << m_boundingRect.topRight()
+ << m_boundingRect.bottomRight()
+ << m_boundingRect.bottomLeft()
+ << m_boundingRect.topLeft();
+}
+
+void BaseItem::updateDepth()
+{
+ const BaseItem *baseItem = parentBaseItem();
+ m_depth = baseItem ? baseItem->depth() + 1 : 0;
+ update();
+}
+
+void BaseItem::setItemBoundingRect(const QRectF &r)
+{
+ if (m_boundingRect != r) {
+ prepareGeometryChange();
+
+ m_boundingRect = r;
+
+ if (!m_blockUpdates)
+ checkParentBoundingRect();
+
+ updatePolygon();
+ emit geometryChanged();
+ }
+}
+
+QVariant BaseItem::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ switch (change) {
+ case ItemSceneHasChanged:
+ m_scene = static_cast<GraphicsScene*>(scene());
+ if (m_scene)
+ m_scene->addChild(this);
+ break;
+ case ItemSelectedHasChanged: {
+ emit selectedStateChanged(value.toBool());
+ if (value.toBool() && tag())
+ tag()->document()->setCurrentTag(tag());
+ break;
+ }
+ case ItemPositionChange:
+ if (m_scene && type() >= InitialStateType) {
+ // Snap to items
+ QPointF currentCenter = sceneCenter();
+ QPointF targetCenter;
+ QPair<bool, bool> res = m_scene->checkSnapToItem(this, currentCenter, targetCenter);
+
+ QPointF val = value.toPointF();
+ QPointF change = val - pos();
+ if (res.first && qAbs(change.x()) < 12)
+ val.setX(pos().x() + targetCenter.x() - currentCenter.x());
+ if (res.second && qAbs(change.y()) < 12)
+ val.setY(pos().y() + targetCenter.y() - currentCenter.y());
+
+ return val;
+ }
+ break;
+ case ItemParentChange:
+ case ItemPositionHasChanged:
+ case ItemTransformHasChanged:
+ updateUIProperties();
+ break;
+ default:
+ break;
+ }
+
+ return QGraphicsObject::itemChange(change, value);
+}
+
+void BaseItem::mousePressEvent(QGraphicsSceneMouseEvent *e)
+{
+ if (e->button() == Qt::RightButton) {
+ e->accept();
+ showContextMenu(e);
+ } else {
+ QGraphicsObject::mousePressEvent(e);
+ }
+}
+
+void BaseItem::showContextMenu(QGraphicsSceneMouseEvent *e)
+{
+ checkSelectionBeforeContextMenu(e);
+ QMenu menu;
+ createContextMenu(&menu);
+ selectedMenuAction(menu.exec(e->screenPos()));
+}
+
+void BaseItem::checkSelectionBeforeContextMenu(QGraphicsSceneMouseEvent *e)
+{
+ if (!isSelected() && !(e->modifiers() & Qt::ControlModifier) && m_scene)
+ m_scene->unselectAll();
+
+ if (m_tag)
+ m_tag->document()->setCurrentTag(m_tag);
+}
+
+void BaseItem::createContextMenu(QMenu *menu)
+{
+ if (!menu || !tag())
+ return;
+
+ if (m_scene) {
+ menu->addAction(m_scene->actionHandler()->action(ActionCopy));
+ menu->addAction(m_scene->actionHandler()->action(ActionPaste));
+ menu->addSeparator();
+ const ScxmlUiFactory *uiFactory = m_scene->uiFactory();
+ if (uiFactory) {
+ auto actionProvider = static_cast<ActionProvider*>(uiFactory->object(Constants::C_UI_FACTORY_OBJECT_ACTIONPROVIDER));
+ if (actionProvider) {
+ actionProvider->initStateMenu(tag(), menu);
+ menu->addSeparator();
+ }
+ }
+ }
+ TagUtils::createChildMenu(tag(), menu);
+}
+
+void BaseItem::selectedMenuAction(const QAction *action)
+{
+ if (!action)
+ return;
+
+ ScxmlTag *tag = this->tag();
+
+ if (tag) {
+ const QVariantMap data = action->data().toMap();
+ int actionType = data.value(Constants::C_SCXMLTAG_ACTIONTYPE, -1).toInt();
+
+ switch (actionType) {
+ case TagUtils::AddChild: {
+ const ScxmlDocument *document = tag->document();
+ if (m_scene && document) {
+ document->undoStack()->beginMacro(tr("Add child"));
+ SceneUtils::addChild(tag, data, m_scene);
+ document->undoStack()->endMacro();
+ }
+ break;
+ }
+ case TagUtils::Remove:
+ postDeleteEvent();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void BaseItem::postDeleteEvent()
+{
+ QCoreApplication::postEvent(scene(), new QKeyEvent(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier));
+}
+
+void BaseItem::setEditorInfo(const QString &key, const QString &value, bool block)
+{
+ if (m_tag && m_tag->editorInfo(key) != value) {
+ if (!m_blockUpdates && !block && m_tag->document())
+ m_tag->document()->setEditorInfo(m_tag, key, value);
+ else
+ m_tag->setEditorInfo(key, value);
+ }
+}
+
+QString BaseItem::editorInfo(const QString &key) const
+{
+ if (m_tag)
+ return m_tag->editorInfo(key);
+
+ return QString();
+}
+
+void BaseItem::setTagValue(const QString &key, const QString &value)
+{
+ if (m_tag && m_tag->attribute(key) != value) {
+ if (!m_blockUpdates && m_tag->document())
+ m_tag->document()->setValue(m_tag, key, value);
+ else
+ m_tag->setAttribute(key, value);
+ }
+}
+
+QString BaseItem::tagValue(const QString &key, bool useNameSpace) const
+{
+ return m_tag ? m_tag->attribute(key, useNameSpace) : QString();
+}
+
+ScxmlTag *BaseItem::tag() const
+{
+ return m_tag;
+}
+
+void BaseItem::setItemSelected(bool sel, bool unselectOthers)
+{
+ if (sel) {
+ if (unselectOthers && m_scene)
+ m_scene->unselectAll();
+
+ if (m_tag)
+ m_tag->document()->setCurrentTag(m_tag);
+ } else {
+ setSelected(false);
+ }
+}
+
+QRectF BaseItem::boundingRect() const
+{
+ return m_boundingRect;
+}
+
+void BaseItem::setTag(ScxmlTag *tag)
+{
+ m_tag = tag;
+}
+
+void BaseItem::init(ScxmlTag *tag, BaseItem *parentItem, bool /*initChildren*/, bool /*blockUpdates*/)
+{
+ setTag(tag);
+ readUISpecifiedProperties(tag);
+ setParentItem(parentItem);
+}
+
+void BaseItem::setBlockUpdates(bool block)
+{
+ m_blockUpdates = block;
+}
+
+void BaseItem::setHighlight(bool hl)
+{
+ if (hl != m_highlight) {
+ m_highlight = hl;
+ update();
+ }
+}
+
+void BaseItem::setOverlapping(bool ol)
+{
+ if (ol != m_overlapping) {
+ m_overlapping = ol;
+ update();
+ }
+}
+
+bool BaseItem::highlight() const
+{
+ return m_highlight;
+}
+
+bool BaseItem::overlapping() const
+{
+ return m_overlapping;
+}
+
+bool BaseItem::isActiveScene() const
+{
+ return m_scene ? m_scene->topMostScene() : false;
+}
+
+ScxmlUiFactory *BaseItem::uiFactory() const
+{
+ return m_scene ? m_scene->uiFactory() : 0;
+}
+
+GraphicsScene *BaseItem::graphicsScene() const
+{
+ return m_scene;
+}
+
+void BaseItem::setParentItem(BaseItem *item)
+{
+ QGraphicsObject::setParentItem(item);
+ updateColors();
+}
+
+void BaseItem::checkWarnings()
+{
+}
+
+void BaseItem::checkOverlapping()
+{
+}
+
+void BaseItem::checkVisibility(double scaleFactor)
+{
+ Q_UNUSED(scaleFactor)
+}
+
+void BaseItem::updateUIProperties()
+{
+}
+
+void BaseItem::readUISpecifiedProperties(const ScxmlTag *tag)
+{
+ Q_UNUSED(tag)
+}
+
+void BaseItem::updateAttributes()
+{
+}
+
+void BaseItem::updateColors()
+{
+}
+
+void BaseItem::updateEditorInfo(bool /*allChildren*/)
+{
+ readUISpecifiedProperties(m_tag);
+}
+
+void BaseItem::moveStateBy(qreal /*dx*/, qreal /*dy*/)
+{
+}
+
+void BaseItem::finalizeCreation()
+{
+}
+
+void BaseItem::doLayout(int depth)
+{
+ Q_UNUSED(depth)
+}
+
+void BaseItem::checkInitial(bool parent)
+{
+ Q_UNUSED(parent)
+}
+
+bool BaseItem::containsScenePoint(const QPointF &p) const
+{
+ return sceneBoundingRect().contains(p);
+}
+
+QString BaseItem::itemId() const
+{
+ if (m_tag) {
+ if (m_tag->tagType() == Transition)
+ return m_tag->attribute("event");
+ else
+ return m_tag->attribute("id", true);
+ }
+
+ return QString();
+}
+
+bool BaseItem::blockUpdates() const
+{
+ return m_blockUpdates;
+}
+
+BaseItem *BaseItem::parentBaseItem() const
+{
+ QGraphicsItem *parent = parentItem();
+ return (parent && parent->type() > UnknownType)
+ ? qgraphicsitem_cast<BaseItem*>(parent) : nullptr;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/baseitem.h b/src/plugins/scxmleditor/plugin_interface/baseitem.h
new file mode 100644
index 00000000000..8c5138f6a30
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/baseitem.h
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "mytypes.h"
+#include "scxmltag.h"
+
+#include <QGraphicsItem>
+#include <QPointer>
+#include <QTimer>
+
+QT_FORWARD_DECLARE_CLASS(QAction)
+QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
+QT_FORWARD_DECLARE_CLASS(QVariant)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+const qreal WARNING_ITEM_SIZE = 25;
+
+class GraphicsScene;
+class ScxmlUiFactory;
+
+/**
+ * @brief The BaseItem class is the base class for every items in the scene which represent the one tag in the SCXML-tree.
+ *
+ *
+ */
+class BaseItem : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+ explicit BaseItem(BaseItem *parent = nullptr);
+ ~BaseItem() override;
+
+ QRectF boundingRect() const override;
+
+ QPointF sceneCenter() const
+ {
+ return mapToScene(m_boundingRect.center());
+ }
+
+ QString itemId() const;
+
+ void setItemBoundingRect(const QRectF &r);
+
+ QPolygonF polygonShape() const
+ {
+ return m_polygon;
+ }
+
+ void updateDepth();
+ int depth() const
+ {
+ return m_depth;
+ }
+
+ void setParentItem(BaseItem *item);
+ virtual void checkWarnings();
+ virtual void checkOverlapping();
+ virtual void checkVisibility(double scaleFactor);
+ virtual void setTag(ScxmlTag *tag);
+ virtual void init(ScxmlTag *tag, BaseItem *parentItem = nullptr, bool initChildren = true, bool blockUpdates = false);
+ virtual void updateAttributes();
+ virtual void updateColors();
+ virtual void updateEditorInfo(bool allChildren = false);
+ virtual void moveStateBy(qreal dx, qreal dy);
+ virtual ScxmlTag *tag() const;
+ virtual void finalizeCreation();
+ virtual void doLayout(int depth);
+ virtual void setHighlight(bool hl);
+ virtual void checkInitial(bool parent = false);
+ virtual bool containsScenePoint(const QPointF &p) const;
+
+ void setEditorInfo(const QString &key, const QString &value, bool block = false);
+ QString editorInfo(const QString &key) const;
+
+ void setTagValue(const QString &key, const QString &value);
+ QString tagValue(const QString &key, bool useNameSpace = false) const;
+
+ void setBlockUpdates(bool block);
+ void setOverlapping(bool ol);
+ bool blockUpdates() const;
+ bool highlight() const;
+ bool overlapping() const;
+
+ BaseItem *parentBaseItem() const;
+ bool isActiveScene() const;
+ GraphicsScene *graphicsScene() const;
+ ScxmlUiFactory *uiFactory() const;
+
+ virtual void updateUIProperties();
+
+protected:
+ virtual void updatePolygon();
+ void setItemSelected(bool sel, bool unselectOthers = true);
+
+signals:
+ void geometryChanged();
+ void selectedStateChanged(bool sel);
+ void openToDifferentView(BaseItem *item);
+
+protected:
+ void postDeleteEvent();
+ void mousePressEvent(QGraphicsSceneMouseEvent *e) override;
+ void showContextMenu(QGraphicsSceneMouseEvent *e);
+ virtual void checkSelectionBeforeContextMenu(QGraphicsSceneMouseEvent *e);
+ virtual void createContextMenu(QMenu *menu);
+ virtual void selectedMenuAction(const QAction *action);
+ virtual void readUISpecifiedProperties(const ScxmlTag *tag);
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
+ void checkParentBoundingRect();
+ QPolygonF m_polygon;
+
+private:
+ QRectF m_boundingRect;
+ QPointer<ScxmlTag> m_tag;
+ QPointer<GraphicsScene> m_scene;
+ bool m_blockUpdates = false;
+ int m_depth = 0;
+ bool m_highlight = false;
+ bool m_overlapping = false;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/connectableitem.cpp b/src/plugins/scxmleditor/plugin_interface/connectableitem.cpp
new file mode 100644
index 00000000000..0bc3d5a19c8
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/connectableitem.cpp
@@ -0,0 +1,809 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "connectableitem.h"
+#include "cornergrabberitem.h"
+#include "graphicsscene.h"
+#include "highlightitem.h"
+#include "quicktransitionitem.h"
+#include "sceneutils.h"
+#include "scxmleditorconstants.h"
+#include "serializer.h"
+#include "stateitem.h"
+
+#include <QDebug>
+#include <QPainter>
+#include <QPen>
+#include <QStringList>
+#include <QUndoStack>
+
+using namespace ScxmlEditor::PluginInterface;
+
+ConnectableItem::ConnectableItem(const QPointF &p, BaseItem *parent)
+ : BaseItem(parent)
+{
+ setFlag(QGraphicsItem::ItemIsMovable, true);
+ setFlag(QGraphicsItem::ItemIsSelectable, true);
+ setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+ setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true);
+ setAcceptDrops(true);
+
+ m_selectedPen.setStyle(Qt::DotLine);
+ m_selectedPen.setColor(QColor(0x44, 0x44, 0xed));
+ m_selectedPen.setCosmetic(true);
+ m_releasedFromParentBrush = QBrush(QColor(0x98, 0x98, 0x98));
+
+ setPos(p);
+ connect(this, &ConnectableItem::geometryChanged, this, &ConnectableItem::updateCornerPositions);
+}
+
+ConnectableItem::~ConnectableItem()
+{
+ setBlockUpdates(true);
+
+ foreach (ConnectableItem *item, m_overlappedItems) {
+ item->removeOverlappingItem(this);
+ }
+ m_overlappedItems.clear();
+
+ foreach (TransitionItem *transition, m_outputTransitions) {
+ transition->disconnectItem(this);
+ }
+ m_outputTransitions.clear();
+
+ foreach (TransitionItem *transition, m_inputTransitions) {
+ transition->disconnectItem(this);
+ }
+ m_inputTransitions.clear();
+
+ qDeleteAll(m_quickTransitions);
+ m_quickTransitions.clear();
+}
+
+void ConnectableItem::createCorners()
+{
+ for (int i = 0; i < 8; ++i) {
+ Qt::CursorShape cur = Qt::SizeHorCursor;
+ switch (i) {
+ case 0:
+ case 4:
+ cur = Qt::SizeFDiagCursor;
+ break;
+ case 1:
+ case 5:
+ cur = Qt::SizeVerCursor;
+ break;
+ case 2:
+ case 6:
+ cur = Qt::SizeBDiagCursor;
+ break;
+ case 3:
+ case 7:
+ cur = Qt::SizeHorCursor;
+ break;
+ default:
+ break;
+ }
+ m_corners << new CornerGrabberItem(this, cur);
+ }
+
+ qDeleteAll(m_quickTransitions);
+ m_quickTransitions.clear();
+ m_quickTransitions << new QuickTransitionItem(0, UnknownType, this);
+ m_quickTransitions << new QuickTransitionItem(1, StateType, this);
+ m_quickTransitions << new QuickTransitionItem(2, ParallelType, this);
+ m_quickTransitions << new QuickTransitionItem(3, HistoryType, this);
+ m_quickTransitions << new QuickTransitionItem(4, FinalStateType, this);
+
+ updateCornerPositions();
+}
+
+void ConnectableItem::removeCorners()
+{
+ qDeleteAll(m_corners);
+ m_corners.clear();
+
+ qDeleteAll(m_quickTransitions);
+ m_quickTransitions.clear();
+}
+
+void ConnectableItem::updateCornerPositions()
+{
+ QRectF r = boundingRect();
+ if (m_corners.count() == 8) {
+ qreal cx = r.center().x();
+ qreal cy = r.center().y();
+ m_corners[0]->setPos(r.topLeft());
+ m_corners[1]->setPos(cx, r.top());
+ m_corners[2]->setPos(r.topRight());
+ m_corners[3]->setPos(r.right(), cy);
+ m_corners[4]->setPos(r.bottomRight());
+ m_corners[5]->setPos(cx, r.bottom());
+ m_corners[6]->setPos(r.bottomLeft());
+ m_corners[7]->setPos(r.left(), cy);
+ }
+
+ for (int i = 0; i < m_quickTransitions.count(); ++i) {
+ m_quickTransitions[i]->setPos(r.topLeft());
+ m_quickTransitions[i]->setVisible(!m_releasedFromParent && canStartTransition(m_quickTransitions[i]->connectionType()));
+ }
+ updateShadowClipRegion();
+}
+
+bool ConnectableItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
+{
+ if (watched->type() == CornerGrabberType) {
+ auto c = qgraphicsitem_cast<CornerGrabberItem*>(watched);
+ auto mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event);
+ if (c == nullptr || mouseEvent == nullptr)
+ return BaseItem::sceneEventFilter(watched, event);
+
+ QRectF r = boundingRect();
+ if (event->type() == QEvent::GraphicsSceneMousePress) {
+ setOpacity(0.5);
+ } else if (event->type() == QEvent::GraphicsSceneMouseMove) {
+ QPointF pw = watched->mapToItem(this, mouseEvent->pos());
+ QRectF rMin;
+ if (type() >= StateType)
+ rMin = qgraphicsitem_cast<StateItem*>(this)->childItemsBoundingRect();
+ if (rMin.isNull()) {
+ rMin = QRectF(0, 0, m_minimumWidth, m_minimumHeight);
+ rMin.moveCenter(r.center());
+ }
+
+ switch (m_corners.indexOf(c)) {
+ case 0:
+ r.setTopLeft(QPointF(qMin(pw.x(), rMin.left()), qMin(pw.y(), rMin.top())));
+ break;
+ case 1:
+ r.setTop(qMin(pw.y(), rMin.top()));
+ break;
+ case 2:
+ r.setTopRight(QPointF(qMax(pw.x(), rMin.right()), qMin(pw.y(), rMin.top())));
+ break;
+ case 3:
+ r.setRight(qMax(pw.x(), rMin.right()));
+ break;
+ case 4:
+ r.setBottomRight(QPointF(qMax(pw.x(), rMin.right()), qMax(pw.y(), rMin.bottom())));
+ break;
+ case 5:
+ r.setBottom(qMax(pw.y(), rMin.bottom()));
+ break;
+ case 6:
+ r.setBottomLeft(QPointF(qMin(pw.x(), rMin.left()), qMax(pw.y(), rMin.bottom())));
+ break;
+ case 7:
+ r.setLeft(qMin(pw.x(), rMin.left()));
+ break;
+ default:
+ break;
+ }
+
+ setItemBoundingRect(r);
+ updateCornerPositions();
+ updateTransitions();
+
+ return true;
+ } else if (event->type() == QEvent::GraphicsSceneMouseRelease) {
+ setOpacity(1.0);
+ updateUIProperties();
+ checkOverlapping();
+ }
+ } else if (watched->type() == QuickTransitionType) {
+ auto mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event);
+ if (!mouseEvent)
+ return BaseItem::sceneEventFilter(watched, event);
+
+ if (event->type() == QEvent::GraphicsSceneMousePress) {
+ m_newTransitionStartedPoint = mouseEvent->pos();
+ tag()->document()->undoStack()->beginMacro(tr("Add new state"));
+
+ m_newTransition = new TransitionItem;
+ scene()->addItem(m_newTransition);
+
+ ScxmlTag *newTag = nullptr;
+ if (tag()->tagType() == Initial || tag()->tagType() == History)
+ newTag = new ScxmlTag(InitialTransition, tag()->document());
+ else
+ newTag = new ScxmlTag(Transition, tag()->document());
+ newTag->setAttribute("type", "external");
+ m_newTransition->init(newTag);
+
+ tag()->document()->addTag(tag(), newTag);
+ m_newTransition->startTransitionFrom(this, watched->mapToScene(mouseEvent->pos()));
+ return true;
+ } else if (event->type() == QEvent::GraphicsSceneMouseMove) {
+ if (m_newTransition)
+ m_newTransition->setEndPos(watched->mapToScene(mouseEvent->pos()));
+ } else if (event->type() == QEvent::GraphicsSceneMouseRelease) {
+ auto tra = qgraphicsitem_cast<QuickTransitionItem*>(watched);
+ if (!tra)
+ return BaseItem::sceneEventFilter(watched, event);
+
+ if (m_newTransition) {
+ QLineF line(m_newTransitionStartedPoint, mouseEvent->pos());
+ if (line.length() > 30) {
+ m_newTransition->connectToTopItem(watched->mapToScene(mouseEvent->pos()), TransitionItem::End, tra->connectionType());
+ m_newTransition = nullptr;
+ setSelected(false);
+ tag()->document()->undoStack()->endMacro();
+ } else {
+ m_newTransition->grabMouse(tra->connectionType());
+ m_newTransition = nullptr;
+ setSelected(false);
+ }
+ return true;
+ }
+ }
+ }
+
+ return BaseItem::sceneEventFilter(watched, event);
+}
+
+void ConnectableItem::addOutputTransition(TransitionItem *transition)
+{
+ m_outputTransitions.append(transition);
+ transitionCountChanged();
+}
+
+void ConnectableItem::addInputTransition(TransitionItem *transition)
+{
+ m_inputTransitions.append(transition);
+ transitionCountChanged();
+}
+
+void ConnectableItem::removeOutputTransition(TransitionItem *transition)
+{
+ m_outputTransitions.removeAll(transition);
+ transitionCountChanged();
+}
+
+void ConnectableItem::removeInputTransition(TransitionItem *transition)
+{
+ m_inputTransitions.removeAll(transition);
+ transitionCountChanged();
+}
+
+void ConnectableItem::updateInputTransitions()
+{
+ foreach (TransitionItem *transition, m_inputTransitions) {
+ transition->updateComponents();
+ transition->updateUIProperties();
+ }
+ transitionsChanged();
+}
+
+void ConnectableItem::updateOutputTransitions()
+{
+ foreach (TransitionItem *transition, m_outputTransitions) {
+ transition->updateComponents();
+ transition->updateUIProperties();
+ }
+ transitionsChanged();
+}
+
+void ConnectableItem::updateTransitions(bool allChildren)
+{
+ updateOutputTransitions();
+ updateInputTransitions();
+
+ if (allChildren) {
+ foreach (QGraphicsItem *it, childItems()) {
+ auto item = static_cast<ConnectableItem*>(it);
+ if (item && item->type() >= InitialStateType)
+ item->updateTransitions(allChildren);
+ }
+ }
+}
+
+void ConnectableItem::updateTransitionAttributes(bool allChildren)
+{
+ foreach (TransitionItem *transition, m_outputTransitions)
+ transition->updateTarget();
+
+ foreach (TransitionItem *transition, m_inputTransitions)
+ transition->updateTarget();
+
+ if (allChildren) {
+ foreach (QGraphicsItem *it, childItems()) {
+ auto item = static_cast<ConnectableItem*>(it);
+ if (item && item->type() >= InitialStateType)
+ item->updateTransitionAttributes(allChildren);
+ }
+ }
+}
+
+void ConnectableItem::transitionsChanged()
+{
+}
+
+void ConnectableItem::transitionCountChanged()
+{
+}
+
+QPointF ConnectableItem::getInternalPosition(const TransitionItem *transition, TransitionItem::TransitionTargetType type) const
+{
+ const QRectF srect = sceneBoundingRect();
+
+ int ind = 0;
+ if (type == TransitionItem::InternalNoTarget) {
+ foreach (TransitionItem *item, m_outputTransitions) {
+ if (item->targetType() == TransitionItem::InternalSameTarget)
+ ind++;
+ }
+ }
+
+ for (int i = 0; i < m_outputTransitions.count(); ++i) {
+ TransitionItem *item = m_outputTransitions[i];
+ if (item == transition)
+ break;
+ else if (item->targetType() == type)
+ ind++;
+ }
+
+ return QPointF(srect.left() + 20, srect.top() + srect.height() * 0.06 + 40 + ind * 30);
+}
+
+void ConnectableItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ // We want to pan scene when Shift is pressed -> that's why ignore mouse-event here
+ if (event->modifiers() & Qt::ShiftModifier) {
+ event->ignore();
+ return;
+ }
+
+ BaseItem::mousePressEvent(event);
+}
+
+void ConnectableItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ // We want to pan scene when Shift is pressed -> that's why ignore mouse-event here
+ if (event->modifiers() & Qt::ShiftModifier) {
+ event->ignore();
+ return;
+ }
+
+ if (!m_moveMacroStarted) {
+ m_moveMacroStarted = true;
+ tag()->document()->undoStack()->beginMacro(tr("Move State"));
+ }
+
+ //Restore old behavior if ctrl & alt modifiers are present
+ if (!m_releasedFromParent && !(event->modifiers() & Qt::AltModifier) && !(event->modifiers() & Qt::ControlModifier)) {
+ releaseFromParent();
+ foreach (QGraphicsItem *it, scene()->selectedItems()) {
+ if (it->type() >= InitialStateType && it != this) {
+ qgraphicsitem_cast<ConnectableItem*>(it)->releaseFromParent();
+ }
+ }
+ } else
+ setOpacity(0.5);
+
+ BaseItem::mouseMoveEvent(event);
+}
+
+void ConnectableItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ // We want to pan scene when Shift is pressed -> that's why ignore mouse-event here
+ if (event->modifiers() & Qt::ShiftModifier) {
+ event->ignore();
+ return;
+ }
+
+ BaseItem::mouseReleaseEvent(event);
+
+ if (m_releasedFromParent) {
+ // Try to find parent
+ QList<QGraphicsItem*> parentItems = scene()->items(event->scenePos());
+ BaseItem *parentItem = nullptr;
+ for (int i = 0; i < parentItems.count(); ++i) {
+ auto item = static_cast<BaseItem*>(parentItems[i]);
+ if (item && item != this && !item->isSelected()
+ && item->type() >= StateType
+ && SceneUtils::canDrop(item->type(), type())) {
+ parentItem = item;
+ break;
+ }
+ }
+ connectToParent(parentItem);
+ foreach (QGraphicsItem *it, scene()->selectedItems()) {
+ if (it->type() >= InitialStateType && it != this)
+ qgraphicsitem_cast<ConnectableItem*>(it)->connectToParent(parentItem);
+ }
+ } else
+ setOpacity(1.0);
+
+ if (m_moveMacroStarted) {
+ m_moveMacroStarted = false;
+ tag()->document()->undoStack()->endMacro();
+ }
+
+ checkOverlapping();
+}
+
+void ConnectableItem::releaseFromParent()
+{
+ m_releasedFromParent = true;
+ setOpacity(0.5);
+ m_releasedIndex = tag()->index();
+ m_releasedParent = parentItem();
+ tag()->document()->changeParent(tag(), 0, m_releasedParent == nullptr ? m_releasedIndex : -1);
+ setZValue(503);
+
+ for (int i = 0; i < m_quickTransitions.count(); ++i)
+ m_quickTransitions[i]->setVisible(false);
+ for (int i = 0; i < m_corners.count(); ++i)
+ m_corners[i]->setVisible(false);
+ update();
+}
+
+void ConnectableItem::connectToParent(BaseItem *parentItem)
+{
+ for (int i = 0; i < m_quickTransitions.count(); ++i)
+ m_quickTransitions[i]->setVisible(canStartTransition(m_quickTransitions[i]->connectionType()));
+ for (int i = 0; i < m_corners.count(); ++i)
+ m_corners[i]->setVisible(true);
+
+ tag()->document()->changeParent(tag(), parentItem ? parentItem->tag() : 0, parentItem == m_releasedParent ? m_releasedIndex : -1);
+
+ setZValue(0);
+ m_releasedIndex = -1;
+ m_releasedParent = nullptr;
+ m_releasedFromParent = false;
+ setOpacity(1.0);
+}
+
+QVariant ConnectableItem::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ switch (change) {
+ case ItemSelectedHasChanged: {
+ if (value.toBool()) {
+ createCorners();
+ SceneUtils::moveTop(this, static_cast<GraphicsScene*>(scene()));
+ } else
+ removeCorners();
+ break;
+ }
+ case ItemParentHasChanged:
+ updateTransitions(true);
+ updateTransitionAttributes(true);
+ // FIXME: intended fallthrough?
+ case ItemPositionHasChanged:
+ if (!m_releasedFromParent && !blockUpdates())
+ checkParentBoundingRect();
+ break;
+ case ItemScenePositionHasChanged:
+ updateTransitions();
+ if (m_highlighItem)
+ m_highlighItem->advance(1);
+ break;
+ default:
+ break;
+ }
+
+ return BaseItem::itemChange(change, value);
+}
+
+qreal ConnectableItem::getOpacity()
+{
+ if (opacity() < 1.0)
+ return opacity();
+
+ if (overlapping())
+ return 0.5;
+
+ if (parentBaseItem())
+ if (parentBaseItem()->type() >= InitialStateType)
+ return static_cast<ConnectableItem*>(parentBaseItem())->getOpacity();
+
+ return 1;
+}
+
+void ConnectableItem::updateShadowClipRegion()
+{
+ QPainterPath br, sr;
+ //StateItem Background rounded rectangle
+ br.addRoundedRect(boundingRect().adjusted(5, 5, -5, -5), 10, 10);
+ //Shadow rounded rectangle
+ sr.addRoundedRect(boundingRect().adjusted(10, 10, 0, 0), 10, 10);
+ //Clippath is subtract
+ m_shadowClipPath = sr - br;
+}
+
+void ConnectableItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
+
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setOpacity(getOpacity());
+
+ if (m_releasedFromParent) {
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(m_releasedFromParentBrush);
+ painter->setClipping(true);
+ painter->setClipPath(m_shadowClipPath);
+ //Since the form is already cliped just draw a rectangle
+ painter->drawRect(boundingRect().adjusted(10, 10, 0, 0));
+ painter->setClipping(false);
+ }
+
+ if (isSelected()) {
+ painter->setPen(m_selectedPen);
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(boundingRect());
+ }
+
+ painter->restore();
+}
+
+void ConnectableItem::updateUIProperties()
+{
+ if (tag() != 0 && isActiveScene()) {
+ Serializer s;
+ s.append(pos());
+ s.append(boundingRect());
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY, s.data());
+ s.clear();
+ s.append(scenePos());
+ s.append(sceneBoundingRect());
+ setEditorInfo("scenegeometry", s.data());
+ }
+}
+
+void ConnectableItem::updateAttributes()
+{
+ BaseItem::updateAttributes();
+
+ foreach (TransitionItem *transition, m_inputTransitions) {
+ if (transition->isEndItem(this))
+ transition->setTagValue("target", itemId());
+ }
+ updateInputTransitions();
+
+ update();
+}
+
+void ConnectableItem::updateEditorInfo(bool allChildren)
+{
+ BaseItem::updateEditorInfo(allChildren);
+ updateTransitions();
+}
+
+void ConnectableItem::moveStateBy(qreal dx, qreal dy)
+{
+ moveBy(dx, dy);
+ updateUIProperties();
+ updateTransitions();
+}
+
+void ConnectableItem::setHighlight(bool hl)
+{
+ BaseItem::setHighlight(hl);
+ if (highlight()) {
+ if (!m_highlighItem) {
+ m_highlighItem = new HighlightItem(this);
+ scene()->addItem(m_highlighItem);
+ }
+ } else {
+ delete m_highlighItem;
+ m_highlighItem = nullptr;
+ }
+
+ if (m_highlighItem)
+ m_highlighItem->advance(0);
+}
+
+void ConnectableItem::readUISpecifiedProperties(const ScxmlTag *tag)
+{
+ if (tag) {
+ QString data = editorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY);
+ if (!data.isEmpty()) {
+ QPointF p(0, 0);
+ QRectF r(-60, 50, 120, 100);
+
+ Serializer s;
+ s.setData(data);
+ s.read(p);
+ s.read(r);
+
+ setItemBoundingRect(r);
+ setPos(p);
+ }
+ }
+}
+
+void ConnectableItem::addTransitions(const ScxmlTag *tag)
+{
+ if (scene()) {
+ for (int i = 0; i < tag->childCount(); ++i) {
+ ScxmlTag *child = tag->child(i);
+ if (child->tagType() == Transition || child->tagType() == InitialTransition) {
+ auto transition = new TransitionItem;
+ scene()->addItem(transition);
+ transition->setStartItem(this);
+ transition->init(child);
+ }
+ }
+ }
+}
+
+void ConnectableItem::init(ScxmlTag *tag, BaseItem *parentItem, bool initChildren, bool /*blockUpdates*/)
+{
+ BaseItem::init(tag, parentItem);
+ if (initChildren)
+ addTransitions(tag);
+}
+
+void ConnectableItem::setMinimumWidth(int width)
+{
+ m_minimumWidth = width;
+ QRectF r = boundingRect();
+ if (r.width() < width) {
+ r.setWidth(width);
+ setItemBoundingRect(r);
+ }
+}
+
+void ConnectableItem::setMinimumHeight(int height)
+{
+ m_minimumHeight = height;
+ QRectF r = boundingRect();
+ if (r.height() < height) {
+ r.setHeight(height);
+ setItemBoundingRect(r);
+ }
+}
+
+void ConnectableItem::finalizeCreation()
+{
+ bool old = blockUpdates();
+ setBlockUpdates(true);
+
+ updateAttributes();
+ updateEditorInfo();
+ updateUIProperties();
+ checkInitial(true);
+
+ if (!old)
+ setBlockUpdates(false);
+}
+
+bool ConnectableItem::hasInputTransitions(const ConnectableItem *parentItem, bool checkChildren) const
+{
+ foreach (const TransitionItem *it, m_inputTransitions) {
+ if (!SceneUtils::isChild(parentItem, it->connectedItem(this)))
+ return true;
+ }
+
+ if (checkChildren) {
+ foreach (QGraphicsItem *it, childItems()) {
+ if (it->type() >= InitialStateType) {
+ auto item = qgraphicsitem_cast<ConnectableItem*>(it);
+ if (item && item->hasInputTransitions(parentItem, checkChildren))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool ConnectableItem::hasOutputTransitions(const ConnectableItem *parentItem, bool checkChildren) const
+{
+ foreach (TransitionItem *it, m_outputTransitions) {
+ if (!SceneUtils::isChild(parentItem, it->connectedItem(this)))
+ return true;
+ }
+
+ if (checkChildren) {
+ foreach (QGraphicsItem *it, childItems()) {
+ if (it->type() >= InitialStateType) {
+ auto item = qgraphicsitem_cast<ConnectableItem*>(it);
+ if (item && item->hasOutputTransitions(parentItem, checkChildren))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void ConnectableItem::addOverlappingItem(ConnectableItem *item)
+{
+ if (!m_overlappedItems.contains(item))
+ m_overlappedItems.append(item);
+
+ setOverlapping(m_overlappedItems.count() > 0);
+}
+
+void ConnectableItem::removeOverlappingItem(ConnectableItem *item)
+{
+ if (m_overlappedItems.contains(item))
+ m_overlappedItems.removeAll(item);
+
+ setOverlapping(m_overlappedItems.count() > 0);
+}
+
+void ConnectableItem::checkOverlapping()
+{
+ QVector<ConnectableItem*> overlappedItems;
+ foreach (QGraphicsItem *it, collidingItems()) {
+ if (it->type() >= InitialStateType && it->parentItem() == parentItem()) {
+ overlappedItems << qgraphicsitem_cast<ConnectableItem*>(it);
+ }
+ }
+
+ // Remove unnecessary items
+ for (int i = m_overlappedItems.count(); i--;) {
+ if (!overlappedItems.contains(m_overlappedItems[i])) {
+ m_overlappedItems[i]->removeOverlappingItem(this);
+ m_overlappedItems.removeAt(i);
+ }
+ }
+
+ // Add new overlapped items
+ foreach (ConnectableItem *it, overlappedItems) {
+ if (!m_overlappedItems.contains(it)) {
+ m_overlappedItems << it;
+ it->addOverlappingItem(this);
+ }
+ }
+
+ setOverlapping(m_overlappedItems.count() > 0);
+}
+
+bool ConnectableItem::canStartTransition(ItemType type) const
+{
+ Q_UNUSED(type)
+ return true;
+}
+
+QVector<TransitionItem*> ConnectableItem::outputTransitions() const
+{
+ return m_outputTransitions;
+}
+
+QVector<TransitionItem*> ConnectableItem::inputTransitions() const
+{
+ return m_inputTransitions;
+}
+
+int ConnectableItem::transitionCount() const
+{
+ return m_outputTransitions.count() + m_inputTransitions.count();
+}
+
+int ConnectableItem::outputTransitionCount() const
+{
+ return m_outputTransitions.count();
+}
+
+int ConnectableItem::inputTransitionCount() const
+{
+ return m_inputTransitions.count();
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/connectableitem.h b/src/plugins/scxmleditor/plugin_interface/connectableitem.h
new file mode 100644
index 00000000000..93217ddfa07
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/connectableitem.h
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "baseitem.h"
+#include "transitionitem.h"
+
+#include <QPen>
+
+QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class CornerGrabberItem;
+class HighlightItem;
+class QuickTransitionItem;
+
+/**
+ * @brief The ConnectableItem class is the base class for all draggable state-items.
+ */
+class ConnectableItem : public BaseItem
+{
+ Q_OBJECT
+
+public:
+ explicit ConnectableItem(const QPointF &pos, BaseItem *parent = nullptr);
+ ~ConnectableItem() override;
+
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+ bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override;
+
+ void addOutputTransition(TransitionItem *transition);
+ void addInputTransition(TransitionItem *transition);
+ void removeOutputTransition(TransitionItem *transition);
+ void removeInputTransition(TransitionItem *transition);
+ QVector<TransitionItem*> outputTransitions() const;
+ QVector<TransitionItem*> inputTransitions() const;
+
+ void setMinimumWidth(int width);
+ void setMinimumHeight(int height);
+ int minHeight() const
+ {
+ return m_minimumHeight;
+ }
+
+ int minWidth() const
+ {
+ return m_minimumWidth;
+ }
+
+ void finalizeCreation() override;
+ void init(ScxmlTag *tag, BaseItem *parentItem = nullptr, bool initChildren = true, bool blockUpdates = false) override;
+
+ void updateUIProperties() override;
+ void updateAttributes() override;
+ void updateEditorInfo(bool allChildren = false) override;
+ void moveStateBy(qreal dx, qreal dy) override;
+ void setHighlight(bool hl) override;
+
+ int transitionCount() const;
+ int outputTransitionCount() const;
+ int inputTransitionCount() const;
+ bool hasInputTransitions(const ConnectableItem *parentItem, bool checkChildren = false) const;
+ bool hasOutputTransitions(const ConnectableItem *parentItem, bool checkChildren = false) const;
+ QPointF getInternalPosition(const TransitionItem *transition, TransitionItem::TransitionTargetType type) const;
+
+ void updateOutputTransitions();
+ void updateInputTransitions();
+ void updateTransitions(bool allChildren = false);
+ void updateTransitionAttributes(bool allChildren = false);
+
+ void addOverlappingItem(ConnectableItem *item);
+ void removeOverlappingItem(ConnectableItem *item);
+ void checkOverlapping() override;
+
+ // Parent change
+ virtual void releaseFromParent();
+ virtual void connectToParent(BaseItem *parentItem);
+
+ qreal getOpacity();
+
+protected:
+ void readUISpecifiedProperties(const ScxmlTag *tag) override;
+ void addTransitions(const ScxmlTag *tag);
+ void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
+
+ virtual bool canStartTransition(ItemType type) const;
+ virtual void transitionCountChanged();
+ virtual void transitionsChanged();
+
+private:
+ void updateCornerPositions();
+ void createCorners();
+ void removeCorners();
+ void updateShadowClipRegion();
+
+ QVector<TransitionItem*> m_outputTransitions;
+ QVector<TransitionItem*> m_inputTransitions;
+ QVector<CornerGrabberItem*> m_corners;
+ QVector<QuickTransitionItem*> m_quickTransitions;
+ HighlightItem *m_highlighItem = nullptr;
+ TransitionItem *m_newTransition = nullptr;
+ QPen m_selectedPen;
+ QBrush m_releasedFromParentBrush;
+ int m_minimumWidth = 120;
+ int m_minimumHeight = 100;
+ bool m_releasedFromParent = false;
+ int m_releasedIndex = -1;
+ QGraphicsItem *m_releasedParent = nullptr;
+ QPointF m_newTransitionStartedPoint;
+ QPainterPath m_shadowClipPath;
+ QPointF m_moveStartPoint;
+ bool m_moveMacroStarted = false;
+ QVector<ConnectableItem*> m_overlappedItems;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/cornergrabberitem.cpp b/src/plugins/scxmleditor/plugin_interface/cornergrabberitem.cpp
new file mode 100644
index 00000000000..d2a4b02e6c6
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/cornergrabberitem.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "cornergrabberitem.h"
+
+#include <QCursor>
+#include <QGraphicsSceneMouseEvent>
+#include <QPainter>
+
+using namespace ScxmlEditor::PluginInterface;
+
+CornerGrabberItem::CornerGrabberItem(QGraphicsItem *parent, Qt::CursorShape cshape)
+ : QGraphicsObject(parent)
+ , m_cursorShape(cshape)
+{
+ setFlag(ItemIgnoresTransformations, true);
+ setParentItem(parent);
+ installSceneEventFilter(parent);
+ setZValue(500);
+ m_rect = QRectF(-5, -5, 10, 10);
+ m_drawingRect = m_rect.adjusted(2, 2, -2, -2);
+
+ setAcceptHoverEvents(true);
+}
+
+void CornerGrabberItem::updateCursor()
+{
+ setCursor(m_selected ? m_cursorShape : Qt::ArrowCursor);
+}
+
+void CornerGrabberItem::setSelected(bool para)
+{
+ m_selected = para;
+ updateCursor();
+ update();
+}
+
+void CornerGrabberItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
+{
+ Q_UNUSED(e)
+ setSelected(false);
+}
+
+void CornerGrabberItem::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
+{
+ Q_UNUSED(e)
+ setSelected(true);
+}
+
+void CornerGrabberItem::mousePressEvent(QGraphicsSceneMouseEvent *e)
+{
+ m_pressPoint = e->pos();
+}
+
+void CornerGrabberItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
+
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(isEnabled() ? QColor(0x62, 0x62, 0xf9) : QColor(0x12, 0x12, 0x12));
+
+ if (m_grabberType == Square)
+ painter->drawRect(m_drawingRect);
+ else
+ painter->drawEllipse(m_drawingRect);
+
+ painter->restore();
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/cornergrabberitem.h b/src/plugins/scxmleditor/plugin_interface/cornergrabberitem.h
new file mode 100644
index 00000000000..f2af0029b51
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/cornergrabberitem.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "baseitem.h"
+
+QT_FORWARD_DECLARE_CLASS(QGraphicsSceneHoverEvent)
+QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+/**
+ * @brief The CornerGrabberItem class provides the way to connect/create item(s) straight from the second item.
+ *
+ */
+class CornerGrabberItem : public QGraphicsObject
+{
+public:
+ explicit CornerGrabberItem(QGraphicsItem *parent, Qt::CursorShape cshape = Qt::PointingHandCursor);
+
+ enum GrabberType {
+ Square,
+ Circle
+ };
+
+ /**
+ * @brief type of the item
+ */
+ int type() const override
+ {
+ return CornerGrabberType;
+ }
+
+ QRectF boundingRect() const override
+ {
+ return m_rect;
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+
+ QPointF pressedPoint()
+ {
+ return m_pressPoint;
+ }
+
+ void setGrabberType(GrabberType type)
+ {
+ m_grabberType = type;
+ }
+
+ void setSelected(bool para);
+ bool isSelected() const
+ {
+ return m_selected;
+ }
+
+private:
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *e) override;
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *e) override;
+ void mousePressEvent(QGraphicsSceneMouseEvent *e) override;
+ void updateCursor();
+
+ QRectF m_rect, m_drawingRect;
+ QPointF m_pressPoint;
+ GrabberType m_grabberType = Square;
+ Qt::CursorShape m_cursorShape;
+ bool m_selected = false;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/finalstateitem.cpp b/src/plugins/scxmleditor/plugin_interface/finalstateitem.cpp
new file mode 100644
index 00000000000..a94d52d3406
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/finalstateitem.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "finalstateitem.h"
+#include <QPainter>
+
+using namespace ScxmlEditor::PluginInterface;
+
+FinalStateItem::FinalStateItem(const QPointF &pos, BaseItem *parent)
+ : ConnectableItem(pos, parent)
+{
+ setItemBoundingRect(QRectF(-20, -20, 40, 40));
+ setMinimumHeight(40);
+ setMinimumWidth(40);
+
+ m_pen.setColor(qRgb(0x12, 0x12, 0x12));
+ m_pen.setWidth(2);
+}
+
+void FinalStateItem::updatePolygon()
+{
+ QRectF r = boundingRect();
+ m_size = qMin(r.width() * 0.45, r.height() * 0.45);
+ QPointF center = r.center();
+
+ m_polygon.clear();
+ m_polygon << (center + QPointF(-m_size, -m_size))
+ << (center + QPointF(m_size, -m_size))
+ << (center + QPointF(m_size, m_size))
+ << (center + QPointF(-m_size, m_size))
+ << (center + QPointF(-m_size, -m_size));
+}
+
+void FinalStateItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ ConnectableItem::paint(painter, option, widget);
+
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setOpacity(getOpacity());
+
+ painter->setBrush(QColor(0xff, 0xff, 0xff));
+ m_pen.setColor(overlapping() ? qRgb(0xff, 0x00, 0x60) : qRgb(0x45, 0x45, 0x45));
+ painter->setPen(m_pen);
+ painter->drawEllipse(boundingRect().center(), m_size, m_size);
+
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(QColor(0x4d, 0x4d, 0x4d));
+ painter->drawEllipse(boundingRect().center(), m_size * 0.8, m_size * 0.8);
+
+ painter->restore();
+}
+
+bool FinalStateItem::canStartTransition(ItemType type) const
+{
+ Q_UNUSED(type)
+ return false;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/finalstateitem.h b/src/plugins/scxmleditor/plugin_interface/finalstateitem.h
new file mode 100644
index 00000000000..b1db4bb9c84
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/finalstateitem.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "connectableitem.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+/**
+ * @brief The FinalStateItem class represents Final-state of the SCXML-standard. It is a extended class from the ConnectableItem.
+ */
+class FinalStateItem : public ConnectableItem
+{
+ Q_OBJECT
+
+public:
+ FinalStateItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
+
+ int type() const override
+ {
+ return FinalStateType;
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+
+protected:
+ void updatePolygon() override;
+ bool canStartTransition(ItemType type) const override;
+
+private:
+ qreal m_size;
+ QPen m_pen;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/genericscxmlplugin.cpp b/src/plugins/scxmleditor/plugin_interface/genericscxmlplugin.cpp
new file mode 100644
index 00000000000..031aff7f350
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/genericscxmlplugin.cpp
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "genericscxmlplugin.h"
+#include "mytypes.h"
+#include "scattributeitemdelegate.h"
+#include "scattributeitemmodel.h"
+#include "scgraphicsitemprovider.h"
+#include "scshapeprovider.h"
+#include "scutilsprovider.h"
+#include "scxmluifactory.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+GenericScxmlPlugin::GenericScxmlPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+GenericScxmlPlugin::~GenericScxmlPlugin()
+{
+ delete m_attributeItemDelegate;
+ delete m_attributeItemModel;
+ delete m_graphicsItemProvider;
+ delete m_shapeProvider;
+ delete m_utilsProvider;
+}
+
+void GenericScxmlPlugin::documentChanged(DocumentChangeType type, ScxmlDocument *doc)
+{
+ Q_UNUSED(type)
+ Q_UNUSED(doc)
+}
+
+void GenericScxmlPlugin::detach()
+{
+ m_factory->unregisterObject("attributeItemDelegate", m_attributeItemDelegate);
+ m_factory->unregisterObject("attributeItemModel", m_attributeItemModel);
+ m_factory->unregisterObject("graphicsItemProvider", m_graphicsItemProvider);
+ m_factory->unregisterObject("shapeProvider", m_shapeProvider);
+ m_factory->unregisterObject("utilsProvider", m_utilsProvider);
+}
+
+void GenericScxmlPlugin::init(ScxmlUiFactory *factory)
+{
+ m_factory = factory;
+
+ m_attributeItemDelegate = new SCAttributeItemDelegate;
+ m_attributeItemModel = new SCAttributeItemModel;
+ m_graphicsItemProvider = new SCGraphicsItemProvider;
+ m_shapeProvider = new SCShapeProvider;
+ m_utilsProvider = new SCUtilsProvider;
+
+ m_factory->registerObject("attributeItemDelegate", m_attributeItemDelegate);
+ m_factory->registerObject("attributeItemModel", m_attributeItemModel);
+ m_factory->registerObject("graphicsItemProvider", m_graphicsItemProvider);
+ m_factory->registerObject("shapeProvider", m_shapeProvider);
+ m_factory->registerObject("utilsProvider", m_utilsProvider);
+}
+
+void GenericScxmlPlugin::refresh()
+{
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/genericscxmlplugin.h b/src/plugins/scxmleditor/plugin_interface/genericscxmlplugin.h
new file mode 100644
index 00000000000..cbf166dc6ff
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/genericscxmlplugin.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "isceditor.h"
+#include "mytypes.h"
+
+#include <QObject>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ScxmlUiFactory;
+class SCAttributeItemDelegate;
+class SCAttributeItemModel;
+class ScxmlDocument;
+class SCGraphicsItemProvider;
+class SCShapeProvider;
+class SCUtilsProvider;
+
+class GenericScxmlPlugin : public QObject, public ISCEditor
+{
+ Q_OBJECT
+ Q_INTERFACES(ScxmlEditor::PluginInterface::ISCEditor)
+
+public:
+ GenericScxmlPlugin(QObject *parent = nullptr);
+ ~GenericScxmlPlugin() override;
+
+ void init(ScxmlUiFactory *factory) override;
+ void detach() override;
+ void documentChanged(DocumentChangeType type, ScxmlDocument *doc) override;
+ void refresh() override;
+
+private:
+ ScxmlUiFactory *m_factory = nullptr;
+ SCAttributeItemDelegate *m_attributeItemDelegate = nullptr;
+ SCAttributeItemModel *m_attributeItemModel = nullptr;
+ SCGraphicsItemProvider *m_graphicsItemProvider = nullptr;
+ SCShapeProvider *m_shapeProvider = nullptr;
+ SCUtilsProvider *m_utilsProvider = nullptr;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/graphicsitemprovider.h b/src/plugins/scxmleditor/plugin_interface/graphicsitemprovider.h
new file mode 100644
index 00000000000..e1196b68fa9
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/graphicsitemprovider.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QObject>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class BaseItem;
+class WarningItem;
+
+class GraphicsItemProvider : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit GraphicsItemProvider(QObject *parent = nullptr)
+ : QObject(parent)
+ {
+ }
+
+ virtual WarningItem *createWarningItem(const QString &key, BaseItem *parentItem) const = 0;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp b/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp
new file mode 100644
index 00000000000..abb3f6a4c8a
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp
@@ -0,0 +1,944 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "graphicsscene.h"
+#include "actionhandler.h"
+#include "actionprovider.h"
+#include "finalstateitem.h"
+#include "initialstateitem.h"
+#include "parallelitem.h"
+#include "sceneutils.h"
+#include "scxmleditorconstants.h"
+#include "scxmltagutils.h"
+#include "scxmluifactory.h"
+#include "snapline.h"
+#include "stateitem.h"
+#include "transitionitem.h"
+#include "utilsprovider.h"
+#include "warning.h"
+#include "warningitem.h"
+
+#include <utils/qtcassert.h>
+
+#include <QAction>
+#include <QGuiApplication>
+#include <QClipboard>
+#include <QMenu>
+#include <QMimeData>
+#include <QThread>
+#include <QUndoStack>
+
+using namespace ScxmlEditor::PluginInterface;
+
+GraphicsScene::GraphicsScene(QObject *parent)
+ : QGraphicsScene(parent)
+{
+ //setMinimumRenderSize(5);
+ setItemIndexMethod(QGraphicsScene::NoIndex);
+}
+
+GraphicsScene::~GraphicsScene()
+{
+ clear();
+}
+
+void GraphicsScene::unselectAll()
+{
+ const QList<QGraphicsItem*> selectedItems = this->selectedItems();
+ foreach (QGraphicsItem *it, selectedItems)
+ it->setSelected(false);
+ if (m_document)
+ m_document->setCurrentTag(0);
+}
+
+void GraphicsScene::unhighlightAll()
+{
+ foreach (BaseItem *it, m_baseItems)
+ it->setHighlight(false);
+}
+
+void GraphicsScene::highlightItems(const QVector<ScxmlTag*> &lstIds)
+{
+ foreach (BaseItem *it, m_baseItems)
+ it->setHighlight(lstIds.contains(it->tag()));
+}
+
+QRectF GraphicsScene::selectedBoundingRect() const
+{
+ QRectF r;
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected())
+ r = r.united(item->sceneBoundingRect());
+ }
+ return r;
+}
+
+qreal GraphicsScene::selectedMaxWidth() const
+{
+ qreal maxw = 0;
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= InitialStateType)
+ maxw = qMax(maxw, item->sceneBoundingRect().width());
+ }
+ return maxw;
+}
+
+qreal GraphicsScene::selectedMaxHeight() const
+{
+ qreal maxh = 0;
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= InitialStateType)
+ maxh = qMax(maxh, item->sceneBoundingRect().height());
+ }
+ return maxh;
+}
+
+void GraphicsScene::alignStates(int alignType)
+{
+ if (alignType >= ActionAlignLeft && alignType <= ActionAlignVertical) {
+ m_document->undoStack()->beginMacro(tr("Align states"));
+ QRectF r = selectedBoundingRect();
+
+ if (r.isValid()) {
+ switch (alignType) {
+ case ActionAlignLeft:
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= InitialStateType)
+ item->moveStateBy(r.left() - item->sceneBoundingRect().left(), 0);
+ }
+ break;
+ case ActionAlignRight:
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= InitialStateType)
+ item->moveStateBy(r.right() - item->sceneBoundingRect().right(), 0);
+ }
+ break;
+ case ActionAlignTop:
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= InitialStateType)
+ item->moveStateBy(0, r.top() - item->sceneBoundingRect().top());
+ }
+ break;
+ case ActionAlignBottom:
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= InitialStateType)
+ item->moveStateBy(0, r.bottom() - item->sceneBoundingRect().bottom());
+ }
+ break;
+ case ActionAlignHorizontal:
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= InitialStateType)
+ item->moveStateBy(0, r.center().y() - item->sceneBoundingRect().center().y());
+ }
+ break;
+ case ActionAlignVertical:
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= InitialStateType)
+ item->moveStateBy(r.center().x() - item->sceneBoundingRect().center().x(), 0);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ m_document->undoStack()->endMacro();
+ }
+}
+
+void GraphicsScene::adjustStates(int adjustType)
+{
+ if (adjustType >= ActionAdjustWidth && adjustType <= ActionAdjustSize) {
+ m_document->undoStack()->beginMacro(tr("Adjust states"));
+ qreal maxw = selectedMaxWidth();
+ qreal maxh = selectedMaxHeight();
+
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= InitialStateType) {
+ QRectF rr = item->boundingRect();
+ if ((adjustType == ActionAdjustWidth || adjustType == ActionAdjustSize) && !qFuzzyCompare(rr.width(), maxw))
+ rr.setWidth(maxw);
+ if ((adjustType == ActionAdjustHeight || adjustType == ActionAdjustSize) && !qFuzzyCompare(rr.height(), maxh))
+ rr.setHeight(maxh);
+
+ item->setItemBoundingRect(rr);
+ qgraphicsitem_cast<ConnectableItem*>(item)->updateTransitions(true);
+ }
+ }
+ m_document->undoStack()->endMacro();
+ }
+}
+void GraphicsScene::cut()
+{
+ m_document->undoStack()->beginMacro(tr("Cut"));
+ copy();
+ removeSelectedItems();
+ m_document->undoStack()->endMacro();
+}
+
+void GraphicsScene::removeSelectedItems()
+{
+ QVector<ScxmlTag*> tags = SceneUtils::findRemovedTags(m_baseItems);
+ if (tags.count() > 0) {
+ m_document->undoStack()->beginMacro(tr("Remove item(s)"));
+
+ // Then remove found tags
+ for (int i = tags.count(); i--;) {
+ m_document->setCurrentTag(tags[i]);
+ m_document->removeTag(tags[i]);
+ }
+ m_document->setCurrentTag(0);
+ m_document->undoStack()->endMacro();
+ }
+}
+
+void GraphicsScene::copy()
+{
+ QPointF minPos;
+ QVector<ScxmlTag*> tags;
+ if (m_document->currentTag()->tagType() == Scxml) {
+ QVector<BaseItem*> items;
+ foreach (BaseItem *item, m_baseItems) {
+ if (!item->parentItem())
+ items << item;
+ }
+ tags = SceneUtils::findCopyTags(items, minPos);
+ } else {
+ tags = SceneUtils::findCopyTags(m_baseItems, minPos);
+ }
+
+ if (tags.isEmpty() && m_document->currentTag())
+ tags << m_document->currentTag();
+
+ if (tags.count() > 0) {
+ auto mime = new QMimeData;
+ QByteArray result = m_document->content(tags);
+ mime->setText(QLatin1String(result));
+ mime->setData("StateChartEditor/StateData", result);
+ QStringList strTypes;
+ foreach (const ScxmlTag *tag, tags) {
+ strTypes << tag->tagName(false);
+ }
+ mime->setData("StateChartEditor/CopiedTagTypes", strTypes.join(",").toLocal8Bit());
+ mime->setData("StateChartEditor/CopiedMinPos", QString::fromLatin1("%1:%2").arg(minPos.x()).arg(minPos.y()).toLocal8Bit());
+ QGuiApplication::clipboard()->setMimeData(mime);
+ }
+
+ checkPaste();
+}
+
+void GraphicsScene::checkPaste()
+{
+ const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData();
+ const QString copiedTagTypes = QLatin1String(mimeData->data("StateChartEditor/CopiedTagTypes"));
+
+ emit pasteAvailable(TagUtils::checkPaste(copiedTagTypes, m_document->currentTag()));
+}
+
+void GraphicsScene::paste(const QPointF &targetPos)
+{
+ const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData();
+
+ QPointF startPos(targetPos);
+
+ BaseItem *targetItem = nullptr;
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= StateType) {
+ targetItem = item;
+ break;
+ }
+ }
+
+ if (m_lastPasteTargetItem != targetItem)
+ m_pasteCounter = 0;
+ m_lastPasteTargetItem = targetItem;
+
+ if (m_lastPasteTargetItem)
+ startPos = m_lastPasteTargetItem->boundingRect().topLeft();
+ QPointF pastedPos = startPos + QPointF(m_pasteCounter * 30, m_pasteCounter * 30);
+ m_pasteCounter++;
+
+ QString strMinPos = QLatin1String(mimeData->data("StateChartEditor/CopiedMinPos"));
+ QPointF minPos(0, 0);
+ if (!strMinPos.isEmpty()) {
+ QStringList coords = strMinPos.split(":", QString::SkipEmptyParts);
+ if (coords.count() == 2)
+ minPos = QPointF(coords[0].toDouble(), coords[1].toDouble());
+ }
+
+ m_document->pasteData(mimeData->data("StateChartEditor/StateData"), minPos, pastedPos);
+}
+
+void GraphicsScene::setEditorInfo(const QString &key, const QString &value)
+{
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected() && item->type() >= TransitionType)
+ item->setEditorInfo(key, value);
+ }
+}
+
+void GraphicsScene::setDocument(ScxmlDocument *document)
+{
+ if (m_document)
+ disconnect(m_document, 0, this, 0);
+
+ m_document = document;
+
+ init();
+ connectDocument();
+}
+
+void GraphicsScene::connectDocument()
+{
+ if (m_document) {
+ connect(m_document, &ScxmlDocument::beginTagChange, this, &GraphicsScene::beginTagChange);
+ connect(m_document, &ScxmlDocument::endTagChange, this, &GraphicsScene::endTagChange);
+ }
+}
+
+void GraphicsScene::disconnectDocument()
+{
+ if (m_document)
+ m_document->disconnect(this);
+}
+
+void GraphicsScene::init()
+{
+ m_initializing = true;
+
+ disconnectDocument();
+ clear();
+ addItem(m_lineX = new SnapLine);
+ addItem(m_lineY = new SnapLine);
+
+ if (m_document) {
+ const ScxmlTag *rootTag = m_document->rootTag();
+ if (rootTag) {
+
+ for (int i = 0; i < rootTag->childCount(); ++i) {
+ ScxmlTag *child = rootTag->child(i);
+ ConnectableItem *newItem = SceneUtils::createItemByTagType(child->tagType());
+ if (newItem) {
+ addItem(newItem);
+ newItem->init(child);
+ }
+ }
+
+ const QList<QGraphicsItem*> items = this->items();
+ for (int i = 0; i < items.count(); ++i) {
+ if (items[i]->type() >= TransitionType) {
+ auto item = qgraphicsitem_cast<BaseItem*>(items[i]);
+ if (item)
+ item->finalizeCreation();
+ }
+ }
+ }
+ }
+
+ m_initializing = false;
+ warningVisibilityChanged(0, 0);
+ emit selectedStateCountChanged(0);
+ emit selectedBaseItemCountChanged(0);
+}
+
+void GraphicsScene::runLayoutToSelectedStates()
+{
+ m_document->undoStack()->beginMacro(tr("Relayout"));
+
+ QVector<BaseItem*> selectedItems;
+ foreach (BaseItem *node, m_baseItems) {
+ if (node->isSelected()) {
+ int index = 0;
+ for (int i = 0; i < selectedItems.count(); ++i) {
+ if (node->depth() <= selectedItems[i]->depth()) {
+ index = i;
+ break;
+ }
+ }
+ selectedItems.insert(index, node);
+ }
+ }
+
+ // Layout selected items
+ for (int i = 0; i < selectedItems.count(); ++i)
+ selectedItems[i]->doLayout(selectedItems[i]->depth());
+
+ // Layout scene items if necessary
+ if (selectedItems.isEmpty()) {
+ QList<QGraphicsItem*> sceneItems;
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->type() >= InitialStateType && !item->parentItem())
+ sceneItems << item;
+ }
+ SceneUtils::layout(sceneItems);
+
+ foreach (QGraphicsItem *item, sceneItems) {
+ if (item->type() >= StateType)
+ static_cast<StateItem*>(item)->shrink();
+ }
+ }
+
+ // Update properties
+ foreach (BaseItem *node, selectedItems) {
+ node->updateUIProperties();
+ }
+
+ m_document->undoStack()->endMacro();
+}
+
+void GraphicsScene::runAutomaticLayout()
+{
+ m_autoLayoutRunning = true;
+
+ // 1. Find max depth
+ int maxDepth = 0;
+ foreach (BaseItem *node, m_baseItems) {
+ maxDepth = qMax(maxDepth, node->depth());
+ node->setBlockUpdates(true);
+ }
+
+ // 2. Layout every depth-level separately
+ for (int d = (maxDepth + 1); d--;) {
+ foreach (BaseItem *node, m_baseItems)
+ node->doLayout(d);
+ }
+
+ // 3. Layout scene items
+ QList<QGraphicsItem*> sceneItems;
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->type() >= InitialStateType && !item->parentItem())
+ sceneItems << item;
+ }
+ SceneUtils::layout(sceneItems);
+
+ foreach (QGraphicsItem *item, sceneItems) {
+ if (item->type() >= StateType)
+ static_cast<StateItem*>(item)->shrink();
+ }
+
+ foreach (BaseItem *node, m_baseItems) {
+ node->updateUIProperties();
+ node->setBlockUpdates(false);
+ }
+
+ m_autoLayoutRunning = false;
+}
+
+void GraphicsScene::beginTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value)
+{
+ switch (change) {
+ case ScxmlDocument::TagRemoveChild: {
+ if (tag)
+ removeItems(tag->child(value.toInt()));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value)
+{
+ Q_UNUSED(value)
+ QTC_ASSERT(tag, return);
+
+ switch (change) {
+ case ScxmlDocument::TagAttributesChanged: {
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->tag() == tag)
+ item->updateAttributes();
+ }
+ break;
+ }
+ case ScxmlDocument::TagEditorInfoChanged: {
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->tag() == tag)
+ item->updateEditorInfo();
+ }
+ break;
+ }
+ case ScxmlDocument::TagCurrentChanged: {
+ foreach (BaseItem *item, m_baseItems) {
+ if (!item->isSelected() && item->tag() == tag)
+ item->setSelected(true);
+ }
+ checkPaste();
+ break;
+ }
+ case ScxmlDocument::TagChangeParent: {
+ auto childItem = qobject_cast<ConnectableItem*>(findItem(tag));
+
+ if (childItem) {
+ BaseItem *newParentItem = findItem(tag->parentTag());
+ BaseItem *oldParentItem = childItem ? childItem->parentBaseItem() : nullptr;
+
+ QPointF sPos = childItem->scenePos();
+ if (oldParentItem) {
+ childItem->setParentItem(nullptr);
+ childItem->setPos(sPos);
+ }
+
+ if (newParentItem)
+ childItem->setPos(newParentItem->mapFromScene(childItem->sceneBoundingRect().center()) - childItem->boundingRect().center());
+
+ childItem->setParentItem(newParentItem);
+ childItem->updateUIProperties();
+ childItem->updateTransitions(true);
+ childItem->updateTransitionAttributes(true);
+ childItem->checkWarnings();
+ childItem->checkInitial();
+ if (newParentItem) {
+ newParentItem->checkInitial();
+ newParentItem->updateAttributes();
+ newParentItem->checkWarnings();
+ newParentItem->checkOverlapping();
+ newParentItem->updateUIProperties();
+ }
+
+ if (oldParentItem)
+ oldParentItem->checkInitial();
+
+ if (oldParentItem == nullptr || newParentItem == nullptr)
+ checkInitialState();
+ }
+ break;
+ }
+ case ScxmlDocument::TagAddTags: {
+ // Finalize transitions
+ QVector<ScxmlTag*> childTransitionTags;
+ if (tag->tagName(false) == "transition")
+ childTransitionTags << tag;
+
+ TagUtils::findAllTransitionChildren(tag, childTransitionTags);
+ for (int i = 0; i < childTransitionTags.count(); ++i) {
+ BaseItem *item = findItem(childTransitionTags[i]);
+ if (item)
+ item->finalizeCreation();
+ }
+ }
+ // FIXME: intended fallthrough?
+ case ScxmlDocument::TagAddChild: {
+ ScxmlTag *childTag = tag->child(value.toInt());
+ if (childTag) {
+ // Check that there is no any item with this tag
+ BaseItem *childItem = findItem(childTag);
+ BaseItem *parentItem = findItem(tag);
+ if (!childItem) {
+ if (childTag->tagType() == Transition || childTag->tagType() == InitialTransition) {
+ auto transition = new TransitionItem;
+ addItem(transition);
+ transition->setStartItem(qgraphicsitem_cast<ConnectableItem*>(parentItem));
+ transition->init(childTag, 0, false, false);
+ transition->updateAttributes();
+ } else {
+ childItem = SceneUtils::createItemByTagType(childTag->tagType(), QPointF());
+ if (childItem) {
+ childItem->init(childTag, parentItem, false);
+ if (!parentItem)
+ addItem(childItem);
+
+ childItem->finalizeCreation();
+ childItem->updateUIProperties();
+ }
+ }
+ }
+
+ if (parentItem) {
+ parentItem->updateAttributes();
+ parentItem->updateUIProperties();
+ parentItem->checkInitial();
+ } else
+ checkInitialState();
+ }
+ break;
+ }
+ case ScxmlDocument::TagRemoveChild: {
+ BaseItem *parentItem = findItem(tag);
+ if (parentItem) {
+ parentItem->updateAttributes();
+ parentItem->checkInitial();
+ } else {
+ checkInitialState();
+ }
+ break;
+ }
+ case ScxmlDocument::TagChangeOrder: {
+ BaseItem *parentItem = findItem(tag->parentTag());
+ if (parentItem)
+ parentItem->updateAttributes();
+ else
+ checkInitialState();
+ }
+ default:
+ break;
+ }
+}
+
+void GraphicsScene::setTopMostScene(bool topmost)
+{
+ m_topMostScene = topmost;
+}
+
+bool GraphicsScene::topMostScene() const
+{
+ return m_topMostScene;
+}
+
+void GraphicsScene::setActionHandler(ActionHandler *mgr)
+{
+ m_actionHandler = mgr;
+}
+
+void GraphicsScene::setWarningModel(ScxmlEditor::OutputPane::WarningModel *model)
+{
+ m_warningModel = model;
+}
+
+void GraphicsScene::setUiFactory(ScxmlUiFactory *uifactory)
+{
+ m_uiFactory = uifactory;
+}
+
+ActionHandler *GraphicsScene::actionHandler() const
+{
+ return m_actionHandler;
+}
+
+ScxmlEditor::OutputPane::WarningModel *GraphicsScene::warningModel() const
+{
+ return m_warningModel;
+}
+
+ScxmlUiFactory *GraphicsScene::uiFactory() const
+{
+ return m_uiFactory;
+}
+
+void GraphicsScene::addConnectableItem(ItemType type, const QPointF &pos, BaseItem *parentItem)
+{
+ m_document->undoStack()->beginMacro(tr("Add new state"));
+ ConnectableItem *newItem = SceneUtils::createItem(type, pos);
+
+ if (newItem) {
+ ScxmlTag *newTag = SceneUtils::createTag(type, m_document);
+ ScxmlTag *parentTag = parentItem ? parentItem->tag() : m_document->rootTag();
+
+ newItem->setTag(newTag);
+ newItem->setParentItem(parentItem);
+ if (!parentItem)
+ addItem(newItem);
+
+ newItem->updateAttributes();
+ newItem->updateEditorInfo();
+ newItem->updateUIProperties();
+
+ if (parentItem)
+ parentItem->updateUIProperties();
+
+ m_document->addTag(parentTag, newTag);
+ unselectAll();
+ newItem->setSelected(true);
+ }
+ m_document->undoStack()->endMacro();
+}
+
+void GraphicsScene::keyPressEvent(QKeyEvent *event)
+{
+ QGraphicsItem *focusItem = this->focusItem();
+ if (focusItem == nullptr || focusItem->type() != TextType) {
+ if (event->key() == Qt::Key_Delete)
+ removeSelectedItems();
+ }
+ QGraphicsScene::keyPressEvent(event);
+}
+
+void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ QGraphicsItem *it = itemAt(event->scenePos(), QTransform());
+ if (it == nullptr || it->type() == LayoutType) {
+ if (event->button() == Qt::LeftButton) {
+ QGraphicsScene::mousePressEvent(event);
+ m_document->setCurrentTag(m_document->rootTag());
+ return;
+ } else if (m_actionHandler && event->button() == Qt::RightButton) {
+ event->accept();
+ QMenu menu;
+ menu.addAction(m_actionHandler->action(ActionCopy));
+ menu.addAction(m_actionHandler->action(ActionPaste));
+ menu.addAction(m_actionHandler->action(ActionScreenshot));
+ menu.addAction(m_actionHandler->action(ActionExportToImage));
+ menu.addSeparator();
+ menu.addAction(m_actionHandler->action(ActionZoomIn));
+ menu.addAction(m_actionHandler->action(ActionZoomOut));
+ menu.addAction(m_actionHandler->action(ActionFitToView));
+
+ if (m_uiFactory) {
+ auto actionProvider = static_cast<ActionProvider*>(m_uiFactory->object(Constants::C_UI_FACTORY_OBJECT_ACTIONPROVIDER));
+ if (actionProvider) {
+ menu.addSeparator();
+ actionProvider->initStateMenu(m_document->rootTag(), &menu);
+ }
+ }
+
+ menu.exec(event->screenPos());
+ return;
+ }
+ }
+ QGraphicsScene::mousePressEvent(event);
+}
+
+BaseItem *GraphicsScene::findItem(const ScxmlTag *tag) const
+{
+ if (!tag)
+ return nullptr;
+
+ foreach (BaseItem *it, m_baseItems) {
+ if (it->tag() == tag)
+ return it;
+ }
+
+ return nullptr;
+}
+
+void GraphicsScene::removeItems(const ScxmlTag *tag)
+{
+ if (tag) {
+ // Find right items
+ QVector<BaseItem*> items;
+ foreach (BaseItem *it, m_baseItems) {
+ if (it->tag() == tag)
+ items << it;
+ }
+
+ // Then delete them
+ for (int i = items.count(); i--;) {
+ items[i]->setTag(0);
+ delete items[i];
+ }
+ }
+}
+
+QPair<bool, bool> GraphicsScene::checkSnapToItem(BaseItem *item, const QPointF &p, QPointF &pp)
+{
+ if (m_selectedStateCount > 1)
+ return QPair<bool, bool>(false, false);
+
+ QGraphicsItem *parentItem = item->parentItem();
+
+ qreal diffX = 8;
+ qreal diffXdY = 2000;
+
+ qreal diffY = 8;
+ qreal diffYdX = 2000;
+
+ foreach (BaseItem *it, m_baseItems) {
+ if (!it->isSelected() && it != item && it->parentItem() == parentItem && it->type() >= InitialStateType) {
+ QPointF c = it->sceneCenter();
+ qreal dX = qAbs(c.x() - p.x());
+ qreal dY = qAbs(c.y() - p.y());
+
+ if (dX < 7 && dY < diffXdY) {
+ pp.setX(c.x());
+ diffX = dX;
+ diffXdY = dY;
+ m_lineY->show(c.x(), c.y(), c.x(), p.y());
+ }
+
+ if (dY < 7 && dX < diffYdX) {
+ pp.setY(c.y());
+ diffY = dY;
+ diffYdX = dX;
+ m_lineX->show(c.x(), c.y(), p.x(), c.y());
+ }
+ }
+ }
+
+ if (qFuzzyCompare(diffX, 8))
+ m_lineY->hideLine();
+ if (qFuzzyCompare(diffY, 8))
+ m_lineX->hideLine();
+
+ return QPair<bool, bool>(diffX < 8, diffY < 8);
+}
+
+void GraphicsScene::selectionChanged(bool para)
+{
+ Q_UNUSED(para)
+
+ int count = 0;
+ int baseCount = 0;
+ int stateTypeCount = 0;
+
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->isSelected()) {
+ if (item->type() >= TransitionType)
+ baseCount++;
+ if (item->type() >= InitialStateType)
+ count++;
+ if (item->type() >= StateType)
+ stateTypeCount++;
+ }
+ }
+
+ m_selectedStateTypeCount = stateTypeCount;
+
+ if (count != m_selectedStateCount) {
+ m_selectedStateCount = count;
+ emit selectedStateCountChanged(m_selectedStateCount);
+ }
+
+ if (baseCount != m_selectedBaseItemCount) {
+ m_selectedBaseItemCount = baseCount;
+ emit selectedBaseItemCountChanged(m_selectedBaseItemCount);
+ }
+}
+
+void GraphicsScene::addWarningItem(WarningItem *item)
+{
+ if (!m_allWarnings.contains(item)) {
+ m_allWarnings << item;
+ if (!m_autoLayoutRunning && !m_initializing)
+ QMetaObject::invokeMethod(this, "warningVisibilityChanged", Qt::QueuedConnection, Q_ARG(int, 0));
+ }
+}
+
+void GraphicsScene::removeWarningItem(WarningItem *item)
+{
+ m_allWarnings.removeAll(item);
+
+ if (!m_autoLayoutRunning && !m_initializing)
+ QMetaObject::invokeMethod(this, "warningVisibilityChanged", Qt::QueuedConnection, Q_ARG(int, 0));
+}
+
+void GraphicsScene::warningVisibilityChanged(int type, WarningItem *item)
+{
+ if (!m_autoLayoutRunning && !m_initializing) {
+ foreach (WarningItem *it, m_allWarnings) {
+ if (it != item && (type == 0 || it->type() == type))
+ it->check();
+ }
+ }
+}
+
+ScxmlTag *GraphicsScene::tagByWarning(const ScxmlEditor::OutputPane::Warning *w) const
+{
+ ScxmlTag *tag = nullptr;
+ foreach (WarningItem *it, m_allWarnings) {
+ if (it->warning() == w) {
+ tag = it->tag();
+ break;
+ }
+ }
+ return tag;
+}
+
+void GraphicsScene::highlightWarningItem(const ScxmlEditor::OutputPane::Warning *w)
+{
+ ScxmlTag *tag = tagByWarning(w);
+
+ if (tag)
+ highlightItems(QVector<ScxmlTag*>() << tag);
+ else
+ unhighlightAll();
+}
+
+void GraphicsScene::selectWarningItem(const ScxmlEditor::OutputPane::Warning *w)
+{
+ ScxmlTag *tag = tagByWarning(w);
+
+ if (tag) {
+ unselectAll();
+ m_document->setCurrentTag(tag);
+ }
+}
+
+QList<QGraphicsItem*> GraphicsScene::sceneItems(Qt::SortOrder order) const
+{
+ QList<QGraphicsItem*> children;
+ QList<QGraphicsItem*> items = this->items(order);
+ for (int i = 0; i < items.count(); ++i) {
+ if (items[i]->parentItem() == nullptr && items[i]->type() >= InitialStateType)
+ children << items[i];
+ }
+
+ return children;
+}
+
+void GraphicsScene::addChild(BaseItem *item)
+{
+ if (!m_baseItems.contains(item)) {
+ connect(item, &BaseItem::selectedStateChanged, this, &GraphicsScene::selectionChanged);
+ connect(item, &BaseItem::openToDifferentView, this, [=](BaseItem *item){
+ openStateView(item);
+ }, Qt::QueuedConnection);
+ m_baseItems << item;
+ }
+}
+
+void GraphicsScene::removeChild(BaseItem *item)
+{
+ if (item)
+ disconnect(item, 0, this, 0);
+ m_baseItems.removeAll(item);
+
+ selectionChanged(false);
+}
+
+void GraphicsScene::checkItemsVisibility(double scaleFactor)
+{
+ foreach (BaseItem *item, m_baseItems) {
+ item->checkVisibility(scaleFactor);
+ }
+}
+
+void GraphicsScene::checkInitialState()
+{
+ if (m_document) {
+ QList<QGraphicsItem*> sceneItems;
+ foreach (BaseItem *item, m_baseItems) {
+ if (item->type() >= InitialStateType && !item->parentItem())
+ sceneItems << item;
+ }
+ if (m_uiFactory) {
+ auto utilsProvider = static_cast<UtilsProvider*>(m_uiFactory->object("utilsProvider"));
+ if (utilsProvider)
+ utilsProvider->checkInitialState(sceneItems, m_document->rootTag());
+ }
+ }
+}
+
+void GraphicsScene::clearAllTags()
+{
+ foreach (BaseItem *it, m_baseItems) {
+ it->setTag(0);
+ }
+}
+
+void GraphicsScene::setBlockUpdates(bool block)
+{
+ foreach (BaseItem *it, m_baseItems) {
+ it->setBlockUpdates(block);
+ }
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/graphicsscene.h b/src/plugins/scxmleditor/plugin_interface/graphicsscene.h
new file mode 100644
index 00000000000..6405c2cd052
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/graphicsscene.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "baseitem.h"
+#include "scxmldocument.h"
+
+#include <QGraphicsLineItem>
+#include <QGraphicsScene>
+
+QT_FORWARD_DECLARE_CLASS(QAction)
+QT_FORWARD_DECLARE_CLASS(QKeyEvent)
+
+namespace ScxmlEditor {
+
+namespace OutputPane {
+class Warning;
+class WarningModel;
+} // namespace OutputPane
+
+namespace PluginInterface {
+
+class ActionHandler;
+class ScxmlUiFactory;
+class SnapLine;
+class WarningItem;
+
+class GraphicsScene : public QGraphicsScene
+{
+ Q_OBJECT
+
+public:
+ explicit GraphicsScene(QObject *parent = nullptr);
+ ~GraphicsScene() override;
+
+ QPair<bool, bool> checkSnapToItem(BaseItem *item, const QPointF &p, QPointF &pp);
+ void checkItemsVisibility(double scaleFactor);
+ void checkInitialState();
+ void clearAllTags();
+ void setBlockUpdates(bool block);
+
+ QList<QGraphicsItem*> sceneItems(Qt::SortOrder order) const;
+ ScxmlTag *tagByWarning(const OutputPane::Warning *w) const;
+ void highlightWarningItem(const OutputPane::Warning *w);
+ void selectWarningItem(const OutputPane::Warning *w);
+
+ QRectF selectedBoundingRect() const;
+ BaseItem *findItem(const ScxmlTag *tag) const;
+ bool topMostScene() const;
+
+ void setActionHandler(ActionHandler *mgr);
+ void setWarningModel(OutputPane::WarningModel *model);
+ void setUiFactory(ScxmlUiFactory *uifactory);
+
+ void setEditorInfo(const QString &key, const QString &value);
+ void setDocument(ScxmlDocument *document);
+ void unselectAll();
+ void unhighlightAll();
+ void highlightItems(const QVector<ScxmlTag*> &lstIds);
+ void addConnectableItem(ItemType type, const QPointF &pos, BaseItem *parentItem);
+ void runAutomaticLayout();
+ void runLayoutToSelectedStates();
+ void alignStates(int alignType);
+ void adjustStates(int adjustType);
+ void copy();
+ void cut();
+ void removeSelectedItems();
+ void checkPaste();
+ void paste(const QPointF &targetPos);
+ void setTopMostScene(bool topmost);
+
+ ActionHandler *actionHandler() const;
+ OutputPane::WarningModel *warningModel() const;
+ ScxmlUiFactory *uiFactory() const;
+
+signals:
+ void openStateView(BaseItem *item);
+
+protected:
+ void keyPressEvent(QKeyEvent *event) override;
+ void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
+
+signals:
+ void selectedStateCountChanged(int count);
+ void selectedBaseItemCountChanged(int count);
+ void pasteAvailable(bool para);
+
+private slots:
+ void warningVisibilityChanged(int type, WarningItem *item = nullptr);
+
+private:
+ void beginTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value);
+ void endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value);
+ void selectionChanged(bool para);
+
+ friend class BaseItem;
+ friend class WarningItem;
+
+ void init();
+ qreal selectedMaxWidth() const;
+ qreal selectedMaxHeight() const;
+ void removeItems(const ScxmlTag *tag);
+ void addChild(BaseItem *item);
+ void removeChild(BaseItem *item);
+ void addWarningItem(WarningItem *item);
+ void removeWarningItem(WarningItem *item);
+ void connectDocument();
+ void disconnectDocument();
+
+ QPointer<ActionHandler> m_actionHandler;
+ QPointer<OutputPane::WarningModel> m_warningModel;
+ QPointer<ScxmlUiFactory> m_uiFactory;
+ QPointer<ScxmlDocument> m_document;
+ QVector<BaseItem*> m_baseItems;
+ QVector<WarningItem*> m_allWarnings;
+ int m_pasteCounter = 0;
+ QPointer<BaseItem> m_lastPasteTargetItem;
+ SnapLine *m_lineX = nullptr;
+ SnapLine *m_lineY = nullptr;
+ int m_selectedStateCount = 0;
+ int m_selectedBaseItemCount = 0;
+ int m_selectedStateTypeCount = 0;
+ bool m_autoLayoutRunning = false;
+ bool m_initializing = false;
+ bool m_topMostScene = false;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/highlightitem.cpp b/src/plugins/scxmleditor/plugin_interface/highlightitem.cpp
new file mode 100644
index 00000000000..3f07fd33706
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/highlightitem.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "highlightitem.h"
+#include <QPainter>
+
+using namespace ScxmlEditor::PluginInterface;
+
+HighlightItem::HighlightItem(BaseItem *baseItem)
+ : QGraphicsObject(nullptr)
+ , m_baseItem(baseItem)
+{
+ m_pen = QPen(QColor(0xff, 0x00, 0x60));
+ m_pen.setWidth(2);
+ m_pen.setStyle(Qt::DashLine);
+ m_pen.setCosmetic(true);
+
+ setZValue(1000);
+}
+
+void HighlightItem::advance(int phase)
+{
+ Q_UNUSED(phase)
+
+ prepareGeometryChange();
+
+ if (m_baseItem) {
+ setPos(m_baseItem->scenePos());
+ m_boundingRect = m_baseItem->boundingRect();
+ }
+ update();
+}
+
+QRectF HighlightItem::boundingRect() const
+{
+ return m_boundingRect;
+}
+
+void HighlightItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
+
+ if (m_baseItem) {
+ painter->save();
+ painter->setRenderHints(QPainter::Antialiasing, true);
+
+ QRectF br = m_baseItem->polygonShape().boundingRect();
+
+ switch (m_baseItem->type()) {
+ case StateType:
+ case ParallelType: {
+ painter->setOpacity(1.0);
+ painter->setPen(m_pen);
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRoundedRect(br, 10, 10);
+ break;
+ }
+ case InitialStateType:
+ case HistoryType:
+ case FinalStateType: {
+ painter->setOpacity(1.0);
+ painter->setPen(m_pen);
+ painter->setBrush(Qt::NoBrush);
+ painter->drawEllipse(br);
+ break;
+ }
+ default:
+ break;
+ }
+
+ painter->restore();
+ }
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/highlightitem.h b/src/plugins/scxmleditor/plugin_interface/highlightitem.h
new file mode 100644
index 00000000000..6b6d7d2da9e
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/highlightitem.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "baseitem.h"
+
+#include <QBrush>
+#include <QGraphicsObject>
+#include <QPen>
+#include <QPointer>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class HighlightItem : public QGraphicsObject
+{
+public:
+ HighlightItem(BaseItem *parent = nullptr);
+
+ int type() const override
+ {
+ return HighlightType;
+ }
+
+ QRectF boundingRect() const override;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+ void advance(int phase) override;
+
+private:
+ QPointer<BaseItem> m_baseItem;
+ QRectF m_boundingRect;
+ QBrush m_brush;
+ QPen m_pen;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/historyitem.cpp b/src/plugins/scxmleditor/plugin_interface/historyitem.cpp
new file mode 100644
index 00000000000..a8014c4c777
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/historyitem.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "historyitem.h"
+#include <QPainter>
+
+using namespace ScxmlEditor::PluginInterface;
+
+HistoryItem::HistoryItem(const QPointF &pos, BaseItem *parent)
+ : ConnectableItem(pos, parent)
+{
+ setItemBoundingRect(QRectF(-20, -20, 40, 40));
+ setMinimumHeight(40);
+ setMinimumWidth(40);
+
+ m_pen.setColor(qRgb(0x12, 0x12, 0x12));
+ m_pen.setWidth(2);
+}
+
+void HistoryItem::updatePolygon()
+{
+ QRectF r = boundingRect();
+ m_size = qMin(r.width() * 0.45, r.height() * 0.45);
+ QPointF center = r.center();
+
+ m_polygon.clear();
+ m_polygon << (center + QPointF(-m_size, -m_size))
+ << (center + QPointF(m_size, -m_size))
+ << (center + QPointF(m_size, m_size))
+ << (center + QPointF(-m_size, m_size))
+ << (center + QPointF(-m_size, -m_size));
+}
+
+void HistoryItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ ConnectableItem::paint(painter, option, widget);
+
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setOpacity(getOpacity());
+
+ painter->setBrush(QColor(0xff, 0xff, 0xff));
+ m_pen.setColor(overlapping() ? qRgb(0xff, 0x00, 0x60) : qRgb(0x45, 0x45, 0x45));
+ painter->setPen(m_pen);
+ painter->drawEllipse(boundingRect().center(), m_size, m_size);
+
+ painter->drawText(boundingRect(), Qt::AlignCenter, QLatin1String(tagValue("type") == "deep" ? "H*" : "H"));
+ painter->restore();
+}
+
+bool HistoryItem::canStartTransition(ItemType type) const
+{
+ if (outputTransitionCount() > 0)
+ return false;
+
+ switch (type) {
+ case UnknownType:
+ case StateType:
+ case ParallelType:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/historyitem.h b/src/plugins/scxmleditor/plugin_interface/historyitem.h
new file mode 100644
index 00000000000..ab1ccf3dc4e
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/historyitem.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "connectableitem.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+/**
+ * @brief The HistoryItem class represents History-state of the SCXML-standard. It is an extended class from the ConnectableItem.
+ */
+class HistoryItem : public ConnectableItem
+{
+ Q_OBJECT
+
+public:
+ HistoryItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
+
+ int type() const override
+ {
+ return HistoryType;
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+
+protected:
+ void updatePolygon() override;
+ bool canStartTransition(ItemType type) const override;
+
+private:
+ qreal m_size;
+ QPen m_pen;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/idwarningitem.cpp b/src/plugins/scxmleditor/plugin_interface/idwarningitem.cpp
new file mode 100644
index 00000000000..87521964052
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/idwarningitem.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "idwarningitem.h"
+#include <QGraphicsScene>
+
+using namespace ScxmlEditor::PluginInterface;
+
+IdWarningItem::IdWarningItem(QGraphicsItem *parent)
+ : WarningItem(parent)
+{
+ setSeverity(OutputPane::Warning::ErrorType);
+ setTypeName(tr("State"));
+ setDescription(tr("Each State has to be unique ID."));
+ setReason(tr("Missing ID"));
+ setX(-boundingRect().width());
+}
+
+void IdWarningItem::check()
+{
+ setId(m_id);
+}
+
+void IdWarningItem::setId(const QString &text)
+{
+ QString oldId = m_id;
+ m_id = text;
+
+ // Check old id
+ if (!oldId.isEmpty())
+ checkDuplicates(oldId);
+
+ // Check new id
+ if (m_id.isEmpty()) {
+ setReason(tr("Missing ID"));
+ setWarningActive(true);
+ } else
+ checkDuplicates(m_id);
+}
+
+void IdWarningItem::checkDuplicates(const QString &id)
+{
+ if (scene()) {
+ QVector<IdWarningItem*> foundItems;
+
+ QList<QGraphicsItem*> items = scene()->items();
+ for (int i = 0; i < items.count(); ++i) {
+ if (items[i]->type() == IdWarningType) {
+ auto item = qgraphicsitem_cast<IdWarningItem*>(items[i]);
+ if (item && item->id() == id)
+ foundItems << item;
+ }
+ }
+
+ if (foundItems.count() == 1) {
+ foundItems[0]->setWarningActive(false);
+ } else {
+ for (int i = 0; i < foundItems.count(); ++i) {
+ foundItems[i]->setReason(tr("Duplicate ID (%1)").arg(id));
+ foundItems[i]->setWarningActive(true);
+ }
+ }
+ }
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/idwarningitem.h b/src/plugins/scxmleditor/plugin_interface/idwarningitem.h
new file mode 100644
index 00000000000..c32b746f7d1
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/idwarningitem.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "warningitem.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class IdWarningItem : public WarningItem
+{
+ Q_OBJECT
+
+public:
+ IdWarningItem(QGraphicsItem *parent = nullptr);
+
+ int type() const override
+ {
+ return IdWarningType;
+ }
+
+ QString id() const
+ {
+ return m_id;
+ }
+
+ void check() override;
+ void setId(const QString &text);
+
+private:
+ void checkDuplicates(const QString &id);
+
+ QString m_id;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/imageprovider.cpp b/src/plugins/scxmleditor/plugin_interface/imageprovider.cpp
new file mode 100644
index 00000000000..0dfb954f6e3
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/imageprovider.cpp
@@ -0,0 +1,33 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "imageprovider.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+ImageProvider::ImageProvider(QObject *parent)
+ : QObject(parent)
+{
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/imageprovider.h b/src/plugins/scxmleditor/plugin_interface/imageprovider.h
new file mode 100644
index 00000000000..66d07c788d5
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/imageprovider.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QObject>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ScxmlTag;
+
+class ImageProvider : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit ImageProvider(QObject *parent = nullptr);
+
+ virtual QImage *backgroundImage(const ScxmlTag *tag) const = 0;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/initialstateitem.cpp b/src/plugins/scxmleditor/plugin_interface/initialstateitem.cpp
new file mode 100644
index 00000000000..9059f789478
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/initialstateitem.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "initialstateitem.h"
+#include "initialwarningitem.h"
+#include "graphicsitemprovider.h"
+#include "scxmleditorconstants.h"
+#include "scxmluifactory.h"
+
+#include <QByteArray>
+#include <QDataStream>
+#include <QPainter>
+
+using namespace ScxmlEditor::PluginInterface;
+
+InitialStateItem::InitialStateItem(const QPointF &pos, BaseItem *parent)
+ : ConnectableItem(pos, parent)
+{
+ setItemBoundingRect(QRectF(-20, -20, 40, 40));
+ setMinimumHeight(40);
+ setMinimumWidth(40);
+ m_pen.setColor(qRgb(0x12, 0x12, 0x12));
+ m_pen.setWidth(2);
+
+ checkWarningItems();
+}
+
+void InitialStateItem::checkWarningItems()
+{
+ ScxmlUiFactory *uifactory = uiFactory();
+ if (uifactory) {
+ auto provider = static_cast<GraphicsItemProvider*>(uifactory->object("graphicsItemProvider"));
+ if (provider) {
+ if (!m_warningItem)
+ m_warningItem = static_cast<InitialWarningItem*>(provider->createWarningItem(Constants::C_STATE_WARNING_INITIAL, this));
+ }
+ }
+}
+
+QVariant InitialStateItem::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ QVariant retValue = ConnectableItem::itemChange(change, value);
+
+ switch (change) {
+ case QGraphicsItem::ItemSceneHasChanged:
+ checkWarningItems();
+ break;
+ default:
+ break;
+ }
+
+ return retValue;
+}
+
+void InitialStateItem::updatePolygon()
+{
+ QRectF r = boundingRect();
+ m_size = qMin(r.width() * 0.45, r.height() * 0.45);
+ QPointF center = r.center();
+
+ m_polygon.clear();
+ m_polygon << (center + QPointF(-m_size, -m_size))
+ << (center + QPointF(m_size, -m_size))
+ << (center + QPointF(m_size, m_size))
+ << (center + QPointF(-m_size, m_size))
+ << (center + QPointF(-m_size, -m_size));
+
+ if (m_warningItem)
+ m_warningItem->updatePos();
+}
+
+void InitialStateItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ ConnectableItem::paint(painter, option, widget);
+
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setOpacity(getOpacity());
+
+ m_pen.setColor(overlapping() ? qRgb(0xff, 0x00, 0x60) : qRgb(0x45, 0x45, 0x45));
+ painter->setPen(m_pen);
+ painter->setBrush(QColor(0x4d, 0x4d, 0x4d));
+ painter->drawEllipse(boundingRect().center(), m_size, m_size);
+
+ painter->restore();
+}
+
+InitialWarningItem *InitialStateItem::warningItem() const
+{
+ return m_warningItem;
+}
+
+bool InitialStateItem::canStartTransition(ItemType type) const
+{
+ if (transitionCount() > 0)
+ return false;
+
+ switch (type) {
+ case UnknownType:
+ case StateType:
+ case ParallelType:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void InitialStateItem::checkWarnings()
+{
+ if (m_warningItem)
+ m_warningItem->check();
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/initialstateitem.h b/src/plugins/scxmleditor/plugin_interface/initialstateitem.h
new file mode 100644
index 00000000000..a7cff82f8eb
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/initialstateitem.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "connectableitem.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class InitialWarningItem;
+
+/**
+ * @brief The InitialStateItem class represents Initial-state of the SCXML-standard. It is a extended class from the ConnectableItem.
+ */
+class InitialStateItem : public ConnectableItem
+{
+public:
+ explicit InitialStateItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
+
+ int type() const override
+ {
+ return InitialStateType;
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+ InitialWarningItem *warningItem() const;
+
+ void checkWarnings() override;
+
+protected:
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
+ void updatePolygon() override;
+ bool canStartTransition(ItemType type) const override;
+
+private:
+ void checkWarningItems();
+ InitialWarningItem *m_warningItem = nullptr;
+ qreal m_size = 1.0;
+ QPen m_pen;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/initialwarningitem.cpp b/src/plugins/scxmleditor/plugin_interface/initialwarningitem.cpp
new file mode 100644
index 00000000000..5b15f1d8bf8
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/initialwarningitem.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "initialwarningitem.h"
+#include "initialstateitem.h"
+#include "sceneutils.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+InitialWarningItem::InitialWarningItem(InitialStateItem *parent)
+ : WarningItem(parent)
+ , m_parentItem(parent)
+{
+ setSeverity(OutputPane::Warning::ErrorType);
+ setTypeName(tr("Initial"));
+ setDescription(tr("It is possible to have max 1 initial-state in the same level."));
+ setReason(tr("Too many initial states in the same level"));
+}
+
+void InitialWarningItem::updatePos()
+{
+ setPos(m_parentItem->boundingRect().topLeft());
+}
+
+void InitialWarningItem::check()
+{
+ if (m_parentItem)
+ setWarningActive(SceneUtils::hasSiblingStates(m_parentItem));
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/initialwarningitem.h b/src/plugins/scxmleditor/plugin_interface/initialwarningitem.h
new file mode 100644
index 00000000000..762bb477028
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/initialwarningitem.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "warningitem.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class InitialStateItem;
+
+class InitialWarningItem : public WarningItem
+{
+public:
+ InitialWarningItem(InitialStateItem *parent = nullptr);
+
+ int type() const override
+ {
+ return InitialWarningType;
+ }
+
+ void check() override;
+
+ void updatePos();
+
+private:
+ InitialStateItem *m_parentItem;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/isceditor.h b/src/plugins/scxmleditor/plugin_interface/isceditor.h
new file mode 100644
index 00000000000..0a18708d029
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/isceditor.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "mytypes.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ScxmlUiFactory;
+class ScxmlDocument;
+
+/**
+ * @brief The ISCEditor interface for the all SCEditor plugins
+ *
+ * Every SCEditor plugin must implement this interface. SCEditor application will load all plugins which have listed
+ * in the environment variable QT_SCEDITOR_PLUGINS and which implements this interface. The function init() will be called
+ * immediately after plugin has successfully loaded. See the @ScxmlUiFactory for more information how to register editor etc for the UI.
+ * The function refresh() will be called when it is time to refresh data. Plugin itself can decide if this is necessary or not.
+ * Normally this function will be called when application get focus.
+ */
+class ISCEditor
+{
+public:
+ /**
+ * @brief init - pure virtual function to init and forward the pointer of the ScxmlUiFactory to the plugin.
+ * @param factory - pointer to the ScxmlUiFactory
+ */
+ virtual void init(ScxmlUiFactory *factory) = 0;
+
+ /**
+ * @brief documentChanged - when document will changed this function will be called
+ * @param type - change reason
+ * @param doc - pointer to ScxmlDocument
+ */
+ virtual void documentChanged(DocumentChangeType type, ScxmlDocument *doc) = 0;
+
+ /**
+ * @brief refresh - tell the plugin that it is time to update all data if necessary
+ */
+ virtual void refresh() = 0;
+
+ /**
+ * @brief detach - here should be unregister all objects
+ */
+ virtual void detach() = 0;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_INTERFACE(ScxmlEditor::PluginInterface::ISCEditor, "StateChartEditor.ISCEditor/1.0")
+QT_END_NAMESPACE
diff --git a/src/plugins/scxmleditor/plugin_interface/layoutitem.cpp b/src/plugins/scxmleditor/plugin_interface/layoutitem.cpp
new file mode 100644
index 00000000000..1acd0a386f6
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/layoutitem.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "layoutitem.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+LayoutItem::LayoutItem(const QRectF &br, QGraphicsItem *parent)
+ : QGraphicsObject(parent)
+ , m_boundingRect(br)
+{
+ setZValue(-100);
+}
+
+QRectF LayoutItem::boundingRect() const
+{
+ return m_boundingRect;
+}
+
+void LayoutItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(painter)
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
+}
+
+void LayoutItem::setBoundingRect(const QRectF &r)
+{
+ prepareGeometryChange();
+ m_boundingRect = r;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/layoutitem.h b/src/plugins/scxmleditor/plugin_interface/layoutitem.h
new file mode 100644
index 00000000000..ffaddefd5ee
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/layoutitem.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "mytypes.h"
+#include <QGraphicsObject>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class LayoutItem : public QGraphicsObject
+{
+public:
+ LayoutItem(const QRectF &br, QGraphicsItem *parent = nullptr);
+
+ int type() const override
+ {
+ return LayoutType;
+ }
+
+ QRectF boundingRect() const override;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+
+ void setBoundingRect(const QRectF &r);
+
+private:
+ QRectF m_boundingRect;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/mytypes.h b/src/plugins/scxmleditor/plugin_interface/mytypes.h
new file mode 100644
index 00000000000..26b5d626adf
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/mytypes.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QGraphicsItem>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+/**
+ * @brief The type of the graphics-items
+ */
+enum ItemType {
+ UnknownType = QGraphicsItem::UserType + 1,
+
+ // Warning items ->
+ HighlightType,
+ LayoutType,
+ IdWarningType,
+ StateWarningType,
+ TransitionWarningType,
+ InitialWarningType,
+
+ // Helper items ->
+ CornerGrabberType,
+ QuickTransitionType,
+ TextType,
+ TagTextType,
+ TagWrapperType,
+ TransitionType,
+
+ // Connectable-items ->
+ InitialStateType,
+ FinalStateType,
+ HistoryType,
+ StateType,
+ ParallelType
+};
+
+enum ActionType {
+ ActionZoomIn = 0,
+ ActionZoomOut,
+ ActionFitToView,
+ ActionPan,
+ ActionMagnifier,
+ ActionNavigator,
+ ActionCopy,
+ ActionCut,
+ ActionPaste,
+ ActionScreenshot,
+ ActionExportToImage,
+ ActionFullNamespace,
+ ActionAlignLeft,
+ ActionAlignRight,
+ ActionAlignTop,
+ ActionAlignBottom,
+ ActionAlignHorizontal,
+ ActionAlignVertical,
+ ActionAdjustWidth,
+ ActionAdjustHeight,
+ ActionAdjustSize,
+ ActionStatistics,
+ ActionLast,
+
+ ActionColorTheme
+};
+
+enum ToolButtonType {
+ ToolButtonStateColor,
+ ToolButtonFontColor,
+ ToolButtonAlignment,
+ ToolButtonAdjustment,
+ ToolButtonLast,
+
+ ToolButtonColorTheme
+};
+
+enum AttributeRole {
+ DataTypeRole = Qt::UserRole + 1,
+ DataRole
+};
+
+enum DocumentChangeType {
+ BeginSave = 0,
+ AfterSave,
+ AfterLoad,
+ NewDocument
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/parallelitem.cpp b/src/plugins/scxmleditor/plugin_interface/parallelitem.cpp
new file mode 100644
index 00000000000..b98ea4f703c
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/parallelitem.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "parallelitem.h"
+#include "scxmldocument.h"
+#include "scxmltagutils.h"
+#include <QPainter>
+
+using namespace ScxmlEditor::PluginInterface;
+
+ParallelItem::ParallelItem(const QPointF &pos, BaseItem *parent)
+ : StateItem(pos, parent)
+{
+ m_pixmap = QPixmap(":/scxmleditor/images/parallel_icon.png");
+ updatePolygon();
+}
+
+void ParallelItem::updatePolygon()
+{
+ StateItem::updatePolygon();
+ int cap = m_titleRect.height() * 0.2;
+ m_pixmapRect = m_titleRect.adjusted(m_titleRect.width() - m_titleRect.height(), cap, -cap, -cap).toRect();
+}
+
+void ParallelItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ StateItem::paint(painter, option, widget);
+
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setOpacity(getOpacity());
+
+ painter->drawPixmap(m_pixmapRect, m_pixmap);
+ painter->restore();
+}
+
+void ParallelItem::doLayout(int d)
+{
+ if (depth() != d)
+ return;
+
+ // 1. Find children items
+ QVector<StateItem*> children;
+ foreach (QGraphicsItem *it, childItems()) {
+ if (it->type() >= StateType) {
+ auto itt = qgraphicsitem_cast<StateItem*>(it);
+ if (itt)
+ children << itt;
+ }
+ }
+
+ // 2. Adjust sizes
+ foreach (StateItem *itt, children) {
+ itt->shrink();
+ }
+
+ qreal maxw = 0;
+ foreach (StateItem *itt, children) {
+ QRectF rr = itt->boundingRect();
+ maxw = qMax(rr.width(), maxw);
+ }
+
+ foreach (StateItem *itt, children) {
+ QRectF rr = itt->boundingRect();
+ if (!qFuzzyCompare(rr.width(), maxw))
+ rr.setWidth(maxw);
+ itt->setItemBoundingRect(rr);
+ }
+
+ // 3. Relocate children-states
+ // a) sort list
+ QVector<StateItem*> sortedList;
+ while (children.count() > 0) {
+ qreal minTop = children.first()->boundingRect().top();
+ int minTopIndex = 0;
+ for (int i = 1; i < children.count(); ++i) {
+ qreal top = children[i]->boundingRect().top();
+ if (top < minTop) {
+ minTop = top;
+ minTopIndex = i;
+ }
+ }
+ sortedList << children.takeAt(minTopIndex);
+ }
+
+ // b) relocate items
+ for (int i = 1; i < sortedList.count(); ++i) {
+ QRectF br1 = sortedList[i - 1]->sceneBoundingRect();
+ QRectF br2 = sortedList[i]->sceneBoundingRect();
+ sortedList[i]->moveStateBy(br1.left() - br2.left(), br1.bottom() + 10 - br2.top());
+ }
+
+ // 4. Shrink parallel-state
+ shrink();
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/parallelitem.h b/src/plugins/scxmleditor/plugin_interface/parallelitem.h
new file mode 100644
index 00000000000..8b0ae52ec63
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/parallelitem.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "stateitem.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+/**
+ * @brief The ParalllelItem class represents Parallel-state of the SCXML-standard. It is a extended class from the StateItem.
+ */
+class ParallelItem : public StateItem
+{
+public:
+ explicit ParallelItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
+
+ int type() const override
+ {
+ return ParallelType;
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+ void doLayout(int d) override;
+
+protected:
+ void updatePolygon() override;
+
+private:
+ QPixmap m_pixmap;
+ QRect m_pixmapRect;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/plugin_interface.pri b/src/plugins/scxmleditor/plugin_interface/plugin_interface.pri
new file mode 100644
index 00000000000..26b8aa19f73
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/plugin_interface.pri
@@ -0,0 +1,93 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/actionhandler.h \
+ $$PWD/actionprovider.h \
+ $$PWD/attributeitemdelegate.h \
+ $$PWD/attributeitemmodel.h \
+ $$PWD/baseitem.h \
+ $$PWD/connectableitem.h \
+ $$PWD/cornergrabberitem.h \
+ $$PWD/finalstateitem.h \
+ $$PWD/genericscxmlplugin.h \
+ $$PWD/graphicsitemprovider.h \
+ $$PWD/graphicsscene.h \
+ $$PWD/highlightitem.h \
+ $$PWD/historyitem.h \
+ $$PWD/idwarningitem.h \
+ $$PWD/imageprovider.h \
+ $$PWD/initialstateitem.h \
+ $$PWD/initialwarningitem.h \
+ $$PWD/isceditor.h \
+ $$PWD/layoutitem.h \
+ $$PWD/mytypes.h \
+ $$PWD/parallelitem.h \
+ $$PWD/quicktransitionitem.h \
+ $$PWD/scattributeitemdelegate.h \
+ $$PWD/scattributeitemmodel.h \
+ $$PWD/sceneutils.h \
+ $$PWD/scgraphicsitemprovider.h \
+ $$PWD/scshapeprovider.h \
+ $$PWD/scutilsprovider.h \
+ $$PWD/scxmldocument.h \
+ $$PWD/scxmlnamespace.h \
+ $$PWD/scxmltag.h \
+ $$PWD/scxmltagutils.h \
+ $$PWD/scxmltypes.h \
+ $$PWD/scxmluifactory.h \
+ $$PWD/serializer.h \
+ $$PWD/shapeprovider.h \
+ $$PWD/snapline.h \
+ $$PWD/stateitem.h \
+ $$PWD/statewarningitem.h \
+ $$PWD/tagtextitem.h \
+ $$PWD/textitem.h \
+ $$PWD/transitionitem.h \
+ $$PWD/transitionwarningitem.h \
+ $$PWD/undocommands.h \
+ $$PWD/utilsprovider.h \
+ $$PWD/warningitem.h \
+ $$PWD/warningprovider.h
+
+SOURCES += \
+ $$PWD/actionhandler.cpp \
+ $$PWD/attributeitemdelegate.cpp \
+ $$PWD/attributeitemmodel.cpp \
+ $$PWD/baseitem.cpp \
+ $$PWD/connectableitem.cpp \
+ $$PWD/cornergrabberitem.cpp \
+ $$PWD/finalstateitem.cpp \
+ $$PWD/genericscxmlplugin.cpp \
+ $$PWD/graphicsscene.cpp \
+ $$PWD/highlightitem.cpp \
+ $$PWD/historyitem.cpp \
+ $$PWD/idwarningitem.cpp \
+ $$PWD/imageprovider.cpp \
+ $$PWD/initialstateitem.cpp \
+ $$PWD/initialwarningitem.cpp \
+ $$PWD/layoutitem.cpp \
+ $$PWD/parallelitem.cpp \
+ $$PWD/quicktransitionitem.cpp \
+ $$PWD/scattributeitemdelegate.cpp \
+ $$PWD/scattributeitemmodel.cpp \
+ $$PWD/sceneutils.cpp \
+ $$PWD/scgraphicsitemprovider.cpp \
+ $$PWD/scshapeprovider.cpp \
+ $$PWD/scutilsprovider.cpp \
+ $$PWD/scxmldocument.cpp \
+ $$PWD/scxmlnamespace.cpp \
+ $$PWD/scxmltag.cpp \
+ $$PWD/scxmltagutils.cpp \
+ $$PWD/scxmluifactory.cpp \
+ $$PWD/serializer.cpp \
+ $$PWD/shapeprovider.cpp \
+ $$PWD/snapline.cpp \
+ $$PWD/stateitem.cpp \
+ $$PWD/statewarningitem.cpp \
+ $$PWD/tagtextitem.cpp \
+ $$PWD/textitem.cpp \
+ $$PWD/transitionitem.cpp \
+ $$PWD/transitionwarningitem.cpp \
+ $$PWD/undocommands.cpp \
+ $$PWD/utilsprovider.cpp \
+ $$PWD/warningitem.cpp
diff --git a/src/plugins/scxmleditor/plugin_interface/quicktransitionitem.cpp b/src/plugins/scxmleditor/plugin_interface/quicktransitionitem.cpp
new file mode 100644
index 00000000000..b2588112314
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/quicktransitionitem.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "quicktransitionitem.h"
+#include <QGraphicsSceneMouseEvent>
+#include <QPainter>
+
+using namespace ScxmlEditor::PluginInterface;
+
+const qreal CAPX = 4;
+const qreal CAPY = 3;
+
+QuickTransitionItem::QuickTransitionItem(int index, ItemType connectionType, QGraphicsItem *parent)
+ : QGraphicsObject(parent)
+ , m_connectionType(connectionType)
+{
+ setParentItem(parent);
+ installSceneEventFilter(parent);
+ setZValue(501);
+
+ m_rect = QRectF(index * 25, -30, 20, 20);
+ m_drawingRect = m_rect.adjusted(4, 4, -4, -4);
+ m_stateRect = m_rect.adjusted(3, 4, -3, -4);
+ m_brush.setStyle(Qt::SolidPattern);
+ m_brush.setColor(QColor(0xe8, 0xe8, 0xe8));
+
+ m_pen.setColor(QColor(0x12, 0x12, 0x12));
+ m_pen.setCapStyle(Qt::RoundCap);
+ setAcceptHoverEvents(true);
+}
+
+void QuickTransitionItem::setConnectionType(ItemType connectionType)
+{
+ m_connectionType = connectionType;
+}
+
+void QuickTransitionItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
+{
+ Q_UNUSED(e)
+ m_brush.setColor(QColor(0xe8, 0xe8, 0xe8));
+ update();
+}
+
+void QuickTransitionItem::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
+{
+ Q_UNUSED(e)
+ m_brush.setColor(QColor(0xff, 0xc4, 0xff));
+ update();
+}
+
+void QuickTransitionItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
+
+ painter->save();
+
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setPen(m_pen);
+ painter->setBrush(m_brush);
+ painter->drawRect(m_rect);
+
+ if (m_connectionType == UnknownType) {
+ QPointF endPoint = m_drawingRect.topRight();
+ painter->drawLine(m_drawingRect.bottomLeft(), endPoint);
+ painter->drawLine(endPoint, endPoint + QPointF(-5, 0));
+ painter->drawLine(endPoint, endPoint + QPointF(0, 5));
+ } else if (m_connectionType >= FinalStateType) {
+ switch (m_connectionType) {
+ case StateType:
+ painter->setPen(m_pen);
+ painter->setBrush(Qt::white);
+ painter->drawRoundedRect(m_stateRect, 2, 2);
+ break;
+
+ case ParallelType:
+ painter->setPen(m_pen);
+ painter->setBrush(Qt::white);
+ painter->drawRoundedRect(m_stateRect, 2, 2);
+
+ painter->setPen(m_pen);
+ painter->drawLine(QPointF(m_stateRect.left() + CAPX, m_stateRect.center().y()), QPointF(m_stateRect.right() - CAPX, m_stateRect.center().y()));
+ painter->drawLine(QPointF(m_stateRect.center().x(), m_stateRect.top() + CAPY), QPointF(m_stateRect.center().x(), m_stateRect.bottom() - CAPY));
+ painter->drawLine(QPointF(m_stateRect.right() - CAPX, m_stateRect.top() + CAPY), QPointF(m_stateRect.center().x(), m_stateRect.top() + CAPY));
+ painter->drawLine(QPointF(m_stateRect.right() - CAPX, m_stateRect.bottom() - CAPY), QPointF(m_stateRect.center().x(), m_stateRect.bottom() - CAPY));
+ break;
+
+ case FinalStateType:
+ painter->setPen(m_pen);
+ painter->setBrush(Qt::white);
+ painter->drawEllipse(m_stateRect.center(), 7, 7);
+
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(Qt::black);
+ painter->drawEllipse(m_stateRect.center(), 5, 5);
+ break;
+
+ case HistoryType:
+ painter->setFont(QFont("Arial", 6));
+ painter->setPen(m_pen);
+ painter->setBrush(Qt::white);
+ painter->drawEllipse(m_stateRect.center(), 7, 7);
+ painter->drawText(m_stateRect, Qt::AlignCenter, tr("H"));
+ break;
+ default:
+ break;
+ }
+ }
+
+ painter->restore();
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/quicktransitionitem.h b/src/plugins/scxmleditor/plugin_interface/quicktransitionitem.h
new file mode 100644
index 00000000000..574b3962f77
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/quicktransitionitem.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "baseitem.h"
+
+#include <QBrush>
+#include <QPen>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class QuickTransitionItem : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+ explicit QuickTransitionItem(int index = 0, ItemType connectionType = UnknownType, QGraphicsItem *parent = nullptr);
+
+ /**
+ * @brief type of the item
+ */
+ int type() const override
+ {
+ return QuickTransitionType;
+ }
+
+ ItemType connectionType() const
+ {
+ return m_connectionType;
+ }
+
+ void setConnectionType(ItemType type);
+
+ QRectF boundingRect() const override
+ {
+ return m_rect;
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+
+private:
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *e) override;
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *e) override;
+
+ ItemType m_connectionType;
+ QRectF m_rect;
+ QBrush m_brush;
+ QRectF m_drawingRect;
+ QRectF m_stateRect;
+ QPen m_pen;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.cpp b/src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.cpp
new file mode 100644
index 00000000000..78735dcf5ac
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scattributeitemdelegate.h"
+#include "mytypes.h"
+#include <QComboBox>
+#include <QLineEdit>
+#include <QRegExp>
+#include <QRegExpValidator>
+
+using namespace ScxmlEditor::PluginInterface;
+
+SCAttributeItemDelegate::SCAttributeItemDelegate(QObject *parent)
+ : AttributeItemDelegate(parent)
+{
+}
+
+QWidget *SCAttributeItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ Q_UNUSED(option)
+
+ switch (index.data(DataTypeRole).toInt()) {
+ case QVariant::StringList: {
+ auto combo = new QComboBox(parent);
+ combo->setFocusPolicy(Qt::StrongFocus);
+ return combo;
+ }
+ case QVariant::String: {
+ if (index.column() == 0) {
+ auto edit = new QLineEdit(parent);
+ edit->setFocusPolicy(Qt::StrongFocus);
+ QRegExp rx("^(?!xml)[_a-z][a-z0-9-._]*$");
+ rx.setCaseSensitivity(Qt::CaseInsensitive);
+ edit->setValidator(new QRegExpValidator(rx, parent));
+ return edit;
+ }
+ }
+ default:
+ break;
+ }
+
+ return QStyledItemDelegate::createEditor(parent, option, index);
+}
+
+void SCAttributeItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QStyledItemDelegate::updateEditorGeometry(editor, option, index);
+ if (editor)
+ editor->setGeometry(option.rect);
+}
+
+void SCAttributeItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
+{
+ switch (index.data(DataTypeRole).toInt()) {
+ case QVariant::StringList: {
+ auto combo = qobject_cast<QComboBox*>(editor);
+ if (combo) {
+ combo->clear();
+ QStringList values = index.data(DataRole).toString().split(";");
+
+ foreach (QString val, values)
+ combo->addItem(val);
+
+ combo->setCurrentText(index.data().toString());
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ QStyledItemDelegate::setEditorData(editor, index);
+}
+
+void SCAttributeItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
+{
+ auto combo = qobject_cast<QComboBox*>(editor);
+ if (combo) {
+ model->setData(index, combo->currentText());
+ return;
+ }
+
+ QStyledItemDelegate::setModelData(editor, model, index);
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.h b/src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.h
new file mode 100644
index 00000000000..2ae9f6bff5a
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scattributeitemdelegate.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "attributeitemdelegate.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class SCAttributeItemDelegate : public AttributeItemDelegate
+{
+public:
+ SCAttributeItemDelegate(QObject *parent = nullptr);
+
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+ void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+ void setEditorData(QWidget *editor, const QModelIndex &index) const override;
+ void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.cpp b/src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.cpp
new file mode 100644
index 00000000000..0cebcc36014
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scattributeitemmodel.h"
+#include "mytypes.h"
+
+#include <QBrush>
+
+using namespace ScxmlEditor::PluginInterface;
+
+SCAttributeItemModel::SCAttributeItemModel(QObject *parent)
+ : AttributeItemModel(parent)
+{
+}
+
+QVariant SCAttributeItemModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ return section == 0 ? tr("Name") : tr("Value");
+
+ return QVariant();
+}
+
+bool SCAttributeItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (role != Qt::EditRole || m_tag == nullptr)
+ return false;
+
+ bool bEditable = m_tag->tagType() <= MetadataItem;
+
+ if (index.row() >= 0 && m_document != 0) {
+ if (!bEditable) {
+ if (index.row() < m_tag->info()->n_attributes)
+ m_document->setValue(m_tag, index.row(), value.toString());
+ } else {
+ if (index.column() == 0) {
+ m_tag->setAttributeName(index.row(), value.toString());
+ m_document->setValue(m_tag, value.toString(), m_tag->attribute(value.toString()));
+ } else
+ m_document->setValue(m_tag, m_tag->attributeName(index.row()), value.toString());
+ }
+ emit dataChanged(index, index);
+ emit layoutChanged();
+ return true;
+ }
+
+ return false;
+}
+
+QVariant SCAttributeItemModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || m_tag == nullptr)
+ return QVariant();
+
+ if (index.row() < 0)
+ return QVariant();
+
+ bool bEditable = m_tag->tagType() <= MetadataItem;
+
+ if (!bEditable && index.row() >= m_tag->info()->n_attributes)
+ return QVariant();
+
+ if (bEditable && index.row() > m_tag->attributeCount())
+ return QVariant();
+
+ bool bExtraRow = bEditable && m_tag->attributeCount() == index.row();
+
+ switch (role) {
+ case Qt::DisplayRole:
+ if (bExtraRow)
+ return index.column() == 0 ? tr("- name -") : tr(" - value -");
+ case Qt::EditRole: {
+ if (index.column() == 0) {
+ if (bEditable) {
+ return m_tag->attributeName(index.row());
+ } else {
+ if (m_tag->info()->attributes[index.row()].required)
+ return QString::fromLatin1("*%1").arg(QLatin1String(m_tag->info()->attributes[index.row()].name));
+ else
+ return m_tag->info()->attributes[index.row()].name;
+ }
+ } else {
+ if (bEditable) {
+ if (m_tag->tagType() > MetadataItem && m_tag->info()->attributes[index.row()].datatype == QVariant::StringList)
+ return QString::fromLatin1(m_tag->info()->attributes[index.row()].value).split(";");
+ else
+ return m_tag->attribute(index.row());
+ } else {
+ return m_tag->attribute(QLatin1String(m_tag->info()->attributes[index.row()].name));
+ }
+ }
+ }
+ case Qt::TextAlignmentRole:
+ if (bExtraRow)
+ return Qt::AlignHCenter;
+ else
+ break;
+ case Qt::ForegroundRole:
+ return bExtraRow ? QBrush(Qt::gray) : QBrush(Qt::black);
+ case DataTypeRole: {
+ if (m_tag->tagType() == Metadata || m_tag->tagType() == MetadataItem)
+ return (int)QVariant::String;
+ else if (index.column() == 1 && m_tag->info()->n_attributes > 0)
+ return m_tag->info()->attributes[index.row()].datatype;
+ else
+ return QVariant::Invalid;
+ }
+ case DataRole: {
+ if (m_tag->info()->n_attributes > 0)
+ return m_tag->info()->attributes[index.row()].value;
+ else
+ return QVariant();
+ }
+ default:
+ break;
+ }
+
+ return QVariant();
+}
+
+Qt::ItemFlags SCAttributeItemModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid() || m_tag == nullptr)
+ return Qt::NoItemFlags;
+
+ if (m_tag->tagType() <= MetadataItem || (index.column() == 1 && m_tag->info()->n_attributes > 0 && m_tag->info()->attributes[index.row()].editable))
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
+
+ return Qt::NoItemFlags;
+}
+
+int SCAttributeItemModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return 2;
+}
+
+int SCAttributeItemModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ if (m_tag) {
+ if (m_tag->tagType() <= MetadataItem)
+ return m_tag->attributeCount() + 1;
+ else
+ return m_tag->info()->n_attributes;
+ }
+
+ return 0;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.h b/src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.h
new file mode 100644
index 00000000000..8fbfdf6637d
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scattributeitemmodel.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "attributeitemmodel.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class SCAttributeItemModel : public AttributeItemModel
+{
+public:
+ SCAttributeItemModel(QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/sceneutils.cpp b/src/plugins/scxmleditor/plugin_interface/sceneutils.cpp
new file mode 100644
index 00000000000..187f471dacf
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/sceneutils.cpp
@@ -0,0 +1,419 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "sceneutils.h"
+#include "connectableitem.h"
+#include "finalstateitem.h"
+#include "graphicsscene.h"
+#include "historyitem.h"
+#include "initialstateitem.h"
+#include "parallelitem.h"
+#include "scxmldocument.h"
+#include "scxmleditorconstants.h"
+#include "scxmltag.h"
+#include "scxmltagutils.h"
+#include "stateitem.h"
+#include "transitionitem.h"
+
+#include <QGuiApplication>
+#include <QGraphicsScene>
+#include <QtMath>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+namespace SceneUtils {
+
+ConnectableItem *createItem(ItemType type, const QPointF &pos)
+{
+ switch (type) {
+ case InitialStateType:
+ return new InitialStateItem(pos);
+ case FinalStateType:
+ return new FinalStateItem(pos);
+ case StateType:
+ return new StateItem(pos);
+ case HistoryType:
+ return new HistoryItem(pos);
+ case ParallelType:
+ return new ParallelItem(pos);
+ default:
+ break;
+ }
+
+ return nullptr;
+}
+
+ConnectableItem *createItemByTagType(TagType type, const QPointF &pos)
+{
+ switch (type) {
+ case Initial:
+ return createItem(InitialStateType, pos);
+ case Final:
+ return createItem(FinalStateType, pos);
+ case State:
+ return createItem(StateType, pos);
+ case History:
+ return createItem(HistoryType, pos);
+ case Parallel:
+ return createItem(ParallelType, pos);
+ default:
+ return nullptr;
+ }
+}
+
+ScxmlTag *createTag(ItemType type, ScxmlDocument *document)
+{
+ TagType t = UnknownTag;
+ switch (type) {
+ case InitialStateType:
+ t = Initial;
+ break;
+ case FinalStateType:
+ t = Final;
+ break;
+ case HistoryType:
+ t = History;
+ break;
+ case StateType:
+ t = State;
+ break;
+ case ParallelType:
+ t = Parallel;
+ break;
+ default:
+ break;
+ }
+
+ if (t != UnknownTag)
+ return new ScxmlTag(t, document);
+
+ return nullptr;
+}
+
+bool canDrop(int parentType, int childType)
+{
+ switch (parentType) {
+ case StateType: {
+ switch (childType) {
+ case InitialStateType:
+ case FinalStateType:
+ case StateType:
+ case ParallelType:
+ case HistoryType:
+ return true;
+ default:
+ return false;
+ }
+ }
+ case ParallelType: {
+ switch (childType) {
+ case StateType:
+ case ParallelType:
+ case HistoryType:
+ return true;
+ default:
+ return false;
+ }
+ }
+ default:
+ return false;
+ }
+}
+
+QVector<ScxmlTag*> findCopyTags(const QVector<BaseItem*> &items, QPointF &minPos)
+{
+ QPointF pp(0, 0);
+ QVector<ScxmlTag*> tags;
+ foreach (BaseItem *it, items) {
+ if (it->type() >= InitialStateType && it->isSelected()) {
+ BaseItem *parent = it->parentBaseItem();
+ BaseItem *lastSelectedParent = it;
+ while (parent) {
+ if (parent->isSelected())
+ lastSelectedParent = parent;
+ parent = parent->parentBaseItem();
+ }
+
+ if (!tags.contains(lastSelectedParent->tag())) {
+ QPointF p = lastSelectedParent->sceneBoundingRect().topLeft();
+ if (tags.isEmpty()) {
+ pp = p;
+ } else {
+ pp.setX(qMin(pp.x(), p.x()));
+ pp.setY(qMin(pp.y(), p.y()));
+ }
+ lastSelectedParent->updateUIProperties();
+ tags << lastSelectedParent->tag();
+ }
+ }
+ }
+
+ minPos = pp;
+ return tags;
+}
+
+QVector<ScxmlTag*> findRemovedTags(const QVector<BaseItem*> &items)
+{
+ // Find right tags
+ QVector<ScxmlTag*> tags;
+ foreach (BaseItem *it, items) {
+ if (it->isSelected()) {
+ // Find the last selected parent
+ BaseItem *parent = it->parentBaseItem();
+ BaseItem *lastSelectedParent = it;
+ while (parent != 0) {
+ if (parent->isSelected())
+ lastSelectedParent = parent;
+ parent = parent->parentBaseItem();
+ }
+
+ // Add tag to the list
+ if (!tags.contains(lastSelectedParent->tag()))
+ tags << lastSelectedParent->tag();
+ }
+ }
+
+ return tags;
+}
+
+void layout(const QList<QGraphicsItem*> &items)
+{
+ // Collect child items
+ QList<ConnectableItem*> childItems;
+ ConnectableItem *initialItem = nullptr;
+ ConnectableItem *finalItem = nullptr;
+ foreach (QGraphicsItem *item, items) {
+ auto connectableItem = qgraphicsitem_cast<ConnectableItem*>(item);
+ if (connectableItem) {
+ if (connectableItem->type() == InitialStateType)
+ initialItem = connectableItem;
+ else if (connectableItem->type() == FinalStateType)
+ finalItem = connectableItem;
+ else if (connectableItem->type() >= HistoryType)
+ childItems << connectableItem;
+ }
+ }
+
+ // Change initial-item position
+ ConnectableItem *firstItem = nullptr;
+ if (initialItem && initialItem->outputTransitionCount() == 1) {
+ firstItem = initialItem->outputTransitions()[0]->connectedItem(initialItem);
+ int index = childItems.indexOf(firstItem);
+ if (index > 0)
+ childItems.swap(index, 0);
+ }
+
+ // Search final-item
+ ConnectableItem *lastItem = nullptr;
+ if (finalItem && finalItem->inputTransitionCount() > 0)
+ lastItem = finalItem->inputTransitions()[0]->connectedItem(finalItem);
+
+ int startAngle = qrand() % 2 == 0 ? 180 : 90;
+ int startDistance = 40 + childItems.count() * 10;
+ if (childItems.count() > 0) {
+ // Init position of the items
+ int angleDiff = 360 / (childItems.count() + 1);
+ for (int i = 0; i < childItems.count(); ++i) {
+ int angle = startAngle + i * angleDiff;
+ QLineF line = QLineF::fromPolar(startDistance, angle);
+ childItems[i]->setPos(line.p2());
+ }
+
+ // Then grow the distances so much that there is no any overlapped items
+ for (int i = 0; i < childItems.count(); ++i) {
+ int angle = startAngle + i * angleDiff;
+ QLineF line = QLineF::fromPolar(startDistance, angle);
+ ConnectableItem *movingItem = childItems[i];
+ QRectF r2 = movingItem->boundingRect();
+ r2.moveTopLeft(r2.topLeft() + movingItem->pos());
+
+ bool intersects = true;
+
+ while (intersects) {
+ intersects = false;
+ for (int j = 0; j < childItems.count(); ++j) {
+ if (j != i) {
+ QRectF r1 = childItems[j]->boundingRect();
+ r1.moveTopLeft(r1.topLeft() + childItems[j]->pos());
+
+ if (r2.intersects(r1)) {
+ intersects = true;
+ break;
+ }
+ }
+ }
+
+ if (intersects) {
+ line.setLength(line.length() + 50);
+ movingItem->setPos(line.p2());
+ r2 = movingItem->boundingRect();
+ r2.moveTopLeft(r2.topLeft() + movingItem->pos());
+ }
+ }
+ }
+
+ // Then decrease the distances so much as possible
+ for (int i = 0; i < childItems.count(); ++i) {
+ ConnectableItem *movingItem = childItems[i];
+ QPointF p = movingItem->pos();
+ QLineF line(QPointF(0, 0), p);
+ QRectF r2 = movingItem->boundingRect();
+ r2.moveTopLeft(r2.topLeft() + p);
+
+ bool cont = true;
+ while (cont) {
+ bool intersects = false;
+ for (int j = 0; j < childItems.count(); ++j) {
+ if (j != i) {
+ QRectF r1 = childItems[j]->boundingRect();
+ r1.moveTopLeft(r1.topLeft() + childItems[j]->pos());
+
+ if (r2.intersects(r1)) {
+ intersects = true;
+ cont = false;
+ line.setLength(line.length() + 20);
+ movingItem->setPos(line.p2());
+ r2 = movingItem->boundingRect();
+ r2.moveTopLeft(r2.topLeft() + movingItem->pos());
+ break;
+ }
+ }
+ }
+
+ if (!intersects) {
+ line.setLength(line.length() - 20);
+ movingItem->setPos(line.p2());
+ r2 = movingItem->boundingRect();
+ r2.moveTopLeft(r2.topLeft() + movingItem->pos());
+ if (line.length() < 100)
+ cont = false;
+ }
+ }
+ }
+
+ // Finally set initial and final positions
+ foreach (ConnectableItem *item, childItems) {
+ if (item == firstItem)
+ initialItem->setPos(firstItem->pos() + firstItem->boundingRect().topLeft() - QPointF(50, 50));
+ else if (item == lastItem) {
+ int angle = startAngle + childItems.indexOf(item) * angleDiff;
+ QLineF line = QLineF::fromPolar(qMax(lastItem->boundingRect().width() / 2, lastItem->boundingRect().height() / 2) + 20, angle);
+ finalItem->setPos(lastItem->pos() + lastItem->boundingRect().center() + line.p2());
+ }
+ }
+ }
+}
+
+bool isChild(const QGraphicsItem *parent, const QGraphicsItem *child)
+{
+ while (child != 0) {
+ if (parent == child)
+ return true;
+ child = child->parentItem();
+ }
+
+ return false;
+}
+
+bool isSomeSelected(QGraphicsItem *item)
+{
+ while (item != 0) {
+ if (item->isSelected())
+ return true;
+ item = item->parentItem();
+ }
+
+ return false;
+}
+
+void moveTop(BaseItem *item, GraphicsScene *scene)
+{
+ if (item && scene) {
+ // Make the current item to the topmost of all
+ QGraphicsItem *parentItem = item->parentItem();
+ QList<QGraphicsItem*> children;
+ if (parentItem)
+ children = parentItem->childItems();
+ else
+ children = scene->sceneItems(Qt::DescendingOrder);
+
+ // Remove unnecessary items
+ for (int i = children.count(); i--;) {
+ if (children[i]->type() < InitialStateType)
+ children.takeAt(i);
+ }
+
+ // Change stack order
+ const int ind = parentItem ? children.indexOf(item) : 0;
+ for (int i = ind; i < children.count(); ++i)
+ children[i]->stackBefore(item);
+ }
+}
+
+ScxmlTag *addNewTag(ScxmlTag *parent, TagType type, GraphicsScene *scene)
+{
+ if (parent) {
+ ScxmlDocument *document = parent->document();
+ auto newTag = new ScxmlTag(type, document);
+ document->addTag(parent, newTag);
+
+ if (scene)
+ scene->unselectAll();
+
+ document->setCurrentTag(newTag);
+ return newTag;
+ }
+
+ return nullptr;
+}
+
+ScxmlTag *addChild(ScxmlTag *tag, const QVariantMap &data, GraphicsScene *scene)
+{
+ TagType newTagType = (TagType)data.value(Constants::C_SCXMLTAG_TAGTYPE, 0).toInt();
+ TagType subMenuTagType = (TagType)data.value(Constants::C_SCXMLTAG_PARENTTAG, 0).toInt();
+ if (newTagType >= UnknownTag) {
+ // Check if we must create or add submenu
+ if (subMenuTagType > UnknownTag && subMenuTagType != tag->tagType()) {
+ // Check if submenu-tag is already available
+ ScxmlTag *subMenuTag = TagUtils::findChild(tag, subMenuTagType);
+ if (subMenuTag)
+ return addNewTag(subMenuTag, newTagType, scene);
+ else {
+ // If dont, create new submenutag and add new child
+ subMenuTag = addNewTag(tag, subMenuTagType, scene);
+ return addNewTag(subMenuTag, newTagType, scene);
+ }
+ } else
+ return addNewTag(tag, newTagType, scene);
+ }
+
+ return nullptr;
+}
+
+} // namespace SceneUtils
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/sceneutils.h b/src/plugins/scxmleditor/plugin_interface/sceneutils.h
new file mode 100644
index 00000000000..baa4394ecf5
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/sceneutils.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "baseitem.h"
+
+#include <QGraphicsScene>
+
+QT_FORWARD_DECLARE_CLASS(QGraphicsItem)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class GraphicsScene;
+class ConnectableItem;
+class InitialStateItem;
+class BaseItem;
+class ScxmlTag;
+
+/**
+ * Namespace SceneUtils includes some usable function to manipulate the data of the items.
+ */
+namespace SceneUtils {
+
+ScxmlTag *addChild(ScxmlTag *tag, const QVariantMap &data, GraphicsScene *scene);
+ScxmlTag *addNewTag(ScxmlTag *parent, TagType type, GraphicsScene *scene);
+ConnectableItem *createItem(ItemType type, const QPointF &pos = QPointF());
+ConnectableItem *createItemByTagType(TagType type, const QPointF &pos = QPointF());
+ScxmlTag *createTag(ItemType type, ScxmlDocument *document);
+bool canDrop(int parentType, int childType);
+QVector<ScxmlTag*> findCopyTags(const QVector<BaseItem*> &items, QPointF &minPos);
+QVector<ScxmlTag*> findRemovedTags(const QVector<BaseItem*> &items);
+void layout(const QList<QGraphicsItem*> &items);
+bool isSomeSelected(QGraphicsItem *item);
+void moveTop(BaseItem *item, GraphicsScene *scene);
+
+bool isChild(const QGraphicsItem *parent, const QGraphicsItem *child);
+
+template <class T>
+bool hasSiblingStates(T *item)
+{
+ if (item) {
+ QList<QGraphicsItem*> children;
+ QGraphicsItem *parentItem = item->parentItem();
+ if (parentItem) {
+ children = parentItem->childItems();
+ } else if (item->scene()) {
+ foreach (QGraphicsItem *it, item->scene()->items()) {
+ if (!it->parentItem())
+ children << it;
+ }
+ }
+
+ foreach (QGraphicsItem *it, children) {
+ if (it != item && it->type() == item->type()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+} // namespace SceneUtils
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scgraphicsitemprovider.cpp b/src/plugins/scxmleditor/plugin_interface/scgraphicsitemprovider.cpp
new file mode 100644
index 00000000000..2b0fd8ac602
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scgraphicsitemprovider.cpp
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scgraphicsitemprovider.h"
+#include "idwarningitem.h"
+#include "initialstateitem.h"
+#include "initialwarningitem.h"
+#include "scxmleditorconstants.h"
+#include "stateitem.h"
+#include "transitionitem.h"
+#include "transitionwarningitem.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+SCGraphicsItemProvider::SCGraphicsItemProvider(QObject *parent)
+ : GraphicsItemProvider(parent)
+{
+}
+
+WarningItem *SCGraphicsItemProvider::createWarningItem(const QString &key, BaseItem *parentItem) const
+{
+ if (key == Constants::C_STATE_WARNING_ID && parentItem)
+ return new IdWarningItem(parentItem);
+
+ if (key == Constants::C_STATE_WARNING_TRANSITION && parentItem && parentItem->type() == TransitionType)
+ return new TransitionWarningItem(static_cast<TransitionItem*>(parentItem));
+
+ if (key == Constants::C_STATE_WARNING_INITIAL && parentItem && parentItem->type() == InitialStateType)
+ return new InitialWarningItem(static_cast<InitialStateItem*>(parentItem));
+
+ return nullptr;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/scgraphicsitemprovider.h b/src/plugins/scxmleditor/plugin_interface/scgraphicsitemprovider.h
new file mode 100644
index 00000000000..316739f18f0
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scgraphicsitemprovider.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "graphicsitemprovider.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class SCGraphicsItemProvider : public GraphicsItemProvider
+{
+public:
+ SCGraphicsItemProvider(QObject *parent = nullptr);
+
+ WarningItem *createWarningItem(const QString &key, BaseItem *parentItem) const override;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scshapeprovider.cpp b/src/plugins/scxmleditor/plugin_interface/scshapeprovider.cpp
new file mode 100644
index 00000000000..90877b392d3
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scshapeprovider.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scshapeprovider.h"
+#include "scxmltag.h"
+
+#include <QDebug>
+
+using namespace ScxmlEditor::PluginInterface;
+
+SCShapeProvider::SCShapeProvider(QObject *parent)
+ : ShapeProvider(parent)
+{
+ init();
+}
+
+SCShapeProvider::~SCShapeProvider()
+{
+ clear();
+}
+
+void SCShapeProvider::clear()
+{
+ qDeleteAll(m_groups);
+ m_groups.clear();
+}
+
+void SCShapeProvider::initGroups()
+{
+ init();
+}
+
+void SCShapeProvider::init()
+{
+ ShapeGroup *group = addGroup(tr("Common States"));
+ group->addShape(createShape(tr("Initial"), QIcon(":/scxmleditor/images/initial.png"), QStringList() << "scxml"
+ << "state"
+ << "parallel",
+ "<initial/>"));
+ group->addShape(createShape(tr("Final"), QIcon(":/scxmleditor/images/final.png"), QStringList() << "scxml"
+ << "state"
+ << "parallel",
+ "<final/>"));
+ group->addShape(createShape(tr("State"), QIcon(":/scxmleditor/images/state.png"), QStringList() << "scxml"
+ << "state"
+ << "parallel",
+ "<state/>"));
+ group->addShape(createShape(tr("Parallel"), QIcon(":/scxmleditor/images/parallel.png"), QStringList() << "scxml"
+ << "state"
+ << "parallel",
+ "<parallel/>"));
+ group->addShape(createShape(tr("History"), QIcon(":/scxmleditor/images/history.png"), QStringList() << "state"
+ << "parallel",
+ "<history/>"));
+}
+
+ShapeProvider::Shape *SCShapeProvider::shape(int groupIndex, int shapeIndex)
+{
+ if (groupIndex >= 0 && groupIndex < m_groups.count()) {
+ if (shapeIndex >= 0 && shapeIndex < m_groups[groupIndex]->shapes.count())
+ return m_groups[groupIndex]->shapes[shapeIndex];
+ }
+
+ return nullptr;
+}
+
+ShapeProvider::ShapeGroup *SCShapeProvider::group(int groupIndex)
+{
+ if (groupIndex >= 0 && groupIndex < m_groups.count())
+ return m_groups[groupIndex];
+
+ return nullptr;
+}
+
+SCShapeProvider::ShapeGroup *SCShapeProvider::addGroup(const QString &title)
+{
+ auto group = new ShapeGroup;
+ group->title = title;
+ m_groups << group;
+ return group;
+}
+
+SCShapeProvider::Shape *SCShapeProvider::createShape(const QString &title, const QIcon &icon, const QStringList &filters, const QByteArray &scxmlData, const QVariant &userData)
+{
+ auto shape = new Shape;
+ shape->title = title;
+ shape->icon = icon;
+ shape->filters = filters;
+ shape->scxmlData = scxmlData;
+ shape->userData = userData;
+
+ return shape;
+}
+
+int SCShapeProvider::groupCount() const
+{
+ return m_groups.count();
+}
+
+QString SCShapeProvider::groupTitle(int groupIndex) const
+{
+ if (groupIndex >= 0 && groupIndex < m_groups.count())
+ return m_groups[groupIndex]->title;
+
+ return QString();
+}
+
+int SCShapeProvider::shapeCount(int groupIndex) const
+{
+ if (groupIndex >= 0 && groupIndex < m_groups.count())
+ return m_groups[groupIndex]->shapes.count();
+
+ return 0;
+}
+
+QString SCShapeProvider::shapeTitle(int groupIndex, int shapeIndex) const
+{
+ if (groupIndex >= 0 && groupIndex < m_groups.count()) {
+ if (shapeIndex >= 0 && shapeIndex < m_groups[groupIndex]->shapes.count())
+ return m_groups[groupIndex]->shapes[shapeIndex]->title;
+ }
+
+ return QString();
+}
+
+QIcon SCShapeProvider::shapeIcon(int groupIndex, int shapeIndex) const
+{
+ if (groupIndex >= 0 && groupIndex < m_groups.count()) {
+ if (shapeIndex >= 0 && shapeIndex < m_groups[groupIndex]->shapes.count())
+ return m_groups[groupIndex]->shapes[shapeIndex]->icon;
+ }
+
+ return QIcon();
+}
+
+bool SCShapeProvider::canDrop(int groupIndex, int shapeIndex, ScxmlTag *parent) const
+{
+ QString tagName = parent ? parent->tagName(false) : "scxml";
+ if (groupIndex >= 0 && groupIndex < m_groups.count()) {
+ if (shapeIndex >= 0 && shapeIndex < m_groups[groupIndex]->shapes.count()) {
+ const QStringList &filters = m_groups[groupIndex]->shapes[shapeIndex]->filters;
+ return filters.isEmpty() || filters.contains(tagName);
+ }
+ }
+
+ return false;
+}
+
+QByteArray SCShapeProvider::scxmlCode(int groupIndex, int shapeIndex, ScxmlTag *parent) const
+{
+ Q_UNUSED(parent)
+
+ if (groupIndex >= 0 && groupIndex < m_groups.count()) {
+ if (shapeIndex >= 0 && shapeIndex < m_groups[groupIndex]->shapes.count())
+ return m_groups[groupIndex]->shapes[shapeIndex]->scxmlData;
+ }
+
+ return QByteArray();
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/scshapeprovider.h b/src/plugins/scxmleditor/plugin_interface/scshapeprovider.h
new file mode 100644
index 00000000000..f4093b5a762
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scshapeprovider.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "shapeprovider.h"
+
+#include <QVector>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ScxmlTag;
+
+class SCShapeProvider : public ShapeProvider
+{
+public:
+ SCShapeProvider(QObject *parent = nullptr);
+ ~SCShapeProvider() override;
+
+ int groupCount() const override;
+ QString groupTitle(int groupIndex) const override;
+
+ int shapeCount(int groupIndex) const override;
+ QString shapeTitle(int groupIndex, int shapeIndex) const override;
+ QIcon shapeIcon(int groupIndex, int shapeIndex) const override;
+
+ bool canDrop(int groupIndex, int shapeIndex, ScxmlTag *parent) const override;
+ QByteArray scxmlCode(int groupIndex, int shapeIndex, ScxmlTag *parent) const override;
+
+protected:
+ virtual void clear();
+ virtual void initGroups();
+ virtual ShapeGroup *addGroup(const QString &title);
+ virtual Shape *createShape(const QString &title, const QIcon &icon, const QStringList &filters, const QByteArray &scxmlData, const QVariant &userData = QVariant());
+ Shape *shape(int groupIndex, int shapeIndex);
+ ShapeGroup *group(int groupIndex);
+
+private:
+ void init();
+ QVector<ShapeGroup*> m_groups;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scutilsprovider.cpp b/src/plugins/scxmleditor/plugin_interface/scutilsprovider.cpp
new file mode 100644
index 00000000000..89dc7bbdb0b
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scutilsprovider.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scutilsprovider.h"
+#include "mytypes.h"
+#include "scxmltag.h"
+#include "stateitem.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+SCUtilsProvider::SCUtilsProvider(QObject *parent)
+ : UtilsProvider(parent)
+{
+}
+
+void SCUtilsProvider::checkInitialState(const QList<QGraphicsItem*> &items, ScxmlTag *parentTag)
+{
+ ScxmlTag *initialStateTag = nullptr;
+
+ if (parentTag) {
+
+ // 1. If we have initial-state, we must use it as init-state
+ if (parentTag->hasChild(Initial)) {
+ parentTag->setAttribute("initial", QString());
+ } else {
+ QString id = parentTag->attribute("initial");
+
+ // 2. If no initial-state available, try to find state with initial-attribute
+ if (!id.isEmpty()) {
+ // Find state with id
+ for (int i = 0; i < parentTag->childCount(); ++i) {
+ ScxmlTag *child = parentTag->child(i);
+ if ((child->tagType() == State || child->tagType() == Parallel)
+ && child->attribute("id", true) == id) {
+ initialStateTag = child;
+ break;
+ }
+ }
+
+ if (!initialStateTag)
+ parentTag->setAttribute("initial", QString());
+ }
+
+ // 3. If we still cant find initial-state, we must select first
+ if (!initialStateTag) {
+ // Search first state
+ for (int i = 0; i < parentTag->childCount(); ++i) {
+ ScxmlTag *child = parentTag->child(i);
+ if (child->tagType() == State || child->tagType() == Parallel) {
+ initialStateTag = child;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ foreach (QGraphicsItem *item, items) {
+ if (item->type() >= StateType) {
+ auto stateItem = static_cast<StateItem*>(item);
+ if (stateItem)
+ stateItem->setInitial(stateItem->tag() == initialStateTag);
+ }
+ }
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/scutilsprovider.h b/src/plugins/scxmleditor/plugin_interface/scutilsprovider.h
new file mode 100644
index 00000000000..046a494a934
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scutilsprovider.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "utilsprovider.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ScxmlTag;
+
+class SCUtilsProvider : public UtilsProvider
+{
+public:
+ SCUtilsProvider(QObject *parent = nullptr);
+
+ void checkInitialState(const QList<QGraphicsItem*> &items, ScxmlTag *parentTag) override;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp b/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp
new file mode 100644
index 00000000000..7e665d6605c
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp
@@ -0,0 +1,701 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmldocument.h"
+#include "scxmlnamespace.h"
+#include "scxmltagutils.h"
+#include "undocommands.h"
+
+#include <QBuffer>
+#include <QDebug>
+#include <QFile>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+#include <app/app_version.h>
+
+using namespace ScxmlEditor::PluginInterface;
+
+ScxmlDocument::ScxmlDocument(const QString &fileName, QObject *parent)
+ : QObject(parent)
+{
+ initVariables();
+ m_fileName = fileName;
+ load(fileName);
+}
+
+ScxmlDocument::ScxmlDocument(const QByteArray &data, QObject *parent)
+ : QObject(parent)
+{
+ initVariables();
+ load(QLatin1String(data));
+}
+
+ScxmlDocument::~ScxmlDocument()
+{
+ clear(false);
+}
+
+void ScxmlDocument::initVariables()
+{
+ m_idDelimiter = "::";
+ m_undoStack = new QUndoStack(this);
+ connect(m_undoStack, &QUndoStack::cleanChanged, this, &ScxmlDocument::documentChanged);
+}
+
+void ScxmlDocument::clear(bool createRoot)
+{
+ m_currentTag = nullptr;
+ m_nextIdHash.clear();
+
+ // First clear undostack
+ m_undoStack->clear();
+
+ // Second delete all other available tags
+ // tags will call the removeChild-function -> m_tags will be cleared
+ for (int i = m_tags.count(); i--;)
+ delete m_tags[i];
+
+ m_rootTags.clear();
+ clearNamespaces();
+
+ if (createRoot) {
+ pushRootTag(createScxmlTag());
+ rootTag()->setAttribute("qt:editorversion", QLatin1String(Core::Constants::IDE_VERSION_LONG));
+
+ auto ns = new ScxmlNamespace("qt", "http://www.qt.io/2015/02/scxml-ext");
+ ns->setTagVisibility("editorInfo", false);
+ addNamespace(ns);
+ }
+
+ m_useFullNameSpace = false;
+}
+
+QString ScxmlDocument::nameSpaceDelimiter() const
+{
+ return m_idDelimiter;
+}
+
+bool ScxmlDocument::useFullNameSpace() const
+{
+ return m_useFullNameSpace;
+}
+
+void ScxmlDocument::setNameSpaceDelimiter(const QString &delimiter)
+{
+ m_idDelimiter = delimiter;
+}
+
+QString ScxmlDocument::fileName() const
+{
+ return m_fileName;
+}
+
+void ScxmlDocument::setFileName(const QString &filename)
+{
+ m_fileName = filename;
+}
+
+ScxmlNamespace *ScxmlDocument::scxmlNamespace(const QString &prefix)
+{
+ return m_namespaces.value(prefix, 0);
+}
+
+void ScxmlDocument::addNamespace(ScxmlNamespace *ns)
+{
+ if (!ns)
+ return;
+
+ delete m_namespaces.take(ns->prefix());
+ m_namespaces[ns->prefix()] = ns;
+
+ ScxmlTag *scxmlTag = scxmlRootTag();
+ if (scxmlTag) {
+ QMapIterator<QString, ScxmlNamespace*> i(m_namespaces);
+ while (i.hasNext()) {
+ i.next();
+ QString prefix = i.value()->prefix();
+ if (prefix.isEmpty())
+ prefix = "xmlns";
+
+ if (prefix.startsWith("xmlns"))
+ scxmlTag->setAttribute(prefix, i.value()->name());
+ else
+ scxmlTag->setAttribute(QString::fromLatin1("xmlns:%1").arg(prefix), i.value()->name());
+ }
+ }
+}
+
+void ScxmlDocument::clearNamespaces()
+{
+ while (!m_namespaces.isEmpty()) {
+ delete m_namespaces.take(m_namespaces.firstKey());
+ }
+}
+
+bool ScxmlDocument::generateSCXML(QIODevice *io, ScxmlTag *tag) const
+{
+ QXmlStreamWriter xml(io);
+ xml.setAutoFormatting(true);
+ xml.writeStartDocument();
+ if (tag)
+ tag->writeXml(xml);
+ else
+ rootTag()->writeXml(xml);
+ xml.writeEndDocument();
+
+ return !xml.hasError();
+}
+
+ScxmlTag *ScxmlDocument::createScxmlTag()
+{
+ auto tag = new ScxmlTag(Scxml, this);
+ QMapIterator<QString, ScxmlNamespace*> i(m_namespaces);
+ while (i.hasNext()) {
+ i.next();
+ QString prefix = i.value()->prefix();
+ if (prefix.isEmpty())
+ prefix = "xmlns";
+
+ if (prefix.startsWith("xmlns"))
+ tag->setAttribute(prefix, i.value()->name());
+ else
+ tag->setAttribute(QString::fromLatin1("xmlns:%1").arg(prefix), i.value()->name());
+ }
+ return tag;
+}
+
+bool ScxmlDocument::hasLayouted() const
+{
+ return m_hasLayouted;
+}
+QColor ScxmlDocument::getColor(int depth) const
+{
+ return m_colors.isEmpty() ? QColor(Qt::gray) : m_colors[depth % m_colors.count()];
+}
+
+void ScxmlDocument::setLevelColors(const QVector<QColor> &colors)
+{
+ m_colors = colors;
+ emit colorThemeChanged();
+}
+
+bool ScxmlDocument::load(QIODevice *io)
+{
+ m_currentTag = nullptr;
+ clearNamespaces();
+
+ bool ok = true;
+ clear(false);
+
+ QXmlStreamReader xml(io);
+ while (!xml.atEnd()) {
+ QXmlStreamReader::TokenType token = xml.readNext();
+
+ if (token == QXmlStreamReader::StartDocument)
+ continue;
+
+ if (token == QXmlStreamReader::StartElement) {
+ if (xml.name() == "scxml") {
+ // Get and add namespaces
+ QXmlStreamNamespaceDeclarations ns = xml.namespaceDeclarations();
+ for (int i = 0; i < ns.count(); ++i)
+ addNamespace(new ScxmlNamespace(ns[i].prefix().toString(), ns[i].namespaceUri().toString()));
+
+ // create root tag
+ pushRootTag(createScxmlTag());
+
+ // and read other tags also
+ rootTag()->readXml(xml);
+
+ // Check editorversion
+ m_hasLayouted = rootTag()->hasAttribute("qt:editorversion");
+ rootTag()->setAttribute("qt:editorversion", QLatin1String(Core::Constants::IDE_VERSION_LONG));
+ }
+ }
+
+ if (token == QXmlStreamReader::Invalid)
+ break;
+ }
+
+ if (xml.hasError()) {
+ m_hasError = true;
+ initErrorMessage(xml, io);
+ m_fileName.clear();
+ ok = false;
+
+ clear();
+ } else {
+ m_hasError = false;
+ m_lastError.clear();
+ }
+
+ m_undoStack->setClean();
+
+ return ok;
+}
+
+void ScxmlDocument::initErrorMessage(const QXmlStreamReader &xml, QIODevice *io)
+{
+ QString errorString;
+ switch (xml.error()) {
+ case QXmlStreamReader::Error::UnexpectedElementError:
+ errorString = tr("UnexpectedElementError");
+ break;
+ case QXmlStreamReader::Error::NotWellFormedError:
+ errorString = tr("NotWellFormedError");
+ break;
+ case QXmlStreamReader::Error::PrematureEndOfDocumentError:
+ errorString = tr("PrematureEndOfDocumentError");
+ break;
+ case QXmlStreamReader::Error::CustomError:
+ errorString = tr("CustomError");
+ break;
+ default:
+ break;
+ }
+
+ QString lineString;
+ io->seek(0);
+ for (int i = 0; i < xml.lineNumber() - 1; ++i)
+ io->readLine();
+ lineString = QLatin1String(io->readLine());
+
+ m_lastError = tr("Error in reading XML.\nType: %1 (%2)\nDescription: %3\n\nRow: %4, Column: %5\n%6")
+ .arg(xml.error())
+ .arg(errorString)
+ .arg(xml.errorString())
+ .arg(xml.lineNumber())
+ .arg(xml.columnNumber())
+ .arg(lineString);
+}
+
+bool ScxmlDocument::pasteData(const QByteArray &data, const QPointF &minPos, const QPointF &pastePos)
+{
+ if (!m_currentTag)
+ m_currentTag = rootTag();
+
+ if (!m_currentTag) {
+ m_hasError = true;
+ m_lastError = tr("Current tag not selected");
+ return false;
+ }
+
+ if (data.trimmed().isEmpty()) {
+ m_hasError = true;
+ m_lastError = tr("Pasted data is empty.");
+ return false;
+ }
+
+ bool ok = true;
+ m_undoStack->beginMacro(tr("Paste item(s)"));
+
+ QByteArray d(data);
+ QBuffer buffer(&d);
+ buffer.open(QIODevice::ReadOnly);
+ QXmlStreamReader xml(&buffer);
+ foreach (ScxmlNamespace *ns, m_namespaces) {
+ xml.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration(ns->prefix(), ns->name()));
+ }
+
+ m_idMap.clear();
+ QVector<ScxmlTag*> addedTags;
+
+ while (!xml.atEnd()) {
+ QXmlStreamReader::TokenType token = xml.readNext();
+
+ if (token == QXmlStreamReader::StartDocument)
+ continue;
+
+ if (token == QXmlStreamReader::StartElement) {
+ if (xml.name().toString() == "scxml")
+ continue;
+
+ ScxmlTag *childTag = nullptr;
+ if ((m_currentTag->tagType() == Initial || m_currentTag->tagType() == History) && xml.name().toString() == "transition")
+ childTag = new ScxmlTag(InitialTransition, this);
+ else
+ childTag = new ScxmlTag(xml.prefix().toString(), xml.name().toString(), this);
+
+ childTag->readXml(xml, true);
+ addedTags << childTag;
+ }
+
+ if (token == QXmlStreamReader::Invalid)
+ break;
+ }
+
+ if (xml.error()) {
+ m_hasError = true;
+ qDeleteAll(addedTags);
+ addedTags.clear();
+ initErrorMessage(xml, &buffer);
+ ok = false;
+ } else {
+ m_hasError = false;
+ m_lastError.clear();
+
+ // Fine-tune names and coordinates
+ for (int i = 0; i < addedTags.count(); ++i)
+ TagUtils::modifyPosition(addedTags[i], minPos, pastePos);
+
+ // Update targets and initial-attributes
+ for (int i = 0; i < addedTags.count(); ++i)
+ addedTags[i]->finalizeTagNames();
+
+ // Add tags to the document
+ addTags(m_currentTag, addedTags);
+ }
+
+ m_undoStack->endMacro();
+
+ return ok;
+}
+
+void ScxmlDocument::load(const QString &fileName)
+{
+ if (QFile::exists(fileName)) {
+ QFile file(fileName);
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ if (load(&file)) {
+ m_fileName = fileName;
+ }
+ }
+ }
+
+ // If loading doesn't work, create root tag here
+ if (m_rootTags.isEmpty()) {
+ pushRootTag(createScxmlTag());
+ rootTag()->setAttribute("qt:editorversion", QLatin1String(Core::Constants::IDE_VERSION_LONG));
+ }
+
+ auto ns = new ScxmlNamespace("qt", "http://www.qt.io/2015/02/scxml-ext");
+ ns->setTagVisibility("editorInfo", false);
+ addNamespace(ns);
+}
+
+void ScxmlDocument::printSCXML()
+{
+ qDebug() << content();
+}
+
+QByteArray ScxmlDocument::content(const QVector<ScxmlTag*> &tags) const
+{
+ QByteArray result;
+ if (tags.count() > 0) {
+ QBuffer buffer(&result);
+ buffer.open(QIODevice::WriteOnly);
+
+ bool writeScxml = tags.count() > 1 || tags[0]->tagType() != Scxml;
+
+ QXmlStreamWriter xml(&buffer);
+ xml.setAutoFormatting(true);
+ xml.writeStartDocument();
+ if (writeScxml)
+ xml.writeStartElement("scxml");
+
+ foreach (ScxmlTag *tag, tags) {
+ tag->writeXml(xml);
+ }
+ xml.writeEndDocument();
+
+ if (writeScxml)
+ xml.writeEndElement();
+ }
+ return result;
+}
+
+QByteArray ScxmlDocument::content(ScxmlTag *tag) const
+{
+ QByteArray byteArray;
+ QBuffer buffer(&byteArray);
+ buffer.open(QIODevice::WriteOnly);
+ generateSCXML(&buffer, tag);
+ return byteArray;
+}
+
+bool ScxmlDocument::save()
+{
+ return save(m_fileName);
+}
+
+bool ScxmlDocument::save(const QString &fileName)
+{
+ QString name(fileName);
+ if (!name.endsWith(".scxml", Qt::CaseInsensitive))
+ name.append(".scxml");
+
+ bool ok = true;
+
+ QFile file(name);
+ if (file.open(QIODevice::WriteOnly)) {
+ ok = generateSCXML(&file, scxmlRootTag());
+ if (ok) {
+ m_fileName = name;
+ m_undoStack->setClean();
+ }
+ file.close();
+ if (!ok)
+ m_lastError = tr("Cannot save xml to the file %1.").arg(fileName);
+ } else {
+ ok = false;
+ m_lastError = tr("Cannot open file %1.").arg(fileName);
+ }
+
+ return ok;
+}
+
+void ScxmlDocument::setContent(ScxmlTag *tag, const QString &content)
+{
+ if (tag && !m_undoRedoRunning)
+ m_undoStack->push(new SetContentCommand(this, tag, content));
+}
+
+void ScxmlDocument::setValue(ScxmlTag *tag, const QString &key, const QString &value)
+{
+ if (tag && !m_undoRedoRunning)
+ m_undoStack->push(new SetAttributeCommand(this, tag, key, value));
+}
+
+void ScxmlDocument::setValue(ScxmlTag *tag, int attributeIndex, const QString &value)
+{
+ if (tag && attributeIndex >= 0 && attributeIndex < tag->info()->n_attributes)
+ m_undoStack->push(new SetAttributeCommand(this, tag, QLatin1String(tag->info()->attributes[attributeIndex].name), value));
+}
+
+void ScxmlDocument::setEditorInfo(ScxmlTag *tag, const QString &key, const QString &value)
+{
+ if (tag && !m_undoRedoRunning)
+ m_undoStack->push(new SetEditorInfoCommand(this, tag, key, value));
+}
+
+void ScxmlDocument::setCurrentTag(ScxmlTag *tag)
+{
+ if (tag != m_currentTag) {
+ emit beginTagChange(TagCurrentChanged, tag, QVariant());
+ m_currentTag = tag;
+ emit endTagChange(TagCurrentChanged, tag, QVariant());
+ }
+}
+
+ScxmlTag *ScxmlDocument::currentTag() const
+{
+ return m_currentTag;
+}
+
+void ScxmlDocument::changeParent(ScxmlTag *child, ScxmlTag *newParent, int tagIndex)
+{
+ if (child && child->parentTag() != newParent && !m_undoRedoRunning)
+ m_undoStack->push(new ChangeParentCommand(this, child, newParent == nullptr ? rootTag() : newParent, tagIndex));
+}
+
+void ScxmlDocument::changeOrder(ScxmlTag *child, int newPos)
+{
+ if (child && !m_undoRedoRunning) {
+ ScxmlTag *parentTag = child->parentTag();
+ if (parentTag) {
+ m_undoStack->push(new ChangeOrderCommand(this, child, parentTag, newPos));
+ }
+ }
+}
+
+void ScxmlDocument::addTags(ScxmlTag *parent, const QVector<ScxmlTag*> tags)
+{
+ if (m_undoRedoRunning)
+ return;
+
+ if (!parent)
+ parent = rootTag();
+
+ m_undoStack->push(new AddRemoveTagsBeginCommand(this, parent));
+ for (int i = 0; i < tags.count(); ++i)
+ addTag(parent, tags[i]);
+ m_undoStack->push(new AddRemoveTagsEndCommand(this, parent));
+}
+
+void ScxmlDocument::addTag(ScxmlTag *parent, ScxmlTag *child)
+{
+ if (m_undoRedoRunning)
+ return;
+
+ if (!parent)
+ parent = rootTag();
+
+ if (parent && child) {
+ m_undoStack->beginMacro(tr("Add Tag"));
+ addTagRecursive(parent, child);
+ m_undoStack->endMacro();
+ }
+}
+
+void ScxmlDocument::removeTag(ScxmlTag *tag)
+{
+ if (tag && !m_undoRedoRunning) {
+ // Create undo/redo -macro, because state can includes lot of child-states
+ m_undoStack->beginMacro(tr("Remove Tag"));
+ removeTagRecursive(tag);
+ m_undoStack->endMacro();
+ }
+}
+
+void ScxmlDocument::addTagRecursive(ScxmlTag *parent, ScxmlTag *tag)
+{
+ if (tag && !m_undoRedoRunning) {
+ m_undoStack->push(new AddRemoveTagCommand(this, parent, tag, TagAddChild));
+
+ // First create AddRemoveTagCommands for the all children recursive
+ for (int i = 0; i < tag->childCount(); ++i)
+ addTagRecursive(tag, tag->child(i));
+ }
+}
+
+void ScxmlDocument::removeTagRecursive(ScxmlTag *tag)
+{
+ if (tag && !m_undoRedoRunning) {
+ // First create AddRemoveTagCommands for the all children recursive
+ int childCount = tag->childCount();
+ for (int i = childCount; i--;)
+ removeTagRecursive(tag->child(i));
+
+ m_undoStack->push(new AddRemoveTagCommand(this, tag->parentTag(), tag, TagRemoveChild));
+ }
+}
+
+void ScxmlDocument::setUseFullNameSpace(bool use)
+{
+ if (m_useFullNameSpace != use)
+ m_undoStack->push(new ChangeFullNameSpaceCommand(this, scxmlRootTag(), use));
+}
+
+QString ScxmlDocument::nextUniqueId(const QString &key)
+{
+ QString name;
+ bool bFound = false;
+ int id = 0;
+ while (true) {
+ id = m_nextIdHash.value(key, 0) + 1;
+ m_nextIdHash[key] = id;
+ bFound = false;
+ name = QString::fromLatin1("%1_%2").arg(key).arg(id);
+
+ // Check duplicate
+ foreach (const ScxmlTag *tag, m_tags) {
+ if (tag->attribute("id") == name) {
+ bFound = true;
+ break;
+ }
+ }
+ if (!bFound)
+ break;
+ }
+ return name;
+}
+
+QString ScxmlDocument::getUniqueCopyId(const ScxmlTag *tag)
+{
+ const QString key = tag->attribute("id");
+ QString name = key;
+ int counter = 1;
+ bool bFound = false;
+
+ while (true) {
+ bFound = false;
+ // Check duplicate
+ foreach (const ScxmlTag *t, m_tags) {
+ if (t->attribute("id") == name && t != tag) {
+ name = QString::fromLatin1("%1_Copy(%2)").arg(key).arg(counter);
+ bFound = true;
+ counter++;
+ }
+ }
+
+ if (!bFound)
+ break;
+ }
+
+ return name;
+}
+
+bool ScxmlDocument::changed() const
+{
+ return !m_undoStack->isClean();
+}
+
+ScxmlTag *ScxmlDocument::scxmlRootTag() const
+{
+ ScxmlTag *tag = rootTag();
+ while (tag && tag->tagType() != Scxml) {
+ tag = tag->parentTag();
+ }
+
+ return tag;
+}
+
+ScxmlTag *ScxmlDocument::rootTag() const
+{
+ return m_rootTags.isEmpty() ? 0 : m_rootTags.last();
+}
+
+void ScxmlDocument::pushRootTag(ScxmlTag *tag)
+{
+ m_rootTags << tag;
+}
+
+ScxmlTag *ScxmlDocument::popRootTag()
+{
+ return m_rootTags.takeLast();
+}
+
+void ScxmlDocument::deleteRootTags()
+{
+ while (m_rootTags.count() > 0)
+ delete m_rootTags.takeLast();
+}
+
+QUndoStack *ScxmlDocument::undoStack() const
+{
+ return m_undoStack;
+}
+
+void ScxmlDocument::addChild(ScxmlTag *tag)
+{
+ if (!m_tags.contains(tag))
+ m_tags << tag;
+}
+
+void ScxmlDocument::removeChild(ScxmlTag *tag)
+{
+ m_tags.removeAll(tag);
+}
+
+void ScxmlDocument::setUndoRedoRunning(bool para)
+{
+ m_undoRedoRunning = para;
+}
+
+QFileInfo ScxmlDocument::qtBinDir() const
+{
+ return m_qtBinDir;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmldocument.h b/src/plugins/scxmleditor/plugin_interface/scxmldocument.h
new file mode 100644
index 00000000000..ee6bba3ae2d
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmldocument.h
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QFileInfo>
+#include <QMap>
+#include <QObject>
+#include <QPointF>
+#include <QString>
+#include <QVector>
+
+QT_FORWARD_DECLARE_CLASS(QUndoStack)
+QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ScxmlNamespace;
+class ScxmlTag;
+
+/**
+ * @brief The ScxmlDocument class represents an SCXML document.
+ *
+ * This is the root of the document tree, and provides the access to the document's data.
+ * The tag-element is ScxmlTag. You can create ScxmlTag-objects outside this class, but you have to call data-manipulation
+ * functions from this class to be sure that all views can update their tags. When calling data-manipulation functions, this class
+ * will send beginTagChange and endTagChange signals with the correspond values. This class also keep the undo-stack of the changes.
+ *
+ * When document has changed, the signal documentChanged(bool) will be emitted.
+ */
+class ScxmlDocument : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief The TagChange enum
+ */
+ enum TagChange {
+ TagAddChild = 0,
+ TagAddTags,
+ TagRemoveChild,
+ TagRemoveTags,
+ TagCurrentChanged,
+ TagAttributesChanged,
+ TagEditorInfoChanged,
+ TagChangeParent,
+ TagChangeParentRemoveChild,
+ TagChangeParentAddChild,
+ TagChangeOrder,
+ TagContentChanged,
+ TagChangeFullNameSpace
+ };
+
+ explicit ScxmlDocument(const QString &fileName = QString(), QObject *parent = nullptr);
+ explicit ScxmlDocument(const QByteArray &data, QObject *parent = nullptr);
+ ~ScxmlDocument() override;
+
+ void addNamespace(ScxmlNamespace *ns);
+ ScxmlNamespace *scxmlNamespace(const QString &prefix);
+
+ /**
+ * @brief setCurrentTag - inform views that current selected tag has changed
+ * @param tag - current tag
+ */
+ void setCurrentTag(ScxmlTag *tag);
+ ScxmlTag *currentTag() const;
+
+ /**
+ * @brief setContent - inform views that tag-content has changed
+ * @param tag
+ * @param content
+ */
+ void setContent(ScxmlTag *tag, const QString &content);
+
+ /**
+ * @brief setValue - inform views that tag-value has changed
+ * @param tag
+ * @param key
+ * @param value
+ */
+ void setValue(ScxmlTag *tag, const QString &key, const QString &value);
+
+ /**
+ * @brief setValue - inform views that tag-value has changed
+ * @param tag
+ * @param attributeIndex
+ * @param value
+ */
+ void setValue(ScxmlTag *tag, int attributeIndex, const QString &value);
+
+ /**
+ * @brief setEditorInfo - inform views (and save undo-command) that editorinfo has changed
+ * @param tag
+ * @param key
+ * @param value
+ */
+ void setEditorInfo(ScxmlTag *tag, const QString &key, const QString &value);
+
+ /**
+ * @brief addTag - inform views that new child will be added to the tree
+ * @param parent
+ * @param child
+ */
+ void addTag(ScxmlTag *parent, ScxmlTag *child);
+ void addTags(ScxmlTag *parent, const QVector<ScxmlTag*> tags);
+
+ /**
+ * @brief removeTag - inform views that tag will be removed
+ * @param tag
+ */
+ void removeTag(ScxmlTag *tag);
+
+ /**
+ * @brief changeParent - inform views that tag parent will be changed
+ * @param child
+ * @param newParent
+ * @param tagIndex
+ */
+ void changeParent(ScxmlTag *child, ScxmlTag *newParent, int tagIndex = -1);
+
+ /**
+ * @brief changeOrder - inform vies that tag's child-position will be changed
+ * @param child
+ * @param newPos
+ */
+ void changeOrder(ScxmlTag *child, int newPos);
+
+ /**
+ * @brief changed - holds the changes-status of the document
+ * @return - true if changed, false otherwise
+ */
+ bool changed() const;
+
+ /**
+ * @brief rootTag - return rootTag of the document
+ * @return
+ */
+ ScxmlTag *rootTag() const;
+ ScxmlTag *scxmlRootTag() const;
+ void pushRootTag(ScxmlTag *tag);
+ ScxmlTag *popRootTag();
+
+ /**
+ * @brief fileName - return current filename of the document
+ * @return
+ */
+ QString fileName() const;
+ void setFileName(const QString &filename);
+
+ /**
+ * @brief undoStack - return undo-stack of the document (see UndoStack)
+ * @return
+ */
+ QUndoStack *undoStack() const;
+
+ /**
+ * @brief save - save document with given filename
+ * @param fileName -
+ * @return return true if file has successfully wrote
+ */
+ bool save(const QString &fileName);
+ bool save();
+ void load(const QString &fileName);
+ bool pasteData(const QByteArray &data, const QPointF &minPos = QPointF(0, 0), const QPointF &pastePos = QPointF(0, 0));
+ bool load(QIODevice *io);
+
+ /**
+ * @brief nextId - generate and return next id depends on the given key
+ * @param key
+ * @return return value will be the form key_nextId
+ */
+ QString nextUniqueId(const QString &key);
+ QString getUniqueCopyId(const ScxmlTag *tag);
+
+ /**
+ * @brief hasLayouted - tells is there qt-namespace available in the current loaded document
+ * @return true or false
+ */
+ bool hasLayouted() const;
+
+ void setLevelColors(const QVector<QColor> &colors);
+ QColor getColor(int depth) const;
+
+ bool hasError() const
+ {
+ return m_hasError;
+ }
+
+ QString lastError() const
+ {
+ return m_lastError;
+ }
+
+ QByteArray content(const QVector<ScxmlTag*> &tags) const;
+ QByteArray content(ScxmlTag *tag = nullptr) const;
+ void clear(bool createRoot = true);
+
+ // Full NameSpace functions
+ QString nameSpaceDelimiter() const;
+ bool useFullNameSpace() const;
+ void setNameSpaceDelimiter(const QString &delimiter);
+ void setUseFullNameSpace(bool use);
+
+ void setUndoRedoRunning(bool para);
+
+ QFileInfo qtBinDir() const;
+
+signals:
+ /**
+ * @brief documentChanged - this signal will be emitted just after document has changed and is not in clean state
+ * @param changed
+ */
+ void documentChanged(bool changed);
+
+ /**
+ * @brief beginTagChange - this signal will be emitted just before some tagchange will be made (see TagChange)
+ * @param change
+ * @param tag
+ * @param value
+ */
+ void beginTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value);
+
+ /**
+ * @brief beginTagChange - this signal will be emitted just after some tagchange has made (see TagChange)
+ * @param change
+ * @param tag
+ * @param value
+ */
+ void endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag, const QVariant &value);
+
+ /**
+ * @brief colorThemeChanged - this signal will be emitted just after color theme has changed
+ */
+ void colorThemeChanged();
+
+private:
+ friend class ScxmlTag;
+ friend class ChangeFullNameSpaceCommand;
+
+ void initErrorMessage(const QXmlStreamReader &xml, QIODevice *io);
+ bool generateSCXML(QIODevice *io, ScxmlTag *tag = nullptr) const;
+ void addChild(ScxmlTag *tag);
+ void removeChild(ScxmlTag *tag);
+ void printSCXML();
+ void deleteRootTags();
+ void clearNamespaces();
+ void initVariables();
+ void addTagRecursive(ScxmlTag *parent, ScxmlTag *tag);
+ void removeTagRecursive(ScxmlTag *tag);
+
+ ScxmlTag *createScxmlTag();
+ QString m_fileName;
+ QUndoStack *m_undoStack;
+ QVector<ScxmlTag*> m_tags;
+ QHash<QString, int> m_nextIdHash;
+ QHash<QString, QString> m_idMap;
+ bool m_hasError = false;
+ QString m_lastError;
+ QVector<ScxmlTag*> m_rootTags;
+ QMap<QString, ScxmlNamespace*> m_namespaces;
+ QVector<QColor> m_colors;
+ bool m_hasLayouted = false;
+ QString m_idDelimiter;
+ bool m_useFullNameSpace = false;
+ ScxmlTag *m_currentTag = nullptr;
+ bool m_undoRedoRunning = false;
+ QFileInfo m_qtBinDir;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmlnamespace.cpp b/src/plugins/scxmleditor/plugin_interface/scxmlnamespace.cpp
new file mode 100644
index 00000000000..05bcc2e0454
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmlnamespace.cpp
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmlnamespace.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+ScxmlNamespace::ScxmlNamespace(const QString &prefix, const QString &name, QObject *parent)
+ : QObject(parent)
+ , m_prefix(prefix)
+ , m_name(name)
+{
+}
+
+QString ScxmlNamespace::prefix() const
+{
+ return m_prefix;
+}
+
+QString ScxmlNamespace::name() const
+{
+ return m_name;
+}
+
+bool ScxmlNamespace::isTagVisible(const QString &tag) const
+{
+ return m_tagVisibility.value(tag, true);
+}
+
+void ScxmlNamespace::setTagVisibility(const QString &tag, bool visible)
+{
+ m_tagVisibility[tag] = visible;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmlnamespace.h b/src/plugins/scxmleditor/plugin_interface/scxmlnamespace.h
new file mode 100644
index 00000000000..3bc4517c349
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmlnamespace.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QMap>
+#include <QObject>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ScxmlNamespace : public QObject
+{
+public:
+ ScxmlNamespace(const QString &prefix, const QString &name, QObject *parent = nullptr);
+
+ QString prefix() const;
+ QString name() const;
+
+ bool isTagVisible(const QString &tag) const;
+ void setTagVisibility(const QString &tag, bool visible);
+
+private:
+ QString m_prefix, m_name;
+ QMap<QString, bool> m_tagVisibility;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmltag.cpp b/src/plugins/scxmleditor/plugin_interface/scxmltag.cpp
new file mode 100644
index 00000000000..0548972ad01
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmltag.cpp
@@ -0,0 +1,679 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmltag.h"
+#include "scxmldocument.h"
+#include "scxmleditorconstants.h"
+#include "scxmltagutils.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+
+using namespace ScxmlEditor::PluginInterface;
+
+ScxmlTag::ScxmlTag(TagType type, ScxmlDocument *document)
+ : m_prefix(
+ QLatin1String(type == Metadata || type == MetadataItem ? "qt" : ""))
+{
+ setDocument(document);
+ init(type);
+ m_tagName = QLatin1String(m_info->name);
+}
+
+ScxmlTag::ScxmlTag(const QString &prefix, const QString &name,
+ ScxmlDocument *document)
+ : m_tagName(name)
+ , m_prefix(prefix)
+{
+ setDocument(document);
+ TagType type = UnknownTag;
+ for (int i = 0; i < Finalize; ++i) {
+ if (QLatin1String(scxml_tags[i].name) == name) {
+ type = (TagType)i;
+ break;
+ }
+ }
+
+ init(type);
+}
+
+ScxmlTag::ScxmlTag(const ScxmlTag *other, bool copyChildren)
+{
+ setDocument(other->m_document);
+ m_tagType = other->m_tagType;
+ m_tagName = other->m_tagName;
+ m_content = other->m_content;
+ m_prefix = other->m_prefix;
+ m_info = &scxml_tags[m_tagType];
+ m_attributeNames = other->m_attributeNames;
+ m_attributeValues = other->m_attributeValues;
+ m_editorInfo = other->m_editorInfo;
+
+ if (copyChildren) {
+ for (int i = 0; i < other->m_childTags.count(); ++i)
+ appendChild(new ScxmlTag(other->m_childTags[i], copyChildren));
+ }
+}
+
+ScxmlTag::~ScxmlTag()
+{
+ if (m_document)
+ m_document->removeChild(this);
+
+ m_attributeNames.clear();
+ m_attributeValues.clear();
+ m_childTags.clear();
+
+ // m_parentTag = 0;
+ m_document = nullptr;
+ m_info = nullptr;
+ m_tagType = UnknownTag;
+}
+
+void ScxmlTag::finalizeTagNames()
+{
+ switch (m_tagType) {
+ case State: {
+ if (hasAttribute("initial")) {
+ QString oldInitial = attribute("initial");
+ QString newInitial = m_document->m_idMap.value(oldInitial);
+ setAttribute("initial", newInitial);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ for (int i = m_childTags.count(); i--;) {
+ ScxmlTag *t = m_childTags[i];
+ switch (t->tagType()) {
+ case InitialTransition:
+ case Transition: {
+ QString oldTarget = t->attribute("target");
+ QString newTarget = m_document->m_idMap.value(oldTarget);
+ if (!oldTarget.isEmpty() && newTarget.isEmpty())
+ delete m_childTags.takeAt(i);
+ else
+ t->setAttribute("target", newTarget);
+ break;
+ }
+ default:
+ t->finalizeTagNames();
+ break;
+ }
+ }
+}
+
+void ScxmlTag::print()
+{
+ qDebug() << "type " << m_tagType;
+ qDebug() << "name " << m_tagName;
+ qDebug() << "parent " << (m_parentTag ? m_parentTag->m_tagName : "");
+ qDebug() << "attributeNames " << m_attributeNames;
+ qDebug() << "attributeValues " << m_attributeValues;
+ qDebug() << "childcount " << m_childTags.count();
+ for (int i = 0; i < m_childTags.count(); ++i)
+ qDebug() << " child " << i << m_childTags[i]->m_tagName;
+}
+
+void ScxmlTag::init(TagType type)
+{
+ m_tagType = type;
+ m_info = &scxml_tags[type];
+
+ for (int i = 0; i < m_info->n_attributes; ++i) {
+ if (m_info->attributes[i].value)
+ setAttribute(
+ QLatin1String(m_info->attributes[i].name),
+ QString::fromLatin1(m_info->attributes[i].value).split(";").first());
+ }
+
+ initId();
+}
+
+void ScxmlTag::initId()
+{
+ // Init state IDs
+ if (m_document) {
+ switch (m_tagType) {
+ case State:
+ setAttribute("id", m_document->nextUniqueId("State"));
+ break;
+ case Parallel:
+ setAttribute("id", m_document->nextUniqueId("Parallel"));
+ break;
+ case History:
+ setAttribute("id", m_document->nextUniqueId("History"));
+ break;
+ case Transition:
+ setAttribute("event", m_document->nextUniqueId("Transition"));
+ break;
+ case Final:
+ setAttribute("id", m_document->nextUniqueId("Final"));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void ScxmlTag::setDocument(ScxmlDocument *document)
+{
+ if (m_document != document) {
+ if (m_document)
+ m_document->removeChild(this);
+
+ m_document = document;
+ if (m_document)
+ m_document->addChild(this);
+ }
+}
+
+ScxmlDocument *ScxmlTag::document() const
+{
+ return m_document;
+}
+
+const scxmltag_type_t *ScxmlTag::info() const
+{
+ return m_info;
+}
+
+TagType ScxmlTag::tagType() const
+{
+ return m_tagType;
+}
+
+QString ScxmlTag::prefix() const
+{
+ return m_prefix;
+}
+
+void ScxmlTag::setTagName(const QString &name)
+{
+ m_tagName = name;
+}
+
+QString ScxmlTag::tagName(bool addPrefix) const
+{
+ if (m_prefix.isEmpty() || !addPrefix)
+ return m_tagName;
+ else
+ return QString::fromLatin1("%1:%2").arg(m_prefix).arg(m_tagName);
+}
+
+QString ScxmlTag::displayName() const
+{
+ switch (m_tagType) {
+ case State:
+ case Parallel:
+ case Final:
+ return attribute("id");
+ case Transition:
+ case InitialTransition:
+ return attribute("event");
+ break;
+ default:
+ return QString();
+ }
+}
+
+QString ScxmlTag::stateNameSpace() const
+{
+ if (m_parentTag) {
+ switch (m_parentTag->m_tagType) {
+ case State:
+ case Parallel:
+ return QString::fromLatin1("%1%2%3")
+ .arg(m_parentTag->stateNameSpace())
+ .arg(m_parentTag->attribute(
+ m_parentTag->m_attributeNames.indexOf("id")))
+ .arg(m_document->nameSpaceDelimiter());
+ default:
+ break;
+ }
+ }
+
+ return QString();
+}
+
+QString ScxmlTag::content() const
+{
+ return m_content;
+}
+
+void ScxmlTag::setContent(const QString &content)
+{
+ m_content = content.trimmed();
+}
+
+bool ScxmlTag::hasData() const
+{
+ if (m_attributeNames.count() > 0 || !m_content.isEmpty())
+ return true;
+
+ foreach (ScxmlTag *tag, m_childTags) {
+ if (tag->hasData())
+ return true;
+ }
+
+ return false;
+}
+
+bool ScxmlTag::hasChild(TagType type) const
+{
+ foreach (ScxmlTag *tag, m_childTags) {
+ if (tag->tagType() == type)
+ return true;
+ }
+
+ return false;
+}
+
+bool ScxmlTag::hasChild(const QString &name) const
+{
+ foreach (ScxmlTag *tag, m_childTags) {
+ if (tag->tagName() == name)
+ return true;
+ }
+
+ return false;
+}
+
+void ScxmlTag::insertChild(int index, ScxmlTag *child)
+{
+ if (index >= 0 && index < m_childTags.count()) {
+ m_childTags.insert(index, child);
+ child->setParentTag(this);
+ } else
+ appendChild(child);
+}
+
+void ScxmlTag::appendChild(ScxmlTag *child)
+{
+ if (!m_childTags.contains(child)) {
+ m_childTags.append(child);
+ child->setParentTag(this);
+ }
+}
+
+void ScxmlTag::removeChild(ScxmlTag *child)
+{
+ m_childTags.removeAll(child);
+}
+
+void ScxmlTag::moveChild(int oldPos, int newPos)
+{
+ m_childTags.insert(newPos, m_childTags.takeAt(oldPos));
+}
+
+QString ScxmlTag::attributeName(int ind) const
+{
+ if (ind >= 0 && ind < m_attributeNames.count())
+ return m_attributeNames[ind];
+
+ return QString();
+}
+
+QString ScxmlTag::attribute(int ind, const QString &defaultValue) const
+{
+ if (ind >= 0 && ind < m_attributeValues.count())
+ return m_attributeValues[ind];
+
+ return defaultValue;
+}
+
+void ScxmlTag::setEditorInfo(const QString &key, const QString &value)
+{
+ if (value.isEmpty())
+ m_editorInfo.remove(key);
+ else
+ m_editorInfo[key] = value;
+}
+
+QString ScxmlTag::editorInfo(const QString &key) const
+{
+ return m_editorInfo.value(key);
+}
+
+bool ScxmlTag::hasEditorInfo(const QString &key) const
+{
+ return m_editorInfo.keys().contains(key);
+}
+
+void ScxmlTag::setAttributeName(int ind, const QString &name)
+{
+ if (m_attributeNames.contains(name))
+ return;
+
+ if (ind >= 0 && ind < m_attributeValues.count()) {
+ m_attributeNames[ind] = name;
+ } else {
+ m_attributeNames << name;
+ m_attributeValues << QCoreApplication::translate(
+ "SXCMLTag::UnknownAttributeValue", "Unknown");
+ }
+}
+
+void ScxmlTag::setAttribute(int ind, const QString &value)
+{
+ if (ind >= 0 && ind < m_attributeNames.count())
+ setAttribute(m_attributeNames[ind], value);
+ else {
+ m_attributeNames << QCoreApplication::translate(
+ "SXCMLTag::UnknownAttributeName", "Unknown");
+ m_attributeValues << value;
+ }
+}
+
+void ScxmlTag::setAttribute(const QString &name, const QString &value)
+{
+ if (value.isEmpty()) {
+ int ind = m_attributeNames.indexOf(name);
+ if (ind >= 0 && ind < m_attributeNames.count()) {
+ m_attributeNames.removeAt(ind);
+ m_attributeValues.removeAt(ind);
+ }
+ } else if (name.isEmpty()) {
+ int ind = m_attributeValues.indexOf(value);
+ if (ind >= 0 && ind < m_attributeValues.count()) {
+ m_attributeNames.removeAt(ind);
+ m_attributeValues.removeAt(ind);
+ }
+ } else {
+ int ind = m_attributeNames.indexOf(name);
+ if (ind >= 0 && ind < m_attributeNames.count()) {
+ m_attributeNames[ind] = name;
+ m_attributeValues[ind] = value;
+ } else {
+ m_attributeNames << name;
+ m_attributeValues << value;
+ }
+ }
+}
+
+QStringList ScxmlTag::attributeNames() const
+{
+ return m_attributeNames;
+}
+
+QStringList ScxmlTag::attributeValues() const
+{
+ return m_attributeValues;
+}
+
+int ScxmlTag::attributeCount() const
+{
+ return m_attributeNames.count();
+}
+
+QString ScxmlTag::attribute(const QString &attr, bool useNameSpace,
+ const QString &defaultValue) const
+{
+ QString value = attribute(m_attributeNames.indexOf(attr), defaultValue);
+ if (useNameSpace && m_document->useFullNameSpace())
+ return QString::fromLatin1("%1%2").arg(stateNameSpace()).arg(value);
+
+ return value;
+}
+
+bool ScxmlTag::hasAttribute(const QString &key) const
+{
+ return m_attributeNames.contains(key);
+}
+
+int ScxmlTag::childCount() const
+{
+ return m_childTags.count();
+}
+
+int ScxmlTag::childIndex(const ScxmlTag *child) const
+{
+ return m_childTags.indexOf(const_cast<ScxmlTag*>(child));
+}
+
+void ScxmlTag::setParentTag(ScxmlTag *parentTag)
+{
+ m_parentTag = parentTag;
+}
+
+bool ScxmlTag::hasParentTag() const
+{
+ return m_parentTag != nullptr;
+}
+
+ScxmlTag *ScxmlTag::child(int index) const
+{
+ return index >= 0 && index < m_childTags.count()
+ ? m_childTags.value(index)
+ : 0;
+}
+
+ScxmlTag *ScxmlTag::parentTag() const
+{
+ return m_parentTag;
+}
+
+int ScxmlTag::index() const
+{
+ return m_parentTag ? m_parentTag->childIndex(const_cast<ScxmlTag*>(this))
+ : 0;
+}
+
+bool ScxmlTag::isRootTag() const
+{
+ return m_document->rootTag() == this;
+}
+
+QVector<ScxmlTag*> ScxmlTag::allChildren() const
+{
+ return m_childTags;
+}
+
+QVector<ScxmlTag*> ScxmlTag::children(const QString &name) const
+{
+ QVector<ScxmlTag*> children;
+ foreach (ScxmlTag *tag, m_childTags) {
+ if (tag->tagName() == name)
+ children << tag;
+ }
+
+ return children;
+}
+
+ScxmlTag *ScxmlTag::child(const QString &name) const
+{
+ foreach (ScxmlTag *tag, m_childTags) {
+ if (tag->tagName() == name)
+ return tag;
+ }
+
+ return nullptr;
+}
+
+void ScxmlTag::writeXml(QXmlStreamWriter &xml)
+{
+ // Start tag
+ xml.writeStartElement(tagName());
+
+ // Don't write attribute if type is Script and content is available
+ if (m_tagType != Script || m_content.isEmpty()) {
+ for (int i = 0; i < m_attributeNames.count(); ++i) {
+ if (m_attributeNames[i] == "id" && m_document->useFullNameSpace())
+ xml.writeAttribute("id", attribute("id", true));
+ else
+ xml.writeAttribute(m_attributeNames[i], m_attributeValues[i]);
+ }
+ }
+
+ // If tag is Scxml (root), we need to fine-tune initial-state
+ if (m_tagType == Scxml) {
+ setEditorInfo("initialGeometry", QString());
+ setEditorInfo("transitionGeometry", QString());
+
+ ScxmlTag *initialTag = child("initial");
+ if (initialTag) {
+ setEditorInfo("initialGeometry", initialTag->editorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY));
+
+ ScxmlTag *initialTransitionTag = initialTag->child("transition");
+ if (initialTransitionTag) {
+ xml.writeAttribute("initial",
+ initialTransitionTag->attribute("target"));
+ setEditorInfo("transitionGeometry",
+ initialTransitionTag->editorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY));
+ }
+ }
+ }
+
+ // Write content if necessary
+ if (m_info->canIncludeContent && !m_content.isEmpty())
+ xml.writeCharacters(m_content);
+
+ // Write editorinfo if necessary
+ if (!m_editorInfo.isEmpty()) {
+ xml.writeStartElement("qt:editorinfo");
+ QHashIterator<QString, QString> i(m_editorInfo);
+ while (i.hasNext()) {
+ i.next();
+ xml.writeAttribute(i.key(), i.value());
+ }
+ xml.writeEndElement();
+ }
+
+ // Write children states
+ for (int i = 0; i < m_childTags.count(); ++i) {
+ if (m_tagType == Scxml && m_childTags[i]->tagType() == Initial)
+ continue;
+
+ if ((m_childTags[i]->tagType() == Metadata || m_childTags[i]->tagType() == MetadataItem) && !m_childTags[i]->hasData())
+ continue;
+
+ m_childTags[i]->writeXml(xml);
+ }
+
+ // End tag
+ xml.writeEndElement();
+}
+
+void ScxmlTag::readXml(QXmlStreamReader &xml, bool checkCopyId)
+{
+ QString scxmlInitial;
+
+ // Read and set attributes
+ QXmlStreamAttributes attributes = xml.attributes();
+ for (int i = 0; i < attributes.count(); ++i) {
+ if (m_tagType == Scxml && attributes[i].qualifiedName() == "initial")
+ scxmlInitial = attributes[i].value().toString();
+ else {
+ QString key = attributes[i].qualifiedName().toString();
+ QString value = attributes[i].value().toString();
+
+ // Modify id-attribute if necessary
+ switch (m_tagType) {
+ case History:
+ case Final:
+ case State:
+ case Parallel:
+ case Initial: {
+ if (key == "id") {
+ setAttribute(key, value);
+
+ QString oldId = value;
+ QString parentNS = stateNameSpace();
+ if (value.startsWith(parentNS))
+ value.remove(parentNS);
+
+ if (checkCopyId)
+ value = m_document->getUniqueCopyId(this);
+ if (m_document->useFullNameSpace())
+ m_document->m_idMap[oldId] = QString::fromLatin1("%1%2").arg(stateNameSpace()).arg(value);
+ else
+ m_document->m_idMap[oldId] = value;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ setAttribute(key, value);
+ }
+ }
+
+ // Read children states
+ QXmlStreamReader::TokenType token = xml.readNext();
+ while (token != QXmlStreamReader::EndElement) {
+ if (token == QXmlStreamReader::Characters)
+ m_content = xml.text().toString().trimmed();
+ else if (token == QXmlStreamReader::StartElement) {
+ if (m_tagType != Metadata && m_tagType != MetadataItem && xml.qualifiedName().toString() == "qt:editorinfo") {
+ // Read editorinfos
+ QXmlStreamAttributes attributes = xml.attributes();
+ foreach (QXmlStreamAttribute attr, attributes) {
+ m_editorInfo[attr.name().toString()] = attr.value().toString();
+ }
+
+ // Update fullnamespace-type of the scxmldocument
+ if (m_tagType == Scxml) {
+ m_document->m_useFullNameSpace = m_editorInfo.value("fullnamespace", "false") == "true";
+ m_document->m_idDelimiter = m_editorInfo.value("fullnamespacedelimiter", "::");
+ }
+
+ xml.readNext();
+ } else {
+ ScxmlTag *childTag = nullptr;
+ if ((m_tagType == Initial || m_tagType == History) && xml.name().toString() == "transition")
+ childTag = new ScxmlTag(InitialTransition, m_document);
+ else
+ childTag = new ScxmlTag(xml.prefix().toString(),
+ xml.name().toString(), m_document);
+
+ appendChild(childTag);
+ childTag->readXml(xml, checkCopyId);
+ }
+ } else if (token == QXmlStreamReader::Invalid) {
+ qDebug() << ScxmlTag::tr("Error in reading XML ") << xml.error() << ":"
+ << xml.errorString();
+ break;
+ }
+
+ token = xml.readNext();
+ }
+
+ // Create initial state of the scxml (root)
+ if (m_tagType == Scxml && !scxmlInitial.isEmpty()) {
+ auto initialTag = new ScxmlTag(Initial, m_document);
+ auto initialTransitionTag = new ScxmlTag(InitialTransition, m_document);
+ initialTransitionTag->setAttribute("target", scxmlInitial);
+ initialTag->setEditorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY,
+ m_editorInfo.value("initialGeometry"));
+ initialTransitionTag->setEditorInfo(
+ Constants::C_SCXML_EDITORINFO_GEOMETRY, m_editorInfo.value("transitionGeometry"));
+
+ appendChild(initialTag);
+ initialTag->appendChild(initialTransitionTag);
+ }
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmltag.h b/src/plugins/scxmleditor/plugin_interface/scxmltag.h
new file mode 100644
index 00000000000..7228357fb2a
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmltag.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmltypes.h"
+#include <QHash>
+#include <QObject>
+#include <QPointer>
+#include <QStringList>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ScxmlDocument;
+
+/**
+ * @brief The ScxmlTag class represents one element in the SCXML document tree.
+ *
+ * Tag have a tagType, info and zero or more attributes associated with them. TagType must be given in the constructor and
+ * it is not possible to change it. You can get and set attributes with the functions attribute(), setAttribute() and hasAttribute().
+ *
+ * ScxmlTag has one parent-tag and zero or more child. Tag doesn't take ownerships of the children.
+ * It is user responsibility to delete all tags. Normally this will be done via the ScxmlDocument inside the undo-commands.
+ */
+class ScxmlTag : public QObject
+{
+public:
+ ScxmlTag(TagType type, ScxmlDocument *document = nullptr);
+ ScxmlTag(const QString &prefix, const QString &name, ScxmlDocument *document = nullptr);
+ ScxmlTag(const ScxmlTag *other, bool copyChildren = true);
+
+ ~ScxmlTag() override;
+
+ void setDocument(ScxmlDocument *document);
+ ScxmlDocument *document() const;
+
+ // Functions for handling tagType/tagName
+ TagType tagType() const;
+ QString prefix() const;
+ QString tagName(bool addPrefix = true) const;
+ void setTagName(const QString &name);
+ const scxmltag_type_t *info() const;
+ QString displayName() const;
+ QString stateNameSpace() const;
+
+ // Get/set content
+ QString content() const;
+ void setContent(const QString &content);
+
+ // Handling editorInfo
+ void setEditorInfo(const QString &key, const QString &value);
+ QString editorInfo(const QString &key) const;
+ bool hasEditorInfo(const QString &key) const;
+
+ // Functions for handling attributes
+ int attributeCount() const;
+ QStringList attributeNames() const;
+ QStringList attributeValues() const;
+ void setAttributeName(int ind, const QString &name);
+ void setAttribute(int ind, const QString &name);
+ QString attributeName(int ind) const;
+ QString attribute(const QString &attribute, bool useNameSpace = false, const QString &defaultValue = QString()) const;
+ QString attribute(int ind, const QString &defaultValue = QString()) const;
+ void setAttribute(const QString &attribute, const QString &value);
+ bool hasAttribute(const QString &key) const;
+
+ // Functions for handling parent
+ void setParentTag(ScxmlTag *parentTag);
+ ScxmlTag *parentTag() const;
+ bool hasParentTag() const;
+
+ // Functions for handling child tags
+ bool hasData() const;
+ bool hasChild(TagType type) const;
+ bool hasChild(const QString &name) const;
+ void insertChild(int index, ScxmlTag *child);
+ void appendChild(ScxmlTag *child);
+ void removeChild(ScxmlTag *child);
+ void moveChild(int oldPos, int newPos);
+ int childCount() const;
+ QVector<ScxmlTag*> allChildren() const;
+ QVector<ScxmlTag*> children(const QString &name) const;
+ ScxmlTag *child(const QString &name) const;
+ ScxmlTag *child(int row) const;
+ int childIndex(const ScxmlTag *child) const;
+ int index() const;
+ bool isRootTag() const;
+
+ /**
+ * @brief writeXml - write tag's content with the QXMLStreamWriter. Call writeXml-function for all children too.
+ * @param xml
+ */
+ void writeXml(QXmlStreamWriter &xml);
+
+ /**
+ * @brief readXml - read tag's information from the QXMLStreamReader and create child tags if necessary.
+ * @param xml
+ */
+ void readXml(QXmlStreamReader &xml, bool checkCopyId = false);
+
+ void finalizeTagNames();
+ void print();
+
+private:
+ void init(TagType type);
+ void initId();
+
+ const scxmltag_type_t *m_info = nullptr;
+ QStringList m_attributeNames;
+ QStringList m_attributeValues;
+ QPointer<ScxmlTag> m_parentTag;
+ QVector<ScxmlTag*> m_childTags;
+ QPointer<ScxmlDocument> m_document;
+ TagType m_tagType = UnknownTag;
+ QString m_tagName;
+ QString m_content;
+ QString m_prefix;
+ QHash<QString, QString> m_editorInfo;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmltagutils.cpp b/src/plugins/scxmleditor/plugin_interface/scxmltagutils.cpp
new file mode 100644
index 00000000000..40189e27ec2
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmltagutils.cpp
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmltagutils.h"
+#include "scxmldocument.h"
+#include "scxmleditorconstants.h"
+#include "serializer.h"
+
+#include <utils/qtcassert.h>
+
+#include <QAction>
+#include <QPointF>
+#include <QRectF>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+namespace TagUtils {
+
+bool checkPaste(const QString &copiedTagTypes, const ScxmlTag *currentTag)
+{
+ if (copiedTagTypes.isEmpty() || currentTag == nullptr)
+ return false;
+
+ QVector<TagType> tagTypes;
+ for (int i = 0; i < Finalize; ++i) {
+ if (copiedTagTypes.contains(QLatin1String(scxml_tags[i].name)))
+ tagTypes << TagType(i);
+ }
+
+ tagTypes.removeAll(InitialTransition);
+
+ if (tagTypes.isEmpty())
+ return false;
+
+ QVector<TagType> childTags = allowedChildTypes(currentTag->tagType());
+ foreach (const TagType &type, tagTypes) {
+ if (!childTags.contains(type))
+ return false;
+ }
+
+ return true;
+}
+
+void createChildMenu(const ScxmlTag *tag, QMenu *menu, bool addRemove)
+{
+ QTC_ASSERT(tag, return);
+
+ initChildMenu(tag->tagType(), menu);
+
+ QVariantMap data;
+ data[Constants::C_SCXMLTAG_PARENTTAG] = tag->tagType();
+ data[Constants::C_SCXMLTAG_ACTIONTYPE] = AddChild;
+
+ if (tag->tagType() == UnknownTag) {
+ data[Constants::C_SCXMLTAG_TAGTYPE] = UnknownTag;
+ menu->addAction(ScxmlTag::tr("New Tag"))->setData(data);
+ } else if (tag->tagType() == Metadata) {
+ data[Constants::C_SCXMLTAG_TAGTYPE] = MetadataItem;
+ menu->addAction(ScxmlTag::tr("Item"))->setData(data);
+ } else {
+ data[Constants::C_SCXMLTAG_PARENTTAG] = Metadata;
+ data[Constants::C_SCXMLTAG_TAGTYPE] = MetadataItem;
+ menu->addAction(ScxmlTag::tr("Metadata"))->setData(data);
+ }
+
+ if (addRemove) {
+ menu->addSeparator();
+ data[Constants::C_SCXMLTAG_ACTIONTYPE] = Remove;
+ QAction *act = menu->addAction(ScxmlTag::tr("Remove"));
+ act->setData(data);
+ act->setEnabled(!tag->isRootTag());
+ }
+}
+
+QVector<TagType> allowedChildTypes(TagType tagType)
+{
+ QVector<TagType> childTags;
+
+ switch (tagType) {
+ case Scxml:
+ childTags << DataModel;
+ childTags << Script;
+ childTags << Initial;
+ childTags << State;
+ childTags << Parallel;
+ childTags << Final;
+ break;
+ case State:
+ childTags << Initial;
+ childTags << Final;
+ case Parallel:
+ childTags << OnEntry;
+ childTags << OnExit;
+ childTags << Transition;
+ childTags << DataModel;
+ childTags << Invoke;
+ childTags << State;
+ childTags << Parallel;
+ childTags << History;
+ break;
+ case Initial:
+ case History:
+ childTags << Transition;
+ break;
+ case Final:
+ childTags << OnEntry;
+ childTags << OnExit;
+ childTags << Donedata;
+ break;
+ case If:
+ childTags << ElseIf;
+ childTags << Else;
+ case Transition:
+ case OnEntry:
+ case OnExit:
+ case ElseIf:
+ case Else:
+ case Foreach:
+ // Executable content
+ childTags << Raise;
+ childTags << Send;
+ childTags << Script;
+ childTags << Assign;
+ childTags << Cancel;
+ childTags << Log;
+ childTags << If;
+ childTags << Foreach;
+ break;
+ case DataModel:
+ childTags << Data;
+ break;
+ case Data:
+ // PENDING
+ break;
+ case Assign:
+ // PENDING
+ break;
+ case Content:
+ // PENDING
+ break;
+ case Script:
+ // PENDING
+ break;
+ case Invoke:
+ childTags << Finalize;
+ case Donedata:
+ case Send:
+ childTags << Param;
+ childTags << Content;
+ break;
+ default:
+ break;
+ }
+
+ return childTags;
+}
+
+QVector<TagType> childTypes(TagType tagType)
+{
+ QVector<TagType> childTags;
+
+ switch (tagType) {
+ case Scxml:
+ childTags << DataModel;
+ childTags << Script;
+ break;
+ case State:
+ case Parallel:
+ childTags << OnEntry;
+ childTags << OnExit;
+ childTags << Transition;
+ childTags << DataModel;
+ childTags << Invoke;
+ break;
+ case Initial:
+ case History:
+ //childTags << Transition;
+ break;
+ case Final:
+ childTags << OnEntry;
+ childTags << OnExit;
+ childTags << Donedata;
+ break;
+ case If:
+ childTags << ElseIf;
+ childTags << Else;
+ case Transition:
+ case OnEntry:
+ case OnExit:
+ case ElseIf:
+ case Else:
+ case Foreach:
+ // Executable content
+ childTags << Raise;
+ childTags << Send;
+ childTags << Script;
+ childTags << Assign;
+ childTags << Cancel;
+ childTags << Log;
+ childTags << If;
+ childTags << Foreach;
+ break;
+ case DataModel:
+ childTags << Data;
+ break;
+ case Data:
+ // PENDING
+ break;
+ case Assign:
+ // PENDING
+ break;
+ case Content:
+ // PENDING
+ break;
+ case Script:
+ // PENDING
+ break;
+ case Invoke:
+ childTags << Finalize;
+ case Donedata:
+ case Send:
+ childTags << Param;
+ childTags << Content;
+ break;
+ default:
+ break;
+ }
+
+ return childTags;
+}
+
+void initChildMenu(TagType tagType, QMenu *menu)
+{
+ menu->setTitle(QLatin1String(scxml_tags[tagType].name));
+
+ QVector<TagType> childTags = childTypes(tagType);
+
+ if (childTags.count() > 0) {
+ for (int i = 0; i < childTags.count(); ++i) {
+ if (childTags[i] == OnEntry || childTags[i] == OnExit)
+ initChildMenu(childTags[i], menu->addMenu(QLatin1String(scxml_tags[childTags[i]].name)));
+ else {
+ QVariantMap data;
+ data[Constants::C_SCXMLTAG_PARENTTAG] = tagType;
+ data[Constants::C_SCXMLTAG_TAGTYPE] = childTags[i];
+ data[Constants::C_SCXMLTAG_ACTIONTYPE] = AddChild;
+ menu->addAction(QLatin1String(scxml_tags[childTags[i]].name))->setData(data);
+ }
+ }
+ }
+}
+
+ScxmlTag *metadataTag(ScxmlTag *tag, const QString &key, bool blockUpdates)
+{
+ QTC_ASSERT(tag, return nullptr);
+
+ ScxmlTag *info = nullptr;
+
+ ScxmlDocument *document = tag->document();
+ if (document) {
+ ScxmlTag *metaData = tag->child("qt:metadata");
+ if (!metaData) {
+ metaData = new ScxmlTag(Metadata, document);
+ if (!blockUpdates)
+ document->addTag(tag, metaData);
+ else
+ tag->appendChild(metaData);
+ }
+
+ info = metaData->child(QString::fromLatin1("qt:%1").arg(key));
+ if (!info) {
+ info = new ScxmlTag(Metadata, document);
+ info->setTagName(key);
+ if (!blockUpdates)
+ document->addTag(metaData, info);
+ else
+ metaData->appendChild(info);
+ }
+ }
+
+ return info;
+}
+
+ScxmlTag *findChild(const ScxmlTag *tag, TagType childType)
+{
+ QTC_ASSERT(tag, return nullptr);
+
+ for (int i = 0; i < tag->childCount(); ++i) {
+ if (tag->child(i)->tagType() == childType)
+ return tag->child(i);
+ }
+
+ return nullptr;
+}
+
+void findAllChildren(const ScxmlTag *tag, QVector<ScxmlTag*> &children)
+{
+ QTC_ASSERT(tag, return);
+
+ for (int i = 0; i < tag->childCount(); ++i) {
+ ScxmlTag *child = tag->child(i);
+ children << child;
+ findAllChildren(child, children);
+ }
+}
+
+void findAllTransitionChildren(const ScxmlTag *tag, QVector<ScxmlTag*> &children)
+{
+ QTC_ASSERT(tag, return);
+
+ for (int i = 0; i < tag->childCount(); ++i) {
+ ScxmlTag *child = tag->child(i);
+ TagType t = child->tagType();
+ if (t == Transition || t == InitialTransition)
+ children << child;
+ else
+ findAllTransitionChildren(child, children);
+ }
+}
+
+void modifyPosition(ScxmlTag *tag, const QPointF &minPos, const QPointF &targetPos)
+{
+ QTC_ASSERT(tag, return);
+
+ const QString sceneData = tag->editorInfo(Constants::C_SCXML_EDITORINFO_SCENEGEOMETRY);
+ const QString localData = tag->editorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY);
+
+ Serializer s;
+ if (!localData.isEmpty() && !sceneData.isEmpty()) {
+ QPointF localPos, scenePos;
+ QRectF localRect, sceneRect;
+
+ s.setData(sceneData);
+ s.read(scenePos);
+ s.read(sceneRect);
+
+ s.clear();
+ s.setData(localData);
+ s.read(localPos);
+ s.read(localRect);
+
+ localPos = targetPos - localRect.topLeft() - (minPos - sceneRect.topLeft());
+
+ s.clear();
+ s.append(localPos);
+ s.append(localRect);
+ tag->document()->setEditorInfo(tag, Constants::C_SCXML_EDITORINFO_GEOMETRY, s.data());
+ } else {
+ s.append(targetPos);
+ if (tag->tagType() == State || tag->tagType() == Parallel)
+ s.append(QRectF(-60, -50, 120, 100));
+ else if (tag->tagType() == Initial || tag->tagType() == Final || tag->tagType() == History)
+ s.append(QRectF(-20, -20, 40, 40));
+ else
+ s.append(QRectF());
+ tag->document()->setEditorInfo(tag, Constants::C_SCXML_EDITORINFO_GEOMETRY, s.data());
+ }
+}
+
+} // namespace TagUtils
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmltagutils.h b/src/plugins/scxmleditor/plugin_interface/scxmltagutils.h
new file mode 100644
index 00000000000..176224f14e5
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmltagutils.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmltag.h"
+#include <QMenu>
+#include <QVector>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+namespace TagUtils {
+
+enum MenuAction {
+ AddChild = 0,
+ SetAsInitial,
+ Relayout,
+ ZoomToState,
+ RemovePoint,
+ Remove
+};
+
+bool checkPaste(const QString &copiedTagType, const ScxmlTag *currentTag);
+QVector<TagType> allowedChildTypes(TagType tagType);
+QVector<TagType> childTypes(TagType type);
+void createChildMenu(const ScxmlTag *tag, QMenu *menu, bool addRemove = true);
+void initChildMenu(TagType tagType, QMenu *menu);
+ScxmlTag *findChild(const ScxmlTag *tag, TagType childType);
+ScxmlTag *metadataTag(ScxmlTag *tag, const QString &tagname, bool blockUpdates = false);
+void findAllChildren(const ScxmlTag *tag, QVector<ScxmlTag*> &children);
+void findAllTransitionChildren(const ScxmlTag *tag, QVector<ScxmlTag*> &children);
+void modifyPosition(ScxmlTag *tag, const QPointF &minPos, const QPointF &targetPos);
+
+} // namespace TagUtils
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmltypes.h b/src/plugins/scxmleditor/plugin_interface/scxmltypes.h
new file mode 100644
index 00000000000..e4179198aae
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmltypes.h
@@ -0,0 +1,449 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QVariant>
+#include <QVector>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+enum TagType {
+ UnknownTag = 0,
+ Metadata,
+ MetadataItem,
+ Scxml,
+ State,
+ Parallel,
+ Transition,
+ InitialTransition,
+ Initial,
+ Final,
+ OnEntry,
+ OnExit,
+ History,
+ Raise,
+ If,
+ ElseIf,
+ Else,
+ Foreach,
+ Log,
+ DataModel,
+ Data,
+ Assign,
+ Donedata,
+ Content,
+ Param,
+ Script,
+ Send,
+ Cancel,
+ Invoke,
+ Finalize
+};
+
+struct scxmltag_attribute_t;
+struct scxmltag_type_t;
+
+struct scxmltag_attribute_t
+{
+ const char *name; // scxml attribute name
+ const char *value; // default value
+ bool required;
+ bool editable;
+ int datatype;
+};
+
+struct scxmltag_type_t
+{
+ const char *name; // scxml output name
+ bool canIncludeContent;
+ const scxmltag_attribute_t *attributes;
+ int n_attributes;
+};
+
+// Define tag-attributes
+const scxmltag_attribute_t scxml_scxml_attributes[] = {
+ { "initial", nullptr, false, false, QVariant::String },
+ { "name", nullptr, false, true, QVariant::String },
+ { "xmlns", "http://www.w3.org/2005/07/scxml", true, false, QVariant::String },
+ { "version", "1.0", true, false, QVariant::String },
+ { "datamodel", nullptr, false, true, QVariant::String },
+ { "binding", "early;late", false, true, QVariant::StringList }
+};
+
+const scxmltag_attribute_t scxml_state_attributes[] = {
+ { "id", nullptr, false, true, QVariant::String },
+ { "initial", nullptr, false, false, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_parallel_attributes[] = {
+ { "id", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_transition_attributes[] = {
+ { "event", nullptr, false, true, QVariant::String },
+ { "cond", nullptr, false, true, QVariant::String },
+ { "target", nullptr, false, true, QVariant::String },
+ { "type", "internal;external", false, true, QVariant::StringList }
+};
+
+const scxmltag_attribute_t scxml_initialtransition_attributes[] = {
+ { "target", nullptr, false, false, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_final_attributes[] = {
+ { "id", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_history_attributes[] = {
+ { "id", nullptr, false, true, QVariant::String },
+ { "type", "shallow;deep", false, true, QVariant::StringList }
+};
+
+const scxmltag_attribute_t scxml_raise_attributes[] = {
+ { "event", nullptr, true, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_if_attributes[] = {
+ { "cond", nullptr, true, true, QVariant::String },
+};
+
+const scxmltag_attribute_t scxml_elseif_attributes[] = {
+ { "cond", nullptr, true, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_foreach_attributes[] = {
+ { "array", nullptr, true, true, QVariant::String },
+ { "item", nullptr, true, true, QVariant::String },
+ { "index", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_log_attributes[] = {
+ { "label", "", false, true, QVariant::String },
+ { "expr", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_data_attributes[] = {
+ { "id", nullptr, true, true, QVariant::String },
+ { "src", nullptr, false, true, QVariant::String },
+ { "expr", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_assign_attributes[] = {
+ { "location", nullptr, true, true, QVariant::String },
+ { "expr", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_content_attributes[] = {
+ { "expr", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_param_attributes[] = {
+ { "name", nullptr, true, true, QVariant::String },
+ { "expr", nullptr, false, true, QVariant::String },
+ { "location", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_script_attributes[] = {
+ { "src", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_send_attributes[] = {
+ { "event", nullptr, false, true, QVariant::String },
+ { "eventexpr", nullptr, false, true, QVariant::String },
+ { "target", nullptr, false, true, QVariant::String },
+ { "targetexpr", nullptr, false, true, QVariant::String },
+ { "type", nullptr, false, true, QVariant::String },
+ { "typeexpr", nullptr, false, true, QVariant::String },
+ { "id", nullptr, false, true, QVariant::String },
+ { "idiocation", nullptr, false, true, QVariant::String },
+ { "delay", nullptr, false, true, QVariant::String },
+ { "delayexpr", nullptr, false, true, QVariant::String },
+ { "namelist", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_cancel_attributes[] = {
+ { "sendid", nullptr, false, true, QVariant::String },
+ { "sendidexpr", nullptr, false, true, QVariant::String }
+};
+
+const scxmltag_attribute_t scxml_invoke_attributes[] = {
+ { "type", nullptr, false, true, QVariant::String },
+ { "typeexpr", nullptr, false, true, QVariant::String },
+ { "src", nullptr, false, true, QVariant::String },
+ { "srcexpr", nullptr, false, true, QVariant::String },
+ { "id", nullptr, false, true, QVariant::String },
+ { "idiocation", nullptr, false, true, QVariant::String },
+ { "namelist", nullptr, false, true, QVariant::String },
+ { "autoforward", ";true;false", false, true, QVariant::StringList }
+};
+
+const scxmltag_type_t scxml_unknown = {
+ "unknown",
+ true,
+ nullptr,
+ 0
+};
+
+const scxmltag_type_t scxml_metadata = {
+ "metadata",
+ true,
+ nullptr,
+ 0
+};
+
+const scxmltag_type_t scxml_metadataitem = {
+ "item",
+ true,
+ nullptr,
+ 0
+};
+
+const scxmltag_type_t scxml_scxml = {
+ "scxml",
+ false,
+ scxml_scxml_attributes,
+ sizeof(scxml_scxml_attributes) / sizeof(scxml_scxml_attributes[0])
+};
+
+const scxmltag_type_t scxml_state = {
+ "state",
+ false,
+ scxml_state_attributes,
+ sizeof(scxml_state_attributes) / sizeof(scxml_state_attributes[0])
+};
+
+const scxmltag_type_t scxml_parallel = {
+ "parallel",
+ false,
+ scxml_parallel_attributes,
+ sizeof(scxml_parallel_attributes) / sizeof(scxml_parallel_attributes[0])
+};
+
+const scxmltag_type_t scxml_transition = {
+ "transition",
+ false,
+ scxml_transition_attributes,
+ sizeof(scxml_transition_attributes) / sizeof(scxml_transition_attributes[0])
+};
+
+const scxmltag_type_t scxml_initialtransition = {
+ "transition",
+ false,
+ scxml_initialtransition_attributes,
+ sizeof(scxml_initialtransition_attributes) / sizeof(scxml_initialtransition_attributes[0])
+};
+
+const scxmltag_type_t scxml_initial = {
+ "initial",
+ false,
+ nullptr,
+ 0
+};
+
+const scxmltag_type_t scxml_final = {
+ "final",
+ false,
+ scxml_final_attributes,
+ sizeof(scxml_final_attributes) / sizeof(scxml_final_attributes[0])
+};
+
+const scxmltag_type_t scxml_onentry = {
+ "onentry",
+ false,
+ nullptr,
+ 0
+};
+
+const scxmltag_type_t scxml_onexit = {
+ "onexit",
+ false,
+ nullptr,
+ 0
+};
+
+const scxmltag_type_t scxml_history = {
+ "history",
+ false,
+ scxml_history_attributes,
+ sizeof(scxml_history_attributes) / sizeof(scxml_history_attributes[0])
+};
+
+const scxmltag_type_t scxml_raise = {
+ "raise",
+ false,
+ scxml_raise_attributes,
+ sizeof(scxml_raise_attributes) / sizeof(scxml_raise_attributes[0])
+};
+
+const scxmltag_type_t scxml_if = {
+ "if",
+ false,
+ scxml_if_attributes,
+ sizeof(scxml_if_attributes) / sizeof(scxml_if_attributes[0])
+};
+
+const scxmltag_type_t scxml_elseif = {
+ "elseif",
+ false,
+ scxml_elseif_attributes,
+ sizeof(scxml_elseif_attributes) / sizeof(scxml_elseif_attributes[0])
+};
+
+const scxmltag_type_t scxml_else = {
+ "else",
+ false,
+ nullptr,
+ 0
+};
+
+const scxmltag_type_t scxml_foreach = {
+ "foreach",
+ false,
+ scxml_foreach_attributes,
+ sizeof(scxml_foreach_attributes) / sizeof(scxml_foreach_attributes[0])
+};
+
+const scxmltag_type_t scxml_log = {
+ "log",
+ false,
+ scxml_log_attributes,
+ sizeof(scxml_log_attributes) / sizeof(scxml_log_attributes[0])
+};
+
+const scxmltag_type_t scxml_datamodel = {
+ "datamodel",
+ false,
+ nullptr,
+ 0
+};
+
+const scxmltag_type_t scxml_data = {
+ "data",
+ false,
+ scxml_data_attributes,
+ sizeof(scxml_data_attributes) / sizeof(scxml_data_attributes[0])
+};
+
+const scxmltag_type_t scxml_assign = {
+ "assign",
+ false,
+ scxml_assign_attributes,
+ sizeof(scxml_assign_attributes) / sizeof(scxml_assign_attributes[0])
+};
+
+const scxmltag_type_t scxml_donedata = {
+ "donedata",
+ false,
+ nullptr,
+ 0
+};
+
+const scxmltag_type_t scxml_content = {
+ "content",
+ false,
+ scxml_content_attributes,
+ sizeof(scxml_content_attributes) / sizeof(scxml_content_attributes[0])
+};
+
+const scxmltag_type_t scxml_param = {
+ "param",
+ false,
+ scxml_param_attributes,
+ sizeof(scxml_param_attributes) / sizeof(scxml_param_attributes[0])
+};
+
+const scxmltag_type_t scxml_script = {
+ "script",
+ true,
+ scxml_script_attributes,
+ sizeof(scxml_script_attributes) / sizeof(scxml_script_attributes[0])
+};
+
+const scxmltag_type_t scxml_send = {
+ "send",
+ false,
+ scxml_send_attributes,
+ sizeof(scxml_send_attributes) / sizeof(scxml_send_attributes[0])
+};
+
+const scxmltag_type_t scxml_cancel = {
+ "cancel",
+ false,
+ scxml_cancel_attributes,
+ sizeof(scxml_cancel_attributes) / sizeof(scxml_cancel_attributes[0])
+};
+
+const scxmltag_type_t scxml_invoke = {
+ "invoke",
+ false,
+ scxml_invoke_attributes,
+ sizeof(scxml_invoke_attributes) / sizeof(scxml_invoke_attributes[0])
+};
+
+const scxmltag_type_t scxml_finalize = {
+ "finalize",
+ false,
+ nullptr,
+ 0
+};
+
+const scxmltag_type_t scxml_tags[] = {
+ scxml_unknown,
+ scxml_metadata,
+ scxml_metadataitem,
+ scxml_scxml,
+ scxml_state,
+ scxml_parallel,
+ scxml_transition,
+ scxml_initialtransition,
+ scxml_initial,
+ scxml_final,
+ scxml_onentry,
+ scxml_onexit,
+ scxml_history,
+ scxml_raise,
+ scxml_if,
+ scxml_elseif,
+ scxml_else,
+ scxml_foreach,
+ scxml_log,
+ scxml_datamodel,
+ scxml_data,
+ scxml_assign,
+ scxml_donedata,
+ scxml_content,
+ scxml_param,
+ scxml_script,
+ scxml_send,
+ scxml_cancel,
+ scxml_invoke,
+ scxml_finalize
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmluifactory.cpp b/src/plugins/scxmleditor/plugin_interface/scxmluifactory.cpp
new file mode 100644
index 00000000000..dd5aa43190c
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmluifactory.cpp
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmluifactory.h"
+#include "genericscxmlplugin.h"
+#include "isceditor.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QDir>
+#include <QGenericPlugin>
+#include <QGenericPluginFactory>
+#include <QPluginLoader>
+
+using namespace ScxmlEditor::PluginInterface;
+
+ScxmlUiFactory::ScxmlUiFactory(QObject *parent)
+ : QObject(parent)
+{
+ initPlugins();
+}
+
+ScxmlUiFactory::~ScxmlUiFactory()
+{
+ for (int i = m_plugins.count(); i--;)
+ m_plugins[i]->detach();
+}
+
+void ScxmlUiFactory::documentChanged(DocumentChangeType type, ScxmlDocument *doc)
+{
+ for (int i = 0; i < m_plugins.count(); ++i)
+ m_plugins[i]->documentChanged(type, doc);
+}
+
+void ScxmlUiFactory::refresh()
+{
+ for (int i = 0; i < m_plugins.count(); ++i)
+ m_plugins[i]->refresh();
+}
+
+QObject *ScxmlUiFactory::object(const QString &type) const
+{
+ return m_objects.value(type, 0);
+}
+
+void ScxmlUiFactory::unregisterObject(const QString &type, QObject *obj)
+{
+ if (obj && m_objects[type] == obj)
+ m_objects.take(type);
+}
+
+void ScxmlUiFactory::registerObject(const QString &type, QObject *obj)
+{
+ if (obj)
+ m_objects[type] = obj;
+}
+
+bool ScxmlUiFactory::isActive(const QString &type, const QObject *obj) const
+{
+ return obj && m_objects.value(type, 0) == obj;
+}
+
+void ScxmlUiFactory::initPlugins()
+{
+ // First init general plugin
+ m_plugins << new GenericScxmlPlugin;
+
+ // Get additional plugins
+ QDir pluginDir(QCoreApplication::applicationDirPath() + QDir::separator() + "SCEPlugins");
+
+ QStringList nameFilters;
+ nameFilters << "*.dll" << "*.so";
+
+ foreach (QFileInfo dllFileInfo, pluginDir.entryInfoList(nameFilters)) {
+
+ QPluginLoader loader(dllFileInfo.absoluteFilePath());
+ loader.load();
+
+ if (!loader.isLoaded())
+ break;
+
+ auto plugin = qobject_cast<QGenericPlugin*>(loader.instance());
+
+ if (!plugin)
+ break;
+
+ QObject *instance = plugin->create(QString(), QString());
+
+ if (instance) {
+ auto scEditorInstance = qobject_cast<ISCEditor*>(instance);
+ if (scEditorInstance) {
+ qDebug() << tr("Created editor-instance.");
+ m_plugins << scEditorInstance;
+ } else {
+ qWarning() << tr("Editor-instance is not of the type ISCEditor.");
+ loader.unload();
+ }
+ }
+ }
+
+ // Last init plugins
+ for (int i = 0; i < m_plugins.count(); ++i)
+ m_plugins[i]->init(this);
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/scxmluifactory.h b/src/plugins/scxmleditor/plugin_interface/scxmluifactory.h
new file mode 100644
index 00000000000..6bb9c5d6554
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/scxmluifactory.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "mytypes.h"
+
+#include <QObject>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ISCEditor;
+class ScxmlDocument;
+
+class ScxmlUiFactory : public QObject
+{
+ Q_OBJECT
+
+public:
+ ScxmlUiFactory(QObject *parent = nullptr);
+ ~ScxmlUiFactory() override;
+
+ // Inform document changes
+ void documentChanged(DocumentChangeType type, ScxmlDocument *doc);
+
+ // Inform plugins to refresh
+ void refresh();
+
+ // Get registered objects
+ QObject *object(const QString &type) const;
+
+ // Register objects
+ void unregisterObject(const QString &type, QObject *object);
+ void registerObject(const QString &type, QObject *object);
+
+ // Check is active
+ bool isActive(const QString &type, const QObject *object) const;
+
+private:
+ void initPlugins();
+
+ QVector<ISCEditor*> m_plugins;
+ QMap<QString, QObject*> m_objects;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/serializer.cpp b/src/plugins/scxmleditor/plugin_interface/serializer.cpp
new file mode 100644
index 00000000000..93cd3f15710
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/serializer.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "serializer.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+Serializer::Serializer()
+ : m_separator(";")
+{
+}
+
+void Serializer::seek(int pos)
+{
+ m_index = qBound(0, pos, m_data.count() - 1);
+}
+
+void Serializer::clear()
+{
+ m_data.clear();
+ m_index = 0;
+}
+
+void Serializer::append(double d)
+{
+ m_data.append(QString::fromLatin1("%1").arg(d, 0, 'f', 2).remove(".00"));
+ m_index = m_data.count() - 1;
+}
+
+QString Serializer::data() const
+{
+ return m_data.join(m_separator);
+}
+
+void Serializer::append(const QPolygonF &d)
+{
+ appendPolygon(d);
+}
+void Serializer::append(const QPolygon &d)
+{
+ appendPolygon(d);
+}
+void Serializer::append(const QRectF &d)
+{
+ appendRect(d);
+}
+void Serializer::append(const QRect &d)
+{
+ appendRect(d);
+}
+void Serializer::append(const QPointF &d)
+{
+ appendPoint(d);
+}
+void Serializer::append(const QPoint &d)
+{
+ appendPoint(d);
+}
+
+void Serializer::read(QPolygonF &d)
+{
+ readPolygon<QPointF>(d);
+}
+void Serializer::read(QPolygon &d)
+{
+ readPolygon<QPoint>(d);
+}
+void Serializer::read(QRectF &d)
+{
+ readRect(d);
+}
+void Serializer::read(QRect &d)
+{
+ readRect(d);
+}
+void Serializer::read(QPointF &d)
+{
+ readPoint(d);
+}
+void Serializer::read(QPoint &d)
+{
+ readPoint(d);
+}
+
+void Serializer::setData(const QString &d)
+{
+ m_data = d.split(m_separator, QString::SkipEmptyParts);
+ m_index = 0;
+}
+
+double Serializer::readNext()
+{
+ double data = 0.0;
+ if (m_index >= 0 && m_index < m_data.count())
+ data = m_data[m_index].toDouble();
+
+ m_index++;
+ return data;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/serializer.h b/src/plugins/scxmleditor/plugin_interface/serializer.h
new file mode 100644
index 00000000000..1168b027f1c
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/serializer.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QPoint>
+#include <QPointF>
+#include <QPolygon>
+#include <QPolygonF>
+#include <QRect>
+#include <QRectF>
+#include <QStringList>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+/**
+ * @brief The Serializer class provides serialization of item-data to the string.
+ *
+ * For example items inside the graphicsview uses this class when they need to save ui-properties to the scxmltag-attribute.
+ */
+class Serializer
+{
+public:
+ Serializer();
+
+ void seek(int pos);
+ void clear();
+
+ void append(const QPolygonF &d);
+ void append(const QPolygon &d);
+ void append(const QRectF &d);
+ void append(const QRect &d);
+ void append(const QPointF &d);
+ void append(const QPoint &d);
+
+ void read(QPolygonF &d);
+ void read(QPolygon &d);
+ void read(QRectF &d);
+ void read(QRect &d);
+ void read(QPointF &d);
+ void read(QPoint &d);
+
+ void setSeparator(const QString &sep);
+ void setData(const QString &d);
+ QString data() const;
+
+private:
+ void append(double d);
+ double readNext();
+
+ int m_index = 0;
+ const QString m_separator;
+ QStringList m_data;
+
+ template <class T, class P>
+ void readPolygon(P &d)
+ {
+ int count = (m_data.count() - m_index) / 2;
+ for (int i = 0; i < count; ++i) {
+ T p;
+ read(p);
+ d << p;
+ }
+ }
+
+ template <class T>
+ void readRect(T &d)
+ {
+ d.setLeft(readNext());
+ d.setTop(readNext());
+ d.setWidth(readNext());
+ d.setHeight(readNext());
+ }
+
+ template <class T>
+ void readPoint(T &d)
+ {
+ d.setX(readNext());
+ d.setY(readNext());
+ }
+
+ template <class T>
+ void appendPolygon(const T &d)
+ {
+ for (int i = 0; i < d.count(); ++i) {
+ append(d[i].x());
+ append(d[i].y());
+ }
+ }
+
+ template <class T>
+ void appendPoint(const T &d)
+ {
+ append(d.x());
+ append(d.y());
+ }
+
+ template <class T>
+ void appendRect(const T &d)
+ {
+ append(d.left());
+ append(d.top());
+ append(d.width());
+ append(d.height());
+ }
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/shapeprovider.cpp b/src/plugins/scxmleditor/plugin_interface/shapeprovider.cpp
new file mode 100644
index 00000000000..ef07d22ed5c
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/shapeprovider.cpp
@@ -0,0 +1,33 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "shapeprovider.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+ShapeProvider::ShapeProvider(QObject *parent)
+ : QObject(parent)
+{
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/shapeprovider.h b/src/plugins/scxmleditor/plugin_interface/shapeprovider.h
new file mode 100644
index 00000000000..f7fa1fd569d
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/shapeprovider.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmltag.h"
+#include <QByteArray>
+#include <QIcon>
+#include <QObject>
+#include <QString>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ShapeProvider : public QObject
+{
+ Q_OBJECT
+
+public:
+ struct Shape
+ {
+ QString title;
+ QIcon icon;
+ QStringList filters;
+ QByteArray scxmlData;
+ QVariant userData;
+ };
+
+ struct ShapeGroup
+ {
+ ~ShapeGroup()
+ {
+ qDeleteAll(shapes);
+ shapes.clear();
+ }
+
+ QString title;
+ QVector<Shape*> shapes;
+ void addShape(Shape *shape)
+ {
+ shapes << shape;
+ }
+ };
+
+ explicit ShapeProvider(QObject *parent = nullptr);
+
+ virtual int groupCount() const = 0;
+ virtual QString groupTitle(int groupIndex) const = 0;
+
+ virtual int shapeCount(int groupIndex) const = 0;
+ virtual QString shapeTitle(int groupIndex, int shapeIndex) const = 0;
+ virtual QIcon shapeIcon(int groupIndex, int shapeIndex) const = 0;
+
+ virtual bool canDrop(int groupIndex, int shapeIndex, ScxmlTag *parent) const = 0;
+ virtual QByteArray scxmlCode(int groupIndex, int shapeIndex, ScxmlTag *parent) const = 0;
+
+signals:
+ void changed();
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/snapline.cpp b/src/plugins/scxmleditor/plugin_interface/snapline.cpp
new file mode 100644
index 00000000000..87d948073c0
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/snapline.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "snapline.h"
+#include <QPen>
+
+using namespace ScxmlEditor::PluginInterface;
+
+SnapLine::SnapLine(QGraphicsItem *parent)
+ : QGraphicsLineItem(parent)
+{
+ QPen pen;
+ pen.setBrush(QColor(0x22, 0xcc, 0x22));
+ pen.setStyle(Qt::DashLine);
+ setPen(pen);
+ setZValue(502);
+
+ m_visibilityTimer.setInterval(1000);
+ m_visibilityTimer.setSingleShot(true);
+ QObject::connect(&m_visibilityTimer, &QTimer::timeout, this, &SnapLine::hideLine);
+
+ hideLine();
+}
+
+void SnapLine::show(qreal x1, qreal y1, qreal x2, qreal y2)
+{
+ setLine(x1, y1, x2, y2);
+ setVisible(true);
+ m_visibilityTimer.start();
+}
+
+void SnapLine::hideLine()
+{
+ setVisible(false);
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/snapline.h b/src/plugins/scxmleditor/plugin_interface/snapline.h
new file mode 100644
index 00000000000..f2857681e8c
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/snapline.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QGraphicsLineItem>
+#include <QTimer>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class SnapLine : public QObject, public QGraphicsLineItem
+{
+ Q_OBJECT
+
+public:
+ SnapLine(QGraphicsItem *parent = nullptr);
+
+ void show(qreal x1, qreal y1, qreal x2, qreal y2);
+ void hideLine();
+
+private:
+ QTimer m_visibilityTimer;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/stateitem.cpp b/src/plugins/scxmleditor/plugin_interface/stateitem.cpp
new file mode 100644
index 00000000000..3f88a25ec02
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/stateitem.cpp
@@ -0,0 +1,581 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "stateitem.h"
+#include "finalstateitem.h"
+#include "graphicsitemprovider.h"
+#include "graphicsscene.h"
+#include "idwarningitem.h"
+#include "imageprovider.h"
+#include "initialstateitem.h"
+#include "parallelitem.h"
+#include "sceneutils.h"
+#include "scxmleditorconstants.h"
+#include "scxmltagutils.h"
+#include "scxmluifactory.h"
+#include "statewarningitem.h"
+#include "textitem.h"
+#include "transitionitem.h"
+#include "utilsprovider.h"
+
+#include <QBrush>
+#include <QCoreApplication>
+#include <QDebug>
+#include <QPainter>
+#include <QTextOption>
+#include <QUndoStack>
+#include <QtMath>
+
+using namespace ScxmlEditor::PluginInterface;
+
+const qreal TEXT_ITEM_HEIGHT = 20;
+const qreal TEXT_ITEM_CAP = 10;
+const qreal STATE_RADIUS = 10;
+
+StateItem::StateItem(const QPointF &pos, BaseItem *parent)
+ : ConnectableItem(pos, parent)
+{
+ m_stateNameItem = new TextItem(this);
+ m_stateNameItem->setParentItem(this);
+
+ checkWarningItems();
+
+ connect(m_stateNameItem, &TextItem::selected, this, [=](bool sel){
+ setItemSelected(sel);
+ });
+ connect(m_stateNameItem, &TextItem::textChanged, this, &StateItem::updateTextPositions);
+ connect(m_stateNameItem, &TextItem::textReady, this, &StateItem::titleHasChanged);
+
+ m_pen = QPen(QColor(0x45, 0x45, 0x45));
+
+ updateColors();
+ updatePolygon();
+}
+
+void StateItem::checkWarningItems()
+{
+ ScxmlUiFactory *uifactory = uiFactory();
+ if (uifactory) {
+ auto provider = static_cast<GraphicsItemProvider*>(uifactory->object("graphicsItemProvider"));
+ if (provider) {
+ if (!m_idWarningItem)
+ m_idWarningItem = static_cast<IdWarningItem*>(provider->createWarningItem(Constants::C_STATE_WARNING_ID, this));
+ if (!m_stateWarningItem)
+ m_stateWarningItem = static_cast<StateWarningItem*>(provider->createWarningItem(Constants::C_STATE_WARNING_STATE, this));
+
+ if (m_idWarningItem && m_stateWarningItem)
+ m_stateWarningItem->setIdWarning(m_idWarningItem);
+
+ checkWarnings();
+
+ if (m_idWarningItem || m_stateWarningItem)
+ updateAttributes();
+ }
+ }
+}
+
+QVariant StateItem::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ QVariant retValue = ConnectableItem::itemChange(change, value);
+
+ switch (change) {
+ case QGraphicsItem::ItemSceneHasChanged:
+ checkWarningItems();
+ break;
+ default:
+ break;
+ }
+
+ return retValue;
+}
+
+void StateItem::updateAttributes()
+{
+ if (tag()) {
+ ConnectableItem::updateAttributes();
+
+ // Check initial attribute
+ QString strNewId = tagValue("id", true);
+ if (!m_parallelState) {
+ QStringList NSIDs = strNewId.split(tag()->document()->nameSpaceDelimiter(), QString::SkipEmptyParts);
+ if (NSIDs.count() > 0) {
+ NSIDs[NSIDs.count() - 1] = m_stateNameItem->toPlainText();
+ QString strOldId = NSIDs.join(tag()->document()->nameSpaceDelimiter());
+ ScxmlTag *parentTag = tag()->parentTag();
+ if (parentTag && !strOldId.isEmpty() && parentTag->attribute("initial") == strOldId)
+ parentTag->setAttribute("initial", strNewId);
+ }
+ }
+
+ m_stateNameItem->setText(tagValue("id"));
+ if (m_idWarningItem)
+ m_idWarningItem->setId(strNewId);
+ updateTextPositions();
+
+ if (m_parallelState)
+ checkInitial(true);
+ }
+
+ updateBoundingRect();
+}
+
+void StateItem::updateEditorInfo(bool allChildren)
+{
+ ConnectableItem::updateEditorInfo(allChildren);
+
+ QString color = editorInfo(Constants::C_SCXML_EDITORINFO_FONTCOLOR);
+ m_stateNameItem->setDefaultTextColor(color.isEmpty() ? QColor(Qt::black) : QColor(color));
+
+ // Update child too if necessary
+ if (allChildren) {
+ QList<QGraphicsItem*> children = childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ if (children[i]->type() >= InitialStateType) {
+ auto child = qgraphicsitem_cast<BaseItem*>(children[i]);
+ if (child)
+ child->updateEditorInfo(allChildren);
+ }
+ }
+ }
+}
+
+void StateItem::updateColors()
+{
+ updateDepth();
+ m_parallelState = parentItem() && parentItem()->type() == ParallelType;
+
+ if (m_parallelState)
+ m_pen.setStyle(Qt::DashLine);
+ else
+ m_pen.setStyle(Qt::SolidLine);
+
+ // Update child color-indices too
+ QList<QGraphicsItem*> children = childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ if (children[i]->type() >= StateType) {
+ auto child = qgraphicsitem_cast<StateItem*>(children[i]);
+ if (child)
+ child->updateColors();
+ }
+ }
+
+ update();
+}
+
+void StateItem::updateBoundingRect()
+{
+ QRectF r2 = childItemsBoundingRect();
+
+ // Check if we need to increase parent boundingrect
+ if (!r2.isNull()) {
+ QRectF r = boundingRect();
+ QRectF r3 = r.united(r2);
+
+ if (r != r3) {
+ setItemBoundingRect(r3);
+ updateTransitions();
+ updateUIProperties();
+ checkOverlapping();
+ }
+ }
+}
+
+void StateItem::shrink()
+{
+ QRectF trect;
+ foreach (TransitionItem *item, outputTransitions()) {
+ if (item->targetType() == TransitionItem::InternalSameTarget || item->targetType() == TransitionItem::InternalNoTarget) {
+ trect = trect.united(item->wholeBoundingRect());
+ }
+ }
+
+ QRectF r = boundingRect();
+ QRectF r2 = childItemsBoundingRect();
+ double minWidth = qMax(120.0, r2.width());
+ double minH = qMax((double)minHeight(), r2.height());
+ minWidth = qMax(minWidth, m_stateNameItem->boundingRect().width() + (double)WARNING_ITEM_SIZE + (double)TEXT_ITEM_CAP);
+
+ if (!m_backgroundImage.isNull()) {
+ minWidth = qMax(minWidth, (double)m_backgroundImage.width() + 3 * STATE_RADIUS + trect.width());
+ minH = qMax(minH, ((double)m_backgroundImage.height() + 3 * STATE_RADIUS + TEXT_ITEM_HEIGHT) / 0.94);
+ }
+
+ r2 = QRectF(0, 0, minWidth, minH);
+ r2.moveCenter(r.center());
+
+ // Check if we need to increase parent boundingrect
+ if (r != r2) {
+ setItemBoundingRect(r2);
+ updateTransitions();
+ updateUIProperties();
+ }
+}
+
+void StateItem::transitionsChanged()
+{
+ QRectF rr = boundingRect();
+ QRectF rectInternalTransitions;
+ QVector<TransitionItem*> internalTransitions = outputTransitions();
+ foreach (TransitionItem *item, internalTransitions) {
+ if (item->targetType() <= TransitionItem::InternalNoTarget) {
+ QRectF br = mapFromItem(item, item->boundingRect()).boundingRect();
+ br.setLeft(rr.left() + 20);
+ br.setTop(br.top() + 4);
+ br.setWidth(br.width() + item->textWidth());
+ rectInternalTransitions = rectInternalTransitions.united(br);
+ }
+ }
+
+ m_transitionRect = rectInternalTransitions;
+ updateBoundingRect();
+}
+
+void StateItem::transitionCountChanged()
+{
+ ConnectableItem::transitionsChanged();
+ checkWarnings();
+ transitionsChanged();
+}
+
+QRectF StateItem::childItemsBoundingRect() const
+{
+ QRectF r;
+ QRectF rr = boundingRect();
+
+ QList<QGraphicsItem*> children = childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ if (children[i]->type() >= InitialStateType) {
+ QRectF br = children[i]->boundingRect();
+ QPointF p = children[i]->pos() + br.topLeft();
+ br.moveTopLeft(p);
+ r = r.united(br);
+ }
+ }
+
+ if (m_transitionRect.isValid()) {
+ r.setLeft(r.left() - m_transitionRect.width());
+ r.setHeight(qMax(r.height(), m_transitionRect.height()));
+ r.moveBottom(qMax(r.bottom(), m_transitionRect.bottom()));
+ }
+
+ if (!r.isNull())
+ r.adjust(-20, -(rr.height() * 0.06 + 40), 20, 20);
+
+ return r;
+}
+
+void StateItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ ConnectableItem::mouseDoubleClickEvent(event);
+ shrink();
+ updateUIProperties();
+}
+
+void StateItem::createContextMenu(QMenu *menu)
+{
+ QVariantMap data;
+ if (!m_parallelState) {
+ data[Constants::C_SCXMLTAG_ACTIONTYPE] = TagUtils::SetAsInitial;
+ menu->addAction(tr("Set as Initial"))->setData(data);
+ }
+
+ data[Constants::C_SCXMLTAG_ACTIONTYPE] = TagUtils::ZoomToState;
+ menu->addAction(tr("Zoom to State"))->setData(data);
+
+ if (type() == ParallelType) {
+ data[Constants::C_SCXMLTAG_ACTIONTYPE] = TagUtils::Relayout;
+ menu->addAction(tr("Relayout"))->setData(data);
+ }
+
+ menu->addSeparator();
+ ConnectableItem::createContextMenu(menu);
+}
+
+void StateItem::selectedMenuAction(const QAction *action)
+{
+ if (!action)
+ return;
+
+ const ScxmlTag *tag = this->tag();
+
+ if (tag) {
+ const QVariantMap data = action->data().toMap();
+ const int actionType = data.value(Constants::C_SCXMLTAG_ACTIONTYPE, -1).toInt();
+
+ ScxmlDocument *document = tag->document();
+ switch (actionType) {
+ case TagUtils::SetAsInitial: {
+ ScxmlTag *parentTag = tag->parentTag();
+ if (parentTag) {
+ document->undoStack()->beginMacro(tr("Change initial-state"));
+
+ ScxmlTag *initialTag = parentTag->child("initial");
+ if (initialTag) {
+ ScxmlTag *initialTransitionTag = initialTag->child("transition");
+ if (initialTransitionTag)
+ document->setValue(initialTransitionTag, "target", tag->attribute("id", true));
+ else {
+ auto newTransition = new ScxmlTag(Transition, document);
+ newTransition->setAttribute("target", tag->attribute("id", true));
+ document->addTag(initialTag, newTransition);
+ }
+ } else
+ document->setValue(parentTag, "initial", tag->attribute("id", true));
+
+ checkInitial(true);
+ document->undoStack()->endMacro();
+ }
+ break;
+ }
+ case TagUtils::Relayout: {
+ document->undoStack()->beginMacro(tr("Relayout"));
+ doLayout(depth());
+ document->undoStack()->endMacro();
+ break;
+ }
+ case TagUtils::ZoomToState: {
+ emit BaseItem::openToDifferentView(this);
+ break;
+ }
+ default:
+ ConnectableItem::selectedMenuAction(action);
+ break;
+ }
+ }
+}
+
+void StateItem::checkInitial(bool parent)
+{
+ QList<QGraphicsItem*> items;
+ ScxmlTag *tag = nullptr;
+
+ if (parent) {
+ if (parentItem()) {
+ items = parentItem()->childItems();
+ if (parentBaseItem())
+ tag = parentBaseItem()->tag();
+ } else {
+ auto sc = static_cast<GraphicsScene*>(scene());
+ if (sc) {
+ sc->checkInitialState();
+ return;
+ }
+ }
+ } else {
+ items = childItems();
+ tag = this->tag();
+ }
+
+ if (items.count() > 0 && tag && uiFactory()) {
+ auto utilsProvider = static_cast<UtilsProvider*>(uiFactory()->object("utilsProvider"));
+ if (utilsProvider)
+ utilsProvider->checkInitialState(items, tag);
+ }
+}
+
+void StateItem::titleHasChanged(const QString &text)
+{
+ QString strOldId = tagValue("id", true);
+ setTagValue("id", text);
+
+ // Check initial attribute
+ if (tag() && !m_parallelState) {
+ ScxmlTag *parentTag = tag()->parentTag();
+ if (!strOldId.isEmpty() && parentTag->attribute("initial") == strOldId)
+ parentTag->setAttribute("initial", tagValue("id", true));
+ }
+}
+
+void StateItem::updateTextPositions()
+{
+ if (m_parallelState) {
+ m_stateNameItem->setPos(m_titleRect.x(), m_titleRect.top());
+ m_stateNameItem->setItalic(true);
+ } else {
+ m_stateNameItem->setPos(m_titleRect.center().x() - m_stateNameItem->boundingRect().width() / 2, m_titleRect.top());
+ m_stateNameItem->setItalic(false);
+ }
+
+ QPointF p(m_stateNameItem->pos().x() - WARNING_ITEM_SIZE, m_titleRect.center().y() - WARNING_ITEM_SIZE / 2);
+ if (m_idWarningItem)
+ m_idWarningItem->setPos(p);
+ if (m_stateWarningItem)
+ m_stateWarningItem->setPos(p);
+}
+
+void StateItem::updatePolygon()
+{
+ m_drawingRect = boundingRect().adjusted(5, 5, -5, -5);
+
+ m_polygon.clear();
+ m_polygon << m_drawingRect.topLeft()
+ << m_drawingRect.topRight()
+ << m_drawingRect.bottomRight()
+ << m_drawingRect.bottomLeft()
+ << m_drawingRect.topLeft();
+
+ m_titleRect = QRectF(m_drawingRect.left(), m_drawingRect.top(), m_drawingRect.width(), TEXT_ITEM_HEIGHT + m_drawingRect.height() * 0.06);
+ QFont f = m_stateNameItem->font();
+ f.setPixelSize(m_titleRect.height() * 0.65);
+ m_stateNameItem->setFont(f);
+
+ updateTextPositions();
+}
+
+bool StateItem::canStartTransition(ItemType /*type*/) const
+{
+ return !m_parallelState;
+}
+
+void StateItem::connectToParent(BaseItem *parentItem)
+{
+ ConnectableItem::connectToParent(parentItem);
+ updateTextPositions();
+ checkInitial(true);
+}
+
+void StateItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ ConnectableItem::paint(painter, option, widget);
+
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing, true);
+
+ // Set opacity and color
+ painter->setOpacity(getOpacity());
+ m_pen.setColor(overlapping() ? qRgb(0xff, 0x00, 0x60) : qRgb(0x45, 0x45, 0x45));
+ painter->setPen(m_pen);
+ QColor stateColor(editorInfo(Constants::C_SCXML_EDITORINFO_STATECOLOR));
+ if (!stateColor.isValid())
+ stateColor = tag() ? tag()->document()->getColor(depth()) : QColor(0x12, 0x34, 0x56);
+
+ // Draw basic frame
+ QRectF r = boundingRect();
+ QLinearGradient grad(r.topLeft(), r.bottomLeft());
+ grad.setColorAt(0, stateColor.lighter(115));
+ grad.setColorAt(1, stateColor);
+ painter->setBrush(QBrush(grad));
+
+ painter->drawRoundedRect(m_drawingRect, STATE_RADIUS, STATE_RADIUS);
+
+ // Background image
+ ScxmlUiFactory *uifactory = uiFactory();
+ if (uifactory) {
+ auto imageProvider = static_cast<ImageProvider*>(uifactory->object("imageProvider"));
+ if (imageProvider) {
+ const QImage *image = imageProvider->backgroundImage(tag());
+ if (image) {
+ int w = m_drawingRect.width() - 2 * STATE_RADIUS;
+ int h = m_drawingRect.height() - m_titleRect.height() - 2 * STATE_RADIUS;
+ int left = m_drawingRect.left() + STATE_RADIUS;
+ int top = m_drawingRect.top() + m_titleRect.height() + STATE_RADIUS;
+ if (m_transitionRect.isValid()) {
+ w = m_drawingRect.right() - m_transitionRect.right() - 2 * STATE_RADIUS;
+ left = m_transitionRect.right() + STATE_RADIUS;
+ }
+
+ m_backgroundImage = image->scaled(w, h, Qt::KeepAspectRatio);
+ painter->drawImage(QPointF(left + (w - m_backgroundImage.width()) / 2, top + (h - m_backgroundImage.height()) / 2), m_backgroundImage);
+ } else
+ m_backgroundImage = QImage();
+ }
+ }
+
+ // Draw title rect
+ if (!m_parallelState) {
+ painter->drawLine(QLineF(m_titleRect.bottomLeft(), m_titleRect.bottomRight()));
+
+ if (m_initial) {
+ double size = m_titleRect.height() * 0.3;
+ painter->setBrush(QColor(0x4d, 0x4d, 0x4d));
+ painter->drawEllipse(QPointF(m_titleRect.left() + 2 * size, m_titleRect.center().y()), size, size);
+ }
+ }
+
+ // Transition part
+ if (m_transitionRect.isValid()) {
+ qreal tx = m_transitionRect.right();
+ painter->drawLine(QLineF(QPointF(tx, m_titleRect.bottom() + STATE_RADIUS), QPointF(tx, m_drawingRect.bottom() - STATE_RADIUS)));
+ }
+
+ painter->restore();
+}
+
+void StateItem::init(ScxmlTag *tag, BaseItem *parentItem, bool initChildren, bool blockUpdates)
+{
+ setBlockUpdates(blockUpdates);
+ ConnectableItem::init(tag, parentItem);
+
+ if (initChildren) {
+ for (int i = 0; i < tag->childCount(); ++i) {
+ ScxmlTag *child = tag->child(i);
+ ConnectableItem *newItem = SceneUtils::createItemByTagType(child->tagType(), QPointF());
+ if (newItem) {
+ newItem->init(child, this, initChildren, blockUpdates);
+ newItem->finalizeCreation();
+ }
+ }
+ }
+ if (blockUpdates)
+ setBlockUpdates(false);
+}
+
+QString StateItem::itemId() const
+{
+ return m_stateNameItem ? m_stateNameItem->toPlainText() : QString();
+}
+
+void StateItem::doLayout(int d)
+{
+ if (depth() != d)
+ return;
+
+ SceneUtils::layout(childItems());
+ updateBoundingRect();
+ shrink();
+}
+
+void StateItem::setInitial(bool initial)
+{
+ m_initial = initial;
+ update();
+ checkWarnings();
+}
+
+void StateItem::checkWarnings()
+{
+ if (m_idWarningItem)
+ m_idWarningItem->check();
+ if (m_stateWarningItem)
+ m_stateWarningItem->check();
+
+ if (parentItem() && parentItem()->type() == StateType)
+ qgraphicsitem_cast<StateItem*>(parentItem())->checkWarnings();
+}
+
+bool StateItem::isInitial() const
+{
+ return m_initial;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/stateitem.h b/src/plugins/scxmleditor/plugin_interface/stateitem.h
new file mode 100644
index 00000000000..97954f8100b
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/stateitem.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "connectableitem.h"
+#include <QPen>
+
+QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class TransitionItem;
+class TextItem;
+class IdWarningItem;
+class StateWarningItem;
+
+/**
+ * @brief The StateItem class represents the SCXML-State.
+ */
+class StateItem : public ConnectableItem
+{
+ Q_OBJECT
+
+public:
+ explicit StateItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
+
+ int type() const override
+ {
+ return StateType;
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+
+ QString itemId() const;
+ void init(ScxmlTag *tag, BaseItem *parentItem = nullptr, bool initChildren = true, bool blockUpdates = false) override;
+ void updateBoundingRect();
+ void updateAttributes() override;
+ void updateEditorInfo(bool allChildren = false) override;
+ void updateColors() override;
+ virtual void doLayout(int d) override;
+ void shrink();
+ void setInitial(bool initial);
+ bool isInitial() const;
+ void checkWarnings() override;
+ void checkInitial(bool parent = false) override;
+ QRectF childItemsBoundingRect() const;
+ void connectToParent(BaseItem *parentItem) override;
+
+protected:
+ void updatePolygon() override;
+ void transitionsChanged() override;
+ void transitionCountChanged() override;
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
+ bool canStartTransition(ItemType type) const override;
+ void createContextMenu(QMenu *menu) override;
+ void selectedMenuAction(const QAction *action) override;
+ void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
+ void writeStateProperties(const QString &tag, QXmlStreamWriter &xml);
+
+ QRectF m_drawingRect;
+ QRectF m_titleRect;
+ QRectF m_transitionRect;
+
+private:
+ void titleHasChanged(const QString &text);
+ void updateTextPositions();
+ void checkParentBoundingRect();
+ void checkWarningItems();
+
+ TextItem *m_stateNameItem;
+ StateWarningItem *m_stateWarningItem = nullptr;
+ IdWarningItem *m_idWarningItem = nullptr;
+ QPen m_pen;
+ bool m_initial = false;
+ bool m_parallelState = false;
+ QImage m_backgroundImage;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/statewarningitem.cpp b/src/plugins/scxmleditor/plugin_interface/statewarningitem.cpp
new file mode 100644
index 00000000000..8074c9c7ffa
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/statewarningitem.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "statewarningitem.h"
+#include "idwarningitem.h"
+
+#include <utils/utilsicons.h>
+
+using namespace ScxmlEditor::PluginInterface;
+
+StateWarningItem::StateWarningItem(StateItem *parent)
+ : WarningItem(parent)
+{
+ setSeverity(OutputPane::Warning::InfoType);
+ setTypeName(tr("State"));
+ setDescription(tr("Draw some transitions to state."));
+
+ setPixmap(Utils::Icons::WARNING.pixmap());
+ setReason(tr("No input connection"));
+}
+
+void StateWarningItem::setIdWarning(IdWarningItem *idwarning)
+{
+ m_idWarningItem = idwarning;
+}
+
+void StateWarningItem::check()
+{
+ if (m_parentItem) {
+ if (m_idWarningItem && m_idWarningItem->isVisible())
+ setWarningActive(false);
+ else {
+ bool outputProblem = !m_parentItem->hasOutputTransitions(m_parentItem, true);
+ bool inputProblem = !m_parentItem->isInitial() && !m_parentItem->hasInputTransitions(m_parentItem, true);
+
+ if (outputProblem && inputProblem) {
+ setReason(tr("No input or output connections (%1)").arg(m_parentItem->itemId()));
+ setDescription(tr("Draw some transitions to or from state."));
+ setWarningActive(true);
+ } else if (outputProblem) {
+ setReason(tr("No output connections (%1)").arg(m_parentItem->itemId()));
+ setDescription(tr("Draw some transitions from state."));
+ setWarningActive(true);
+ } else if (inputProblem) {
+ setReason(tr("No input connections (%1)").arg(m_parentItem->itemId()));
+ setDescription(tr("Draw some transitions to state."));
+ setWarningActive(true);
+ } else
+ setWarningActive(false);
+ }
+ }
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/statewarningitem.h b/src/plugins/scxmleditor/plugin_interface/statewarningitem.h
new file mode 100644
index 00000000000..0893a6408de
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/statewarningitem.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "stateitem.h"
+#include "warningitem.h"
+#include <QPointer>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class IdWarningItem;
+
+/**
+ * @brief The StateWarningItem class
+ */
+class StateWarningItem : public WarningItem
+{
+ Q_OBJECT
+
+public:
+ StateWarningItem(StateItem *parent = nullptr);
+
+ void setIdWarning(IdWarningItem *idwarning);
+
+ int type() const override
+ {
+ return StateWarningType;
+ }
+
+ void check() override;
+
+private:
+ QPointer<IdWarningItem> m_idWarningItem;
+ StateItem *m_parentItem = nullptr;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/tagtextitem.cpp b/src/plugins/scxmleditor/plugin_interface/tagtextitem.cpp
new file mode 100644
index 00000000000..9c084328bd0
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/tagtextitem.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "tagtextitem.h"
+
+#include <QGraphicsScene>
+#include <QGraphicsSceneHoverEvent>
+#include <QLabel>
+#include <QPainter>
+
+using namespace ScxmlEditor::PluginInterface;
+
+TagTextItem::TagTextItem(QGraphicsItem *parent)
+ : QGraphicsObject(parent)
+{
+ setFlag(ItemIsMovable, true);
+ setFlag(ItemIsFocusable, true);
+ setFlag(ItemIsSelectable, true);
+ m_textItem = new TextItem(this);
+ connect(m_textItem, &TextItem::textChanged, this, [=](){
+ emit textChanged();
+ });
+ connect(m_textItem, &TextItem::textReady, this, [=](const QString &text){
+ emit textReady(text);
+ });
+ connect(m_textItem, &TextItem::selected, this, [=](bool sel){
+ emit selected(sel);
+ });
+ setAcceptHoverEvents(true);
+}
+
+QRectF TagTextItem::boundingRect() const
+{
+ return m_textItem->boundingRect().adjusted(-8, -8, 8, 8);
+}
+
+void TagTextItem::setText(const QString &text)
+{
+ m_textItem->setText(text);
+}
+
+void TagTextItem::setDefaultTextColor(const QColor &col)
+{
+ m_textItem->setDefaultTextColor(col);
+}
+
+bool TagTextItem::needIgnore(const QPointF sPos)
+{
+ // If we found QuickTransition-item or CornerGrabber at this point, we must ignore mouse press here
+ // So we can press QuickTransition/CornerGrabber item although there is transition lines front of these items
+ foreach (QGraphicsItem *item, scene()->items(sPos)) {
+ if (item->type() == QuickTransitionType || (item->type() == CornerGrabberType && item->parentItem() != this))
+ return true;
+ }
+
+ return false;
+}
+
+void TagTextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
+{
+ if (needIgnore(e->scenePos())) {
+ e->ignore();
+ return;
+ }
+
+ setCursor(Qt::SizeAllCursor);
+ QGraphicsObject::hoverEnterEvent(e);
+}
+
+void TagTextItem::hoverMoveEvent(QGraphicsSceneHoverEvent *e)
+{
+ if (needIgnore(e->scenePos())) {
+ setCursor(Qt::ArrowCursor);
+ e->ignore();
+ return;
+ }
+
+ setCursor(Qt::SizeAllCursor);
+ QGraphicsObject::hoverEnterEvent(e);
+}
+
+void TagTextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
+{
+ setCursor(Qt::ArrowCursor);
+ QGraphicsObject::hoverLeaveEvent(e);
+}
+
+void TagTextItem::mousePressEvent(QGraphicsSceneMouseEvent *e)
+{
+ if (needIgnore(e->scenePos())) {
+ e->ignore();
+ return;
+ }
+
+ m_startPos = pos();
+ QGraphicsObject::mousePressEvent(e);
+}
+
+void TagTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e)
+{
+ m_movePoint += (pos() - m_startPos);
+ emit movePointChanged();
+ QGraphicsObject::mouseReleaseEvent(e);
+}
+
+void TagTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(painter)
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
+}
+
+void TagTextItem::resetMovePoint(const QPointF &point)
+{
+ m_movePoint = point;
+}
+
+QPointF TagTextItem::movePoint() const
+{
+ return m_movePoint;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/tagtextitem.h b/src/plugins/scxmleditor/plugin_interface/tagtextitem.h
new file mode 100644
index 00000000000..77e13d87bf3
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/tagtextitem.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "textitem.h"
+#include <QGraphicsObject>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+/**
+ * @brief The TagTextItem class provides the movable and editable text-item.
+ */
+class TagTextItem : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+ explicit TagTextItem(QGraphicsItem *parent = nullptr);
+
+ int type() const override
+ {
+ return TagTextType;
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+
+ QRectF boundingRect() const override;
+
+ void setText(const QString &text);
+ void setDefaultTextColor(const QColor &col);
+
+ void resetMovePoint(const QPointF &p = QPointF(0, 0));
+ QPointF movePoint() const;
+
+protected:
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *e) override;
+ void hoverMoveEvent(QGraphicsSceneHoverEvent *e) override;
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *e) override;
+ void mousePressEvent(QGraphicsSceneMouseEvent *e) override;
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *e) override;
+
+signals:
+ void textChanged();
+ void textReady(const QString &text);
+ void selected(bool sel);
+ void movePointChanged();
+
+private:
+ bool needIgnore(const QPointF sPos);
+
+ QPointF m_movePoint;
+ QPointF m_startPos;
+ TextItem *m_textItem;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/textitem.cpp b/src/plugins/scxmleditor/plugin_interface/textitem.cpp
new file mode 100644
index 00000000000..f86bf03cfe6
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/textitem.cpp
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "textitem.h"
+#include <QCursor>
+#include <QGraphicsScene>
+#include <QGraphicsSceneMouseEvent>
+#include <QPainter>
+#include <QTextCursor>
+#include <QTextDocument>
+#include <QTextOption>
+#include <qmath.h>
+
+using namespace ScxmlEditor::PluginInterface;
+
+TextItem::TextItem(QGraphicsItem *parent)
+ : QGraphicsTextItem(parent)
+{
+ init();
+}
+
+TextItem::TextItem(const QString &id, QGraphicsItem *parent)
+ : QGraphicsTextItem(id, parent)
+{
+ init();
+}
+
+void TextItem::init()
+{
+ setTextInteractionFlags(Qt::TextEditorInteraction);
+ setFlag(ItemIsSelectable, true);
+ setFlag(ItemIsFocusable, true);
+
+ QTextOption options;
+ options.setWrapMode(QTextOption::NoWrap);
+ options.setAlignment(Qt::AlignCenter);
+ document()->setDefaultTextOption(options);
+
+ connect(document(), &QTextDocument::contentsChanged, this, &TextItem::checkText);
+
+ QFont f = font();
+ f.setPixelSize(13);
+ setFont(f);
+}
+
+void TextItem::setItalic(bool ital)
+{
+ QFont f = font();
+ f.setItalic(ital);
+ setFont(f);
+}
+
+void TextItem::checkText()
+{
+ if (document()->textWidth() <= 40)
+ document()->setTextWidth(40);
+ else
+ document()->setTextWidth(-1);
+
+ emit textChanged();
+}
+
+void TextItem::setText(const QString &t)
+{
+ blockSignals(true);
+ setPlainText(t);
+ blockSignals(false);
+}
+
+void TextItem::focusInEvent(QFocusEvent *event)
+{
+ QGraphicsTextItem::focusInEvent(event);
+ emit selected(true);
+}
+
+void TextItem::focusOutEvent(QFocusEvent *event)
+{
+ emit textReady(toPlainText());
+ QGraphicsTextItem::focusOutEvent(event);
+}
+
+void TextItem::keyPressEvent(QKeyEvent *event)
+{
+ switch (event->key()) {
+ case Qt::Key_Escape:
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_Tab:
+ event->accept();
+ clearFocus();
+ return;
+ default:
+ break;
+ }
+
+ QGraphicsTextItem::keyPressEvent(event);
+}
+
+bool TextItem::needIgnore(const QPointF sPos) const
+{
+ // If we found QuickTransition-item or CornerGrabber at this point, we must ignore mouse press here
+ // So we can press QuickTransition/CornerGrabber item although there is transition lines front of these items
+ foreach (QGraphicsItem *item, scene()->items(sPos)) {
+ if (item->type() == QuickTransitionType || (item->type() == CornerGrabberType && item->parentItem() != this))
+ return true;
+ }
+
+ return false;
+}
+
+void TextItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (needIgnore(event->scenePos())) {
+ event->ignore();
+ return;
+ }
+
+ QGraphicsTextItem::mousePressEvent(event);
+ setFocus();
+}
+
+void TextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (needIgnore(event->scenePos())) {
+ event->ignore();
+ return;
+ }
+
+ QGraphicsTextItem::mouseReleaseEvent(event);
+ setFocus();
+}
+
+void TextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (needIgnore(event->scenePos())) {
+ event->ignore();
+ return;
+ }
+
+ setFocus();
+ QGraphicsTextItem::mouseDoubleClickEvent(event);
+ emit selected(true);
+}
+
+void TextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
+{
+ if (needIgnore(e->scenePos())) {
+ e->ignore();
+ return;
+ }
+
+ setCursor(Qt::IBeamCursor);
+ QGraphicsTextItem::hoverEnterEvent(e);
+}
+
+void TextItem::hoverMoveEvent(QGraphicsSceneHoverEvent *e)
+{
+ if (needIgnore(e->scenePos())) {
+ setCursor(Qt::ArrowCursor);
+ e->ignore();
+ return;
+ }
+
+ setCursor(Qt::IBeamCursor);
+ QGraphicsTextItem::hoverEnterEvent(e);
+}
+
+void TextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
+{
+ setCursor(Qt::ArrowCursor);
+ QGraphicsTextItem::hoverLeaveEvent(e);
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/textitem.h b/src/plugins/scxmleditor/plugin_interface/textitem.h
new file mode 100644
index 00000000000..aa9de9f6228
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/textitem.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "baseitem.h"
+#include <QFocusEvent>
+#include <QGraphicsTextItem>
+#include <QKeyEvent>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class TextItem : public QGraphicsTextItem
+{
+ Q_OBJECT
+
+public:
+ explicit TextItem(QGraphicsItem *parent = nullptr);
+ explicit TextItem(const QString &id, QGraphicsItem *parent = nullptr);
+
+ int type() const override
+ {
+ return TextType;
+ }
+
+ void setItalic(bool ital);
+ void setText(const QString &t);
+
+protected:
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *e) override;
+ void hoverMoveEvent(QGraphicsSceneHoverEvent *e) override;
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *e) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void focusInEvent(QFocusEvent *event) override;
+ void focusOutEvent(QFocusEvent *event) override;
+ void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
+
+signals:
+ void textChanged();
+ void textReady(const QString &text);
+ void selected(bool sel);
+
+private:
+ void checkText();
+ void init();
+ bool needIgnore(const QPointF sPos) const;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/transitionitem.cpp b/src/plugins/scxmleditor/plugin_interface/transitionitem.cpp
new file mode 100644
index 00000000000..af5471808a6
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/transitionitem.cpp
@@ -0,0 +1,1276 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "transitionitem.h"
+#include "connectableitem.h"
+#include "cornergrabberitem.h"
+#include "finalstateitem.h"
+#include "graphicsitemprovider.h"
+#include "graphicsscene.h"
+#include "parallelitem.h"
+#include "sceneutils.h"
+#include "scxmldocument.h"
+#include "scxmleditorconstants.h"
+#include "scxmltagutils.h"
+#include "scxmluifactory.h"
+#include "serializer.h"
+#include "stateitem.h"
+#include "tagtextitem.h"
+
+#include <QBrush>
+#include <QDebug>
+#include <QGraphicsScene>
+#include <QMenu>
+#include <QPainter>
+#include <QPen>
+#include <QUndoStack>
+#include <QtMath>
+
+using namespace ScxmlEditor::PluginInterface;
+
+const qreal SELECTION_DISTANCE = 10;
+
+TransitionItem::TransitionItem(BaseItem *parent)
+ : BaseItem(parent)
+ , m_startTargetFactor(0.5, 0.5)
+ , m_endTargetFactor(0.5, 0.5)
+{
+ setFlag(ItemIsSelectable, true);
+
+ m_highlightPen = QPen(QColor(0xff, 0x00, 0x60));
+ m_highlightPen.setWidth(8);
+ m_highlightPen.setJoinStyle(Qt::MiterJoin);
+
+ m_pen = QPen(QColor(0x12, 0x12, 0x12));
+ m_pen.setWidth(2);
+
+ m_arrow << QPointF(0, 0)
+ << QPointF(1, 1)
+ << QPointF(0, 1);
+
+ m_eventTagItem = new TagTextItem(this);
+ connect(m_eventTagItem, &TagTextItem::selected, this, [=](bool sel){
+ setItemSelected(sel);
+ });
+ connect(m_eventTagItem, &TagTextItem::textReady, this, &TransitionItem::textHasChanged);
+ connect(m_eventTagItem, &TagTextItem::movePointChanged, this, &TransitionItem::textItemPositionChanged);
+
+ checkWarningItems();
+}
+
+TransitionItem::~TransitionItem()
+{
+ setBlockUpdates(true);
+ removeTransition(Start);
+ removeTransition(End);
+ //updateTarget();
+}
+
+void TransitionItem::checkWarningItems()
+{
+ ScxmlUiFactory *uifactory = uiFactory();
+ if (uifactory) {
+ auto provider = static_cast<GraphicsItemProvider*>(uifactory->object("graphicsItemProvider"));
+ if (provider) {
+ if (!m_warningItem)
+ m_warningItem = static_cast<TransitionWarningItem*>(provider->createWarningItem(Constants::C_STATE_WARNING_TRANSITION, this));
+ }
+ }
+}
+
+void TransitionItem::setTag(ScxmlTag *tag)
+{
+ BaseItem::setTag(tag);
+ if (tag) {
+ if (tag->tagType() == InitialTransition)
+ m_eventTagItem->setVisible(false);
+ }
+}
+
+void TransitionItem::textItemPositionChanged()
+{
+ QPointF p = m_eventTagItem->movePoint();
+ QString data;
+ if (p.toPoint() != QPoint(0, 0)) {
+ Serializer s;
+ s.append(p);
+ data = s.data();
+ }
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_MOVEPOINT, data);
+
+ updateComponents();
+}
+
+void TransitionItem::textHasChanged(const QString &text)
+{
+ setTagValue("event", text);
+}
+
+void TransitionItem::createGrabbers()
+{
+ if (m_cornerGrabbers.count() != m_cornerPoints.count()) {
+ int selectedGrabberIndex = m_cornerGrabbers.indexOf(m_selectedCornerGrabber);
+
+ if (m_cornerGrabbers.count() > 0) {
+ qDeleteAll(m_cornerGrabbers);
+ m_cornerGrabbers.clear();
+ }
+
+ for (int i = 0; i < m_cornerPoints.count(); ++i) {
+ auto cornerGrabber = new CornerGrabberItem(this);
+ cornerGrabber->setGrabberType(CornerGrabberItem::Circle);
+ m_cornerGrabbers << cornerGrabber;
+ }
+
+ if (selectedGrabberIndex >= 0 && selectedGrabberIndex < m_cornerGrabbers.count())
+ m_selectedCornerGrabber = m_cornerGrabbers[selectedGrabberIndex];
+ else
+ m_selectedCornerGrabber = nullptr;
+ }
+
+ m_pen.setStyle(Qt::DotLine);
+
+ m_lineSelected = true;
+ updateGrabberPositions();
+}
+
+void TransitionItem::removeGrabbers()
+{
+ if (m_cornerGrabbers.count() > 0) {
+ qDeleteAll(m_cornerGrabbers);
+ m_cornerGrabbers.clear();
+ }
+
+ m_lineSelected = false;
+ m_pen.setStyle(Qt::SolidLine);
+}
+
+void TransitionItem::updateGrabberPositions()
+{
+ for (int i = 0; i < qMin(m_cornerGrabbers.count(), m_cornerPoints.count()); ++i)
+ m_cornerGrabbers[i]->setPos(m_cornerPoints[i]);
+}
+
+void TransitionItem::removeUnnecessaryPoints()
+{
+ if (m_cornerPoints.count() > 2) {
+ bool found = true;
+ while (found) {
+ found = false;
+ for (int i = 1; i < (m_cornerPoints.count() - 1); ++i) {
+ if (QLineF(m_cornerPoints[i], m_cornerPoints[i + 1]).length() <= 20 || QLineF(m_cornerPoints[i], m_cornerPoints[i - 1]).length() <= 20) {
+ m_cornerPoints.takeAt(i);
+ if (i < m_cornerGrabbers.count())
+ delete m_cornerGrabbers.takeAt(i);
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ storeValues();
+ updateComponents();
+}
+
+QVariant TransitionItem::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ QVariant retValue = BaseItem::itemChange(change, value);
+
+ switch (change) {
+ case ItemSelectedChange:
+ if (!m_mouseGrabbed) {
+ if (value.toBool())
+ createGrabbers();
+ else
+ removeGrabbers();
+ }
+ break;
+ case ItemSceneHasChanged:
+ checkWarningItems();
+ break;
+ default:
+ break;
+ }
+
+ return retValue;
+}
+
+void TransitionItem::snapToAnyPoint(int id, const QPointF &newPoint, int diff)
+{
+ // Check snap to grid
+ bool snappedX = false;
+ bool snappedY = false;
+ for (int i = 0; i < m_cornerPoints.count(); ++i) {
+ if (id != i) {
+ if (qAbs(newPoint.x() - m_cornerPoints[i].x()) < diff) {
+ m_cornerPoints[id].setX(m_cornerPoints[i].x());
+ snappedX = true;
+ }
+ if (qAbs(newPoint.y() - m_cornerPoints[i].y()) < diff) {
+ m_cornerPoints[id].setY(m_cornerPoints[i].y());
+ snappedY = true;
+ }
+ }
+ }
+
+ if (!snappedX)
+ m_cornerPoints[id].setX(newPoint.x());
+
+ if (!snappedY)
+ m_cornerPoints[id].setY(newPoint.y());
+}
+
+void TransitionItem::snapPointToPoint(int idSnap, const QPointF &p, int diff)
+{
+ if (idSnap >= 0 && idSnap < m_cornerPoints.count()) {
+ if (qAbs(p.x() - m_cornerPoints[idSnap].x()) < diff)
+ m_cornerPoints[idSnap].setX(p.x());
+ if (qAbs(p.y() - m_cornerPoints[idSnap].y()) < diff)
+ m_cornerPoints[idSnap].setY(p.y());
+ }
+}
+
+void TransitionItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ // We want to pan scene when Shift is pressed -> that's why ignore mouse-event here
+ if (event->modifiers() & Qt::ShiftModifier) {
+ event->ignore();
+ return;
+ }
+
+ QPointF p = event->pos();
+ bool bLeftButton = event->button() == Qt::LeftButton;
+ if (m_mouseGrabbed) {
+ if (bLeftButton) {
+ m_cornerPoints.append(p);
+ snapToAnyPoint(m_cornerPoints.count() - 1, p);
+
+ if (m_cornerGrabbers.count() > 0) {
+ auto corner = new CornerGrabberItem(this);
+ corner->setGrabberType(CornerGrabberItem::Circle);
+ corner->setPos(p);
+ m_cornerGrabbers.append(corner);
+ }
+ }
+ event->accept();
+ } else {
+ // Check QuickTransition
+ if (bLeftButton) {
+ // If we found QuickTransition-item or CornerGrabber at this point, we must ignore mouse press here
+ // So we can press QuickTransition/CornerGrabber item although there is transition lines front of these items
+ foreach (QGraphicsItem *item, scene()->items(event->scenePos())) {
+ if (item->type() == QuickTransitionType || (item->type() == CornerGrabberType && item->parentItem() != this)) {
+ event->ignore();
+ return;
+ }
+ }
+ }
+
+ // Check selection
+ bool sel = m_lineSelected;
+ int i;
+ int foundPointIndex = -1;
+ for (i = 0; i < m_cornerPoints.count(); ++i) {
+ if (QLineF(m_cornerPoints[i], p).length() <= SELECTION_DISTANCE) {
+ // Is pressed point close enough of the elbow-point
+ foundPointIndex = i;
+ sel = true;
+ break;
+ } else {
+ if (i < m_cornerPoints.count() - 1) {
+ QLineF line(m_cornerPoints[i], m_cornerPoints[i + 1]);
+
+ // Is pressed point close enough of the line
+ QPointF intersPoint;
+ QLineF line2(p, p + QPointF(SELECTION_DISTANCE, SELECTION_DISTANCE));
+ line2.setAngle(line.angle() + 90);
+ if (line.intersect(line2, &intersPoint) == QLineF::BoundedIntersection)
+ sel = true;
+ else {
+ line2.setAngle(line.angle() - 90);
+ sel = line.intersect(line2, &intersPoint) == QLineF::BoundedIntersection;
+ }
+
+ if (sel)
+ break;
+ }
+ }
+ }
+
+ // Create or remove grabbers
+ if (sel != m_lineSelected) {
+ if (sel)
+ createGrabbers();
+ else
+ removeGrabbers();
+
+ if (foundPointIndex > 0 && foundPointIndex < m_cornerGrabbers.count()) {
+ m_selectedCornerGrabber = m_cornerGrabbers[foundPointIndex];
+ m_selectedCornerGrabber->setSelected(true);
+ }
+ } else if (m_lineSelected && bLeftButton) {
+ m_cornerPoints.insert((i + 1), p);
+ m_selectedCornerGrabber = new CornerGrabberItem(this);
+ m_selectedCornerGrabber->setGrabberType(CornerGrabberItem::Circle);
+ m_selectedCornerGrabber->setPos(p);
+ m_cornerGrabbers.insert((i + 1), m_selectedCornerGrabber);
+ m_selectedCornerGrabber->setSelected(true);
+ } else if (m_lineSelected && !bLeftButton) {
+ showContextMenu(event);
+ }
+
+ if (sel)
+ event->accept();
+ else
+ event->ignore();
+ }
+}
+
+void TransitionItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ // We want to pan scene when Shift is pressed -> that's why ignore mouse-event here
+ if (event->modifiers() & Qt::ShiftModifier) {
+ event->ignore();
+ return;
+ }
+
+ if (m_mouseGrabbed) {
+ setEndPos(event->pos());
+ event->ignore();
+ } else if (m_selectedCornerGrabber) {
+ snapToAnyPoint(m_cornerGrabbers.indexOf(m_selectedCornerGrabber), event->pos());
+ updateComponents();
+ storeValues();
+ BaseItem::mouseMoveEvent(event);
+ }
+}
+
+void TransitionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ // We want to pan scene when Shift is pressed -> that's why ignore mouse-event here
+ if (event->modifiers() & Qt::ShiftModifier) {
+ event->ignore();
+ return;
+ }
+
+ if (m_mouseGrabbed) {
+ if (event->button() == Qt::RightButton) {
+ connectToTopItem(mapToScene(event->pos()), TransitionItem::End, m_grabbedTargetType);
+ setSelected(false);
+ tag()->document()->undoStack()->endMacro();
+ m_mouseGrabbed = false;
+ ungrabMouse();
+ storeValues();
+ }
+ event->accept();
+ } else {
+ if (event->button() == Qt::LeftButton) {
+ if (m_selectedCornerGrabber) {
+ m_selectedCornerGrabber = nullptr;
+ setSelected(true);
+ }
+ removeUnnecessaryPoints();
+ }
+ BaseItem::mouseReleaseEvent(event);
+ }
+}
+
+void TransitionItem::checkSelectionBeforeContextMenu(QGraphicsSceneMouseEvent *e)
+{
+ int ind = -1;
+ for (int i = 0; i < m_cornerGrabbers.count(); ++i) {
+ if (m_cornerGrabbers[i]->isSelected()) {
+ ind = i;
+ break;
+ }
+ }
+ m_selectedGrabberIndex = ind;
+ BaseItem::checkSelectionBeforeContextMenu(e);
+}
+
+void TransitionItem::createContextMenu(QMenu *menu)
+{
+ QVariantMap data;
+ if (m_selectedGrabberIndex > 0) {
+ data[Constants::C_SCXMLTAG_ACTIONTYPE] = TagUtils::RemovePoint;
+ data["cornerIndex"] = m_selectedGrabberIndex;
+ menu->addAction(tr("Remove Point"))->setData(data);
+ }
+
+ menu->addSeparator();
+ BaseItem::createContextMenu(menu);
+}
+
+void TransitionItem::selectedMenuAction(const QAction *action)
+{
+ if (action) {
+ QVariantMap data = action->data().toMap();
+ int actionType = data.value(Constants::C_SCXMLTAG_ACTIONTYPE, -1).toInt();
+
+ switch (actionType) {
+ case TagUtils::RemovePoint: {
+ int ind = data.value("cornerIndex", 0).toInt();
+ if (ind > 0) {
+ delete m_cornerGrabbers.takeAt(ind);
+ m_cornerPoints.takeAt(ind);
+ updateComponents();
+ storeValues();
+ }
+ break;
+ }
+ default:
+ BaseItem::selectedMenuAction(action);
+ break;
+ }
+ }
+}
+
+void TransitionItem::keyPressEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Delete) {
+ bool bFound = false;
+ if (m_cornerGrabbers.count() > 2) {
+ for (int i = m_cornerGrabbers.count() - 1; i >= 1; i--) {
+ if (m_cornerGrabbers[i]->isSelected()) {
+ delete m_cornerGrabbers.takeAt(i);
+ m_cornerPoints.takeAt(i);
+ bFound = true;
+ }
+ }
+ }
+ if (bFound) {
+ updateComponents();
+ storeValues();
+ event->accept();
+ return;
+ }
+ }
+
+ BaseItem::keyPressEvent(event);
+}
+
+bool TransitionItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
+{
+ if (watched->type() == CornerGrabberType) {
+ auto c = qgraphicsitem_cast<CornerGrabberItem*>(watched);
+ auto mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event);
+ if (c == nullptr || mouseEvent == nullptr)
+ return BaseItem::sceneEventFilter(watched, event);
+
+ int cid = m_cornerGrabbers.indexOf(c);
+ if (mouseEvent->type() == QEvent::GraphicsSceneMouseMove) {
+ if (mouseEvent->buttons() & Qt::LeftButton) {
+ QPointF movingPoint = c->pressedPoint() - mouseEvent->pos();
+
+ if (cid == 0) {
+ if (!m_movingFirstPoint) {
+ m_movingFirstPoint = true;
+ removeTransition(Start);
+ }
+ } else if (cid == (m_cornerPoints.count() - 1)) {
+ if (!m_movingLastPoint) {
+ m_movingLastPoint = true;
+ if (m_endItem)
+ removeTransition(End);
+ else {
+ updateZValue();
+ updateTargetType();
+ }
+ }
+ }
+
+ if (cid >= 0 && cid < m_cornerPoints.count())
+ snapToAnyPoint(cid, m_cornerPoints[cid] - movingPoint);
+
+ updateComponents();
+ }
+ return true;
+ } else if (mouseEvent->type() == QEvent::GraphicsSceneMouseRelease) {
+ if (mouseEvent->button() == Qt::LeftButton) {
+ if (cid == 0 || (cid == m_cornerPoints.count() - 1)) {
+ m_movingFirstPoint = false;
+ m_movingLastPoint = false;
+ connectToTopItem(watched->mapToScene(mouseEvent->pos()), cid == 0 ? Start : End, UnknownType);
+ }
+ removeUnnecessaryPoints();
+ } else
+ showContextMenu(mouseEvent);
+
+ storeValues();
+ return true;
+ }
+ }
+
+ return BaseItem::sceneEventFilter(watched, event);
+}
+
+void TransitionItem::removeTransition(TransitionPoint p)
+{
+ // Remove transition from the item
+ // Private function. Transition can itself remove connection from the item
+ if (p == Start && m_startItem) {
+ m_oldStartItem = m_startItem;
+ m_startItem->removeOutputTransition(this);
+ m_startItem = nullptr;
+ updateZValue();
+ updateTargetType();
+ if (m_oldStartItem)
+ m_oldStartItem->updateTransitions();
+ } else if (p == End && m_endItem) {
+ m_endItem->removeInputTransition(this);
+ m_endItem = nullptr;
+ updateZValue();
+ updateTargetType();
+ }
+}
+
+void TransitionItem::disconnectItem(ConnectableItem *item)
+{
+ // Disconnect item, normally called from the ConnectableItem
+ if (item == m_startItem)
+ removeTransition(Start);
+ if (item == m_endItem)
+ removeTransition(End);
+
+ updateTarget();
+}
+
+void TransitionItem::setStartItem(ConnectableItem *item)
+{
+ m_oldStartItem = nullptr;
+ m_startItem = item;
+
+ if (item) {
+ if (tag())
+ tag()->document()->changeParent(tag(), m_startItem->tag());
+ item->addOutputTransition(this);
+
+ if (m_cornerPoints.isEmpty()) {
+ m_cornerPoints << sceneTargetPoint(TransitionPoint::Start);
+ m_cornerPoints << sceneTargetPoint(TransitionPoint::End);
+ }
+ }
+
+ updateZValue();
+ updateComponents();
+ storeValues();
+}
+
+void TransitionItem::startTransitionFrom(ConnectableItem *item, const QPointF &mouseScenePos)
+{
+ m_oldStartItem = nullptr;
+ m_startItem = item;
+ m_startItem->addOutputTransition(this);
+ m_cornerPoints.clear();
+ m_cornerPoints << sceneTargetPoint(TransitionPoint::Start);
+ m_cornerPoints << mapFromScene(mouseScenePos);
+
+ createGrabbers();
+ updateZValue();
+ updateComponents();
+ storeValues();
+ m_cornerGrabbers.last()->setSelected(true);
+}
+
+void TransitionItem::connectToTopItem(const QPointF &pos, TransitionPoint tp, ItemType targetType)
+{
+ int cornerPoints = m_cornerPoints.count();
+
+ ConnectableItem *parentItem = nullptr;
+ ScxmlTag *parentTag = nullptr;
+ ScxmlDocument *document = tag()->document();
+
+ snapToAnyPoint(m_cornerPoints.count() - 1, pos);
+ QPointF p(m_cornerPoints.last());
+
+ // First try to find parentItem
+ QList<QGraphicsItem*> items = scene()->items(p);
+ if (items.count() > 0) {
+ for (int i = 0; i < items.count(); ++i) {
+ ItemType type = ItemType(items[i]->type());
+ if ((targetType == UnknownType && type >= FinalStateType) || type >= StateType) {
+ auto it = qgraphicsitem_cast<ConnectableItem*>(items[i]);
+ if (it) {
+ parentItem = it;
+ parentTag = parentItem->tag();
+ break;
+ }
+ }
+ }
+ }
+
+ if (!parentTag) {
+ if (document)
+ parentTag = document->rootTag();
+ }
+
+ // Connect existing item
+ if (targetType == UnknownType) {
+ switch (tp) {
+ case Start:
+ if (parentItem) {
+ m_startTargetFactor = calculateTargetFactor(parentItem, pos);
+ savePoint(m_startTargetFactor * 100, Constants::C_SCXML_EDITORINFO_STARTTARGETFACTORS);
+ }
+ setStartItem(parentItem ? parentItem : m_oldStartItem);
+ break;
+ case End:
+ m_endTargetFactor = calculateTargetFactor(parentItem, pos);
+ savePoint(m_endTargetFactor * 100, Constants::C_SCXML_EDITORINFO_ENDTARGETFACTORS);
+ setEndItem(parentItem);
+ break;
+ default:
+ break;
+ }
+
+ setSelected(false);
+ if (parentItem)
+ parentItem->setSelected(true);
+ removeGrabbers();
+ if (m_startItem == m_endItem && cornerPoints == 2) {
+ setTagValue("type", "internal");
+ setEndItem(0);
+ m_targetType = InternalNoTarget;
+ }
+
+ updateEventName();
+ storeValues();
+ } else {
+ // Create new item and connect to it
+ ConnectableItem *newItem = SceneUtils::createItem(targetType, parentItem == nullptr ? p : parentItem->mapFromScene(p));
+ if (newItem) {
+ ScxmlTag *newTag = SceneUtils::createTag(targetType, tag()->document());
+ newItem->setTag(newTag);
+ newItem->setParentItem(parentItem);
+ if (!parentItem)
+ scene()->addItem(newItem);
+
+ newItem->addInputTransition(this);
+ newItem->updateAttributes();
+ newItem->updateEditorInfo();
+ newItem->updateUIProperties();
+
+ if (parentItem)
+ parentItem->updateUIProperties();
+
+ document->addTag(parentTag, newTag);
+
+ setEndItem(newItem);
+ setSelected(false);
+ newItem->setSelected(true);
+ }
+
+ removeGrabbers();
+ }
+
+ updateTargetType();
+}
+
+void TransitionItem::setEndPos(const QPointF &endPos, bool snap)
+{
+ if (m_cornerPoints.count() >= 2) {
+ m_cornerPoints.last().setX(endPos.x());
+ m_cornerPoints.last().setY(endPos.y());
+
+ if (snap)
+ snapToAnyPoint(m_cornerPoints.count() - 1, endPos);
+ updateComponents();
+ storeValues();
+ }
+}
+
+void TransitionItem::setEndItem(ConnectableItem *item)
+{
+
+ if (item) {
+ m_endItem = item;
+ item->addInputTransition(this);
+ setEndPos(sceneTargetPoint(End), false);
+
+ if (m_cornerPoints.count() > 2)
+ snapPointToPoint(m_cornerPoints.count() - 2, m_cornerPoints.last(), 15);
+ } else {
+ removeTransition(End);
+ updateComponents();
+ storeValues();
+ }
+
+ updateZValue();
+ updateTarget();
+}
+
+QPointF TransitionItem::loadPoint(const QString &name)
+{
+ Serializer s;
+ QPointF p;
+ s.setData(editorInfo(name));
+ s.read(p);
+ return p;
+}
+
+void TransitionItem::savePoint(const QPointF &p, const QString &name)
+{
+ Serializer s;
+ s.append(p);
+ setEditorInfo(name, s.data(), true);
+}
+
+QPointF TransitionItem::calculateTargetFactor(ConnectableItem *item, const QPointF &pos)
+{
+ if (item) {
+ QRectF r = item->sceneBoundingRect().adjusted(-8, -8, 8, 8);
+ QPointF pixelFactorPoint = pos - r.topLeft();
+ QPointF normalizedPoint(qBound(0.0, pixelFactorPoint.x() / r.width(), 1.0), qBound(0.0, pixelFactorPoint.y() / r.height(), 1.0));
+
+ // Center point if close enough
+ if (qAbs(normalizedPoint.x() - 0.5) < 0.2 && qAbs(normalizedPoint.y() - 0.5) < 0.2)
+ return QPointF(0.5, 0.5);
+
+ return normalizedPoint;
+ }
+
+ return QPointF(0.5, 0.5);
+}
+
+QPointF TransitionItem::sceneTargetPoint(TransitionPoint p)
+{
+ ConnectableItem *item = nullptr;
+ QPointF factorPoint;
+ if (p == TransitionPoint::Start) {
+ item = m_startItem;
+ factorPoint = m_startTargetFactor;
+ } else {
+ if (m_endItem) {
+ item = m_endItem;
+ factorPoint = m_endTargetFactor;
+ } else {
+ item = m_startItem;
+ factorPoint = QPointF(0.5, 0.5);
+ }
+ }
+
+ QRectF r;
+ if (item)
+ r = item->sceneBoundingRect();
+
+ return r.topLeft() + QPointF(factorPoint.x() * r.width(), factorPoint.y() * r.height());
+}
+
+QPointF TransitionItem::findIntersectionPoint(ConnectableItem *item, const QLineF &line, const QPointF &defaultPoint)
+{
+ // Check circles
+ ItemType t = ItemType(item->type());
+ if (t >= InitialStateType && t <= HistoryType) {
+ QLineF l = item == m_endItem ? line : QLineF(line.p2(), line.p1());
+ l.setLength(l.length() - qMin(item->boundingRect().width(), item->boundingRect().height()) * 0.45);
+ return l.p2();
+ }
+
+ // Find intersection point between line and target item
+ QPolygonF itemPolygon = item->polygonShape();
+ if (itemPolygon.count() > 0) {
+ QPointF intersectPoint;
+ QPointF p1 = itemPolygon.at(0) + item->scenePos();
+ QPointF p2;
+ QLineF checkLine;
+ for (int i = 1; i < itemPolygon.count(); ++i) {
+ p2 = itemPolygon.at(i) + item->scenePos();
+ checkLine = QLineF(p1, p2);
+ if (checkLine.intersect(line, &intersectPoint) == QLineF::BoundedIntersection)
+ return intersectPoint;
+ p1 = p2;
+ }
+ }
+
+ return defaultPoint;
+}
+
+void TransitionItem::updateComponents()
+{
+ if (m_cornerPoints.count() < 2)
+ return;
+
+ // Check if we must move all points together
+ bool movePoints = (SceneUtils::isSomeSelected(m_startItem) && SceneUtils::isSomeSelected(m_endItem) && m_cornerPoints.count() > 2) || (m_movingFirstPoint && m_targetType == InternalNoTarget);
+
+ if (!movePoints && !m_mouseGrabbed) {
+ // Check the corners which are in the same line if cornerGrabbers are hidden
+ if (m_cornerGrabbers.isEmpty() && m_cornerPoints.count() > 2) {
+ for (int i = 0; i < m_cornerPoints.count() - 2; ++i) {
+ if (m_cornerPoints[i] != m_cornerPoints[i + 1] && m_cornerPoints[i] != m_cornerPoints[i + 2]) {
+ QLineF l1(m_cornerPoints[i], m_cornerPoints[i + 1]);
+ QLineF l2(m_cornerPoints[i], m_cornerPoints[i + 2]);
+
+ if (qRound(l1.angle()) == qRound(l2.angle())) {
+ m_cornerPoints.takeAt(i + 1);
+ storeValues();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if ((m_movingFirstPoint || m_movingLastPoint) && !(m_movingFirstPoint && m_targetType == InternalNoTarget))
+ movePoints = false;
+
+ // Init first line
+ QLineF firstLine(m_cornerPoints[0], m_cornerPoints[1]);
+ if (m_startItem) {
+ if (m_targetType <= InternalNoTarget) {
+ QPointF p = m_startItem->getInternalPosition(this, m_targetType);
+ firstLine.setP1(p);
+ firstLine.setP2(p + QPointF(20, 0));
+ m_cornerPoints[1] = firstLine.p2();
+ if (!(m_movingFirstPoint && m_targetType == InternalNoTarget))
+ movePoints = false;
+ } else {
+ firstLine.setP1(sceneTargetPoint(TransitionPoint::Start));
+ firstLine.setP1(findIntersectionPoint(m_startItem, firstLine, firstLine.p1()));
+ }
+ }
+
+ if (movePoints) {
+ if (m_movingFirstPoint && m_targetType == InternalNoTarget)
+ m_cornerPoints[1] = m_cornerPoints[0] + QPointF(20, 0);
+ else {
+ QPointF movingPoint = firstLine.p1() - m_cornerPoints[0];
+ for (int i = 0; i < m_cornerPoints.count(); ++i)
+ m_cornerPoints[i] += movingPoint;
+
+ for (int i = 0; i < m_arrow.count(); ++i)
+ m_arrow[i] += movingPoint;
+ }
+ }
+
+ m_cornerPoints[0] = firstLine.p1();
+
+ // Init last line
+ int lastIndex = m_cornerPoints.count() - 1;
+ QLineF lastLine(m_cornerPoints[lastIndex - 1], m_cornerPoints[lastIndex]);
+ if (m_endItem && m_targetType > InternalNoTarget) {
+ lastLine.setP2(sceneTargetPoint(TransitionPoint::End));
+ lastLine.setP2(findIntersectionPoint(m_endItem, lastLine, lastLine.p2()));
+ m_cornerPoints[lastIndex] = lastLine.p2();
+ }
+
+ m_arrowAngle = 0;
+ if (m_targetType == InternalSameTarget) {
+ m_arrowAngle = M_PI * 0.6;
+ } else {
+ // Calculate angle of the lastLine and update arrow
+ if (lastLine.length() > 0) {
+ m_arrowAngle = qAcos(lastLine.dx() / lastLine.length()) + M_PI;
+ if (lastLine.dy() >= 0)
+ m_arrowAngle = (2 * M_PI) - m_arrowAngle;
+ }
+ }
+
+ m_arrow[0] = lastLine.p2() + QPointF(qSin(m_arrowAngle + M_PI / 3) * m_arrowSize, qCos(m_arrowAngle + M_PI / 3) * m_arrowSize);
+ m_arrow[1] = lastLine.p2();
+ m_arrow[2] = lastLine.p2() + QPointF(qSin(m_arrowAngle + M_PI - M_PI / 3) * m_arrowSize, qCos(m_arrowAngle + M_PI - M_PI / 3) * m_arrowSize);
+
+ setItemBoundingRect(m_cornerPoints.boundingRect().normalized().adjusted(-SELECTION_DISTANCE, -SELECTION_DISTANCE,
+ SELECTION_DISTANCE, SELECTION_DISTANCE));
+
+ // Set the right place for the name of the transition
+ int ind = m_cornerPoints.count() / 2 - 1;
+ QLineF nameLine(m_cornerPoints[ind], m_cornerPoints[ind + 1]);
+ if (m_targetType <= InternalNoTarget) {
+ m_eventTagItem->setPos(m_cornerPoints[1].x() + 6, m_cornerPoints[1].y() - m_eventTagItem->boundingRect().height() / 3);
+ } else {
+ const qreal w2 = m_eventTagItem->boundingRect().width() / 2;
+ QPointF startPos = nameLine.pointAt(0.5);
+ QLineF targetLine(startPos, startPos + QPointF(SELECTION_DISTANCE, SELECTION_DISTANCE));
+ targetLine.setAngle(nameLine.angle() + 90);
+
+ m_eventTagItem->setPos(targetLine.p2() + m_eventTagItem->movePoint() - QPointF(w2, m_eventTagItem->boundingRect().height() / 2));
+ }
+
+ if (m_warningItem)
+ m_warningItem->setPos(m_eventTagItem->pos() - QPointF(WARNING_ITEM_SIZE, 0));
+ updateGrabberPositions();
+ updateZValue();
+}
+
+void TransitionItem::grabMouse(ItemType targetType)
+{
+ m_grabbedTargetType = targetType;
+ m_mouseGrabbed = true;
+ BaseItem::grabMouse();
+}
+
+void TransitionItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
+
+ painter->save();
+
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setPen(m_pen);
+
+ if (m_cornerPoints.count() >= 2) {
+ if (m_targetType == InternalSameTarget) {
+ QRectF rect(m_cornerPoints[0].x(), m_cornerPoints[0].y() - SELECTION_DISTANCE,
+ m_cornerPoints[1].x() - m_cornerPoints[0].x(),
+ SELECTION_DISTANCE * 2);
+ painter->drawArc(rect, 0, 180 * 16);
+ } else {
+ if (highlight()) {
+ painter->setPen(m_highlightPen);
+ painter->drawPolyline(m_cornerPoints);
+ }
+ painter->setPen(m_pen);
+ painter->drawPolyline(m_cornerPoints);
+ }
+ }
+
+ for (int i = 0; i < m_cornerPoints.count() - 1; ++i)
+ painter->drawEllipse(m_cornerPoints[i], 2, 2);
+
+ if (highlight()) {
+ painter->setPen(m_highlightPen);
+ painter->drawPolyline(m_arrow);
+ }
+
+ painter->setPen(m_pen);
+ painter->drawPolyline(m_arrow);
+
+ painter->restore();
+}
+
+void TransitionItem::updateEditorInfo(bool allChilds)
+{
+ BaseItem::updateEditorInfo(allChilds);
+
+ const QColor fontColor = editorInfo(Constants::C_SCXML_EDITORINFO_FONTCOLOR);
+ m_eventTagItem->setDefaultTextColor(fontColor.isValid() ? fontColor : Qt::black);
+
+ const QColor stateColor = editorInfo(Constants::C_SCXML_EDITORINFO_STATECOLOR);
+ m_pen.setColor(stateColor.isValid() ? stateColor : qRgb(0x12, 0x12, 0x12));
+}
+
+void TransitionItem::updateTarget()
+{
+ setTagValue("target", m_endItem ? m_endItem->itemId() : QString());
+ if (m_endItem)
+ m_endItem->checkInitial(true);
+}
+
+void TransitionItem::updateAttributes()
+{
+ BaseItem::updateAttributes();
+
+ // Find new target
+ if (m_endItem == nullptr || (m_endItem && tagValue("target") != m_endItem->itemId())) {
+ if (m_endItem)
+ m_endItem->removeInputTransition(this);
+
+ m_endItem = nullptr;
+ findEndItem();
+ updateTarget();
+ updateZValue();
+ }
+
+ // Set event-name
+ updateEventName();
+ updateTargetType();
+}
+
+void TransitionItem::init(ScxmlTag *tag, BaseItem *parentItem, bool initChildren, bool blockUpdates)
+{
+ Q_UNUSED(initChildren)
+ setBlockUpdates(blockUpdates);
+
+ setTag(tag);
+ setParentItem(parentItem);
+ updateEditorInfo();
+
+ if (blockUpdates)
+ setBlockUpdates(false);
+}
+
+void TransitionItem::readUISpecifiedProperties(const ScxmlTag *tag)
+{
+ if (tag) {
+ if (m_cornerPoints.count() >= 2) {
+ while (m_cornerPoints.count() > 2)
+ m_cornerPoints.takeAt(1);
+
+ Serializer s;
+
+ QPointF p = loadPoint(Constants::C_SCXML_EDITORINFO_STARTTARGETFACTORS);
+ if (p.isNull())
+ p = QPointF(50, 50);
+ m_startTargetFactor = p / 100;
+
+ p = loadPoint(Constants::C_SCXML_EDITORINFO_ENDTARGETFACTORS);
+ if (p.isNull())
+ p = QPointF(50, 50);
+ m_endTargetFactor = p / 100;
+
+ QString localPointsData = editorInfo(Constants::C_SCXML_EDITORINFO_LOCALGEOMETRY);
+ if (!localPointsData.isEmpty()) {
+ QPointF startPos = sceneTargetPoint(Start);
+ QPolygonF localPoints;
+ s.setData(localPointsData);
+ s.read(localPoints);
+ for (int i = 0; i < localPoints.count(); ++i)
+ m_cornerPoints.insert(i + 1, startPos + localPoints[i]);
+ } else {
+ QPolygonF scenePoints;
+ s.setData(editorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY));
+ s.read(scenePoints);
+ for (int i = 0; i < scenePoints.count(); ++i)
+ m_cornerPoints.insert(i + 1, scenePoints[i]);
+ }
+
+ m_eventTagItem->resetMovePoint(loadPoint(Constants::C_SCXML_EDITORINFO_MOVEPOINT));
+
+ if (m_lineSelected)
+ createGrabbers();
+ updateComponents();
+ }
+ }
+}
+
+void TransitionItem::finalizeCreation()
+{
+ bool old = blockUpdates();
+ setBlockUpdates(true);
+
+ updateAttributes();
+
+ if (!old)
+ setBlockUpdates(false);
+}
+
+void TransitionItem::checkVisibility(double scaleFactor)
+{
+ m_eventTagItem->setVisible(scaleFactor > 0.5);
+}
+
+bool TransitionItem::containsScenePoint(const QPointF &p) const
+{
+ QPointF pp = mapFromScene(p);
+
+ for (int i = 0; i < m_cornerPoints.count() - 1; ++i) {
+ QLineF line(m_cornerPoints[i], m_cornerPoints[i + 1]);
+
+ // Is point close enough of the line
+ QPointF intersPoint;
+ QLineF line2(pp, pp + QPointF(SELECTION_DISTANCE, SELECTION_DISTANCE));
+ line2.setAngle(line.angle() + 90);
+ if (line.intersect(line2, &intersPoint) == QLineF::BoundedIntersection) {
+ return true;
+ } else {
+ line2.setAngle(line.angle() - 90);
+ if (line.intersect(line2, &intersPoint) == QLineF::BoundedIntersection)
+ return true;
+ }
+ }
+ return false;
+}
+
+void TransitionItem::findEndItem()
+{
+ QString targetId = tagValue("target");
+ if (m_endItem == nullptr && !targetId.isEmpty()) {
+ QList<QGraphicsItem*> items = scene()->items();
+ for (int i = 0; i < items.count(); ++i) {
+ if (items[i]->type() >= FinalStateType) {
+ auto item = qgraphicsitem_cast<ConnectableItem*>(items[i]);
+ if (item && item->itemId() == targetId) {
+ setEndItem(item);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void TransitionItem::updateEventName()
+{
+ m_eventTagItem->setText(tagValue("event"));
+}
+
+void TransitionItem::storeGeometry(bool block)
+{
+ if (tag()) {
+ if (m_cornerPoints.count() <= 2) {
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY, QString(), block);
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_LOCALGEOMETRY, QString(), block);
+ } else {
+ QPolygonF pol = m_cornerPoints;
+ pol.takeFirst();
+ pol.takeLast();
+ Serializer s;
+ for (int i = 0; i < pol.count(); ++i) {
+ QPointF spos = sceneTargetPoint(Start);
+ pol[i].setX(pol[i].x() - spos.x());
+ pol[i].setY(pol[i].y() - spos.y());
+ }
+ s.append(pol);
+
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_LOCALGEOMETRY, s.data(), block);
+ }
+ }
+}
+
+void TransitionItem::storeMovePoint(bool block)
+{
+ if (m_eventTagItem->movePoint().toPoint() == QPoint(0, 0))
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_MOVEPOINT, QString(), block);
+ else
+ savePoint(m_eventTagItem->movePoint(), Constants::C_SCXML_EDITORINFO_MOVEPOINT);
+}
+
+void TransitionItem::storeTargetFactors(bool block)
+{
+ if (m_startTargetFactor == QPointF(0.5, 0.5))
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_STARTTARGETFACTORS, QString(), block);
+ else
+ savePoint(m_startTargetFactor * 100, Constants::C_SCXML_EDITORINFO_STARTTARGETFACTORS);
+
+ if (m_endTargetFactor == QPointF(0.5, 0.5))
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_ENDTARGETFACTORS, QString(), block);
+ else
+ savePoint(m_endTargetFactor * 100, Constants::C_SCXML_EDITORINFO_ENDTARGETFACTORS);
+}
+
+void TransitionItem::storeValues(bool block)
+{
+ storeGeometry(block);
+ storeMovePoint(block);
+ storeTargetFactors(block);
+}
+
+void TransitionItem::updateUIProperties()
+{
+ if (tag() != 0 && isActiveScene())
+ storeValues();
+}
+
+void TransitionItem::updateTargetType()
+{
+ if (m_movingFirstPoint && m_targetType == InternalNoTarget)
+ return;
+
+ TransitionTargetType type = ExternalTarget;
+
+ if (m_startItem != 0 && m_startItem == m_endItem)
+ type = InternalSameTarget;
+ else if (m_startItem != 0 && m_endItem == nullptr) {
+ if (m_movingLastPoint) {
+ type = ExternalNoTarget;
+ } else {
+ QRectF srect = m_startItem->sceneBoundingRect();
+ if (srect.contains(m_cornerPoints.last()))
+ type = InternalNoTarget;
+ else
+ type = ExternalNoTarget;
+ }
+ } else {
+ type = ExternalTarget;
+ }
+
+ if (type <= InternalNoTarget) {
+ m_eventTagItem->resetMovePoint();
+ m_arrowSize = 6;
+ // Remove extra points
+ while (m_cornerPoints.count() > 2)
+ m_cornerPoints.takeAt(1);
+ while (m_cornerGrabbers.count() > 2)
+ delete m_cornerGrabbers.takeAt(1);
+
+ // Remove editorinfo.geometry
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_GEOMETRY, QString(), true);
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_LOCALGEOMETRY, QString(), true);
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_MOVEPOINT, QString(), true);
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_STARTTARGETFACTORS, QString(), true);
+ setEditorInfo(Constants::C_SCXML_EDITORINFO_ENDTARGETFACTORS, QString(), true);
+ } else {
+ m_arrowSize = 10;
+ }
+
+ if (m_targetType != type) {
+ m_targetType = type;
+ if (m_warningItem)
+ m_warningItem->check();
+ if (m_startItem && !m_startItem->blockUpdates()) {
+ m_startItem->updateOutputTransitions();
+ if (m_startItem->type() >= StateType)
+ static_cast<StateItem*>(m_startItem)->updateBoundingRect();
+ }
+ }
+}
+
+ConnectableItem *TransitionItem::connectedItem(const ConnectableItem *other) const
+{
+ if (other) {
+ if (other == m_startItem)
+ return m_endItem;
+ else if (other == m_endItem)
+ return m_startItem;
+ }
+
+ return nullptr;
+}
+
+bool TransitionItem::isStartItem(const ConnectableItem *item) const
+{
+ return m_startItem == item;
+}
+
+bool TransitionItem::isEndItem(const ConnectableItem *item) const
+{
+ return m_endItem == item;
+}
+
+bool TransitionItem::hasStartItem() const
+{
+ return m_startItem != nullptr;
+}
+
+bool TransitionItem::hasEndItem() const
+{
+ return m_endItem != nullptr;
+}
+
+void TransitionItem::updateZValue()
+{
+ setZValue(qMax(m_startItem ? (m_startItem->zValue() + 1) : 1, m_endItem ? (m_endItem->zValue() + 1) : 1));
+}
+
+qreal TransitionItem::textWidth() const
+{
+ return m_eventTagItem->boundingRect().width();
+}
+
+QRectF TransitionItem::wholeBoundingRect() const
+{
+ return boundingRect().united(m_eventTagItem->sceneBoundingRect());
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/transitionitem.h b/src/plugins/scxmleditor/plugin_interface/transitionitem.h
new file mode 100644
index 00000000000..b1e4279fb97
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/transitionitem.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "transitionwarningitem.h"
+#include <QGraphicsSceneMouseEvent>
+#include <QGraphicsTextItem>
+#include <QKeyEvent>
+#include <QPen>
+#include <QPointF>
+#include <QPolygon>
+#include <QRectF>
+#include <QVector>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class CornerGrabberItem;
+class TagTextItem;
+class ConnectableItem;
+
+/**
+ * @brief The TransitionItem class provides the item to connect two Connectable-items.
+ */
+class TransitionItem : public BaseItem
+{
+ Q_OBJECT
+
+public:
+ explicit TransitionItem(BaseItem *parent = nullptr);
+ ~TransitionItem() override;
+
+ enum TransitionTargetType {
+ InternalSameTarget = 0,
+ InternalNoTarget,
+ ExternalNoTarget,
+ ExternalTarget
+ };
+
+ enum TransitionPoint {
+ Start = 0,
+ End
+ };
+
+ /**
+ * @brief type - return tye type of the item.
+ */
+ int type() const override
+ {
+ return TransitionType;
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
+ bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override;
+
+ void disconnectItem(ConnectableItem *item);
+ void setStartItem(ConnectableItem *item);
+ void setEndItem(ConnectableItem *item);
+
+ void startTransitionFrom(ConnectableItem *item, const QPointF &mouseScenePos);
+ void setEndPos(const QPointF &endPos, bool snap = true);
+ void connectToTopItem(const QPointF &pos, TransitionPoint p, ItemType targetType);
+
+ bool isStartItem(const ConnectableItem *item) const;
+ bool isEndItem(const ConnectableItem *item) const;
+ ConnectableItem *connectedItem(const ConnectableItem *other) const;
+
+ bool hasStartItem() const;
+ bool hasEndItem() const;
+
+ void init(ScxmlTag *tag, BaseItem *parentItem = nullptr, bool initChildren = true, bool blockUpdates = false) override;
+ void updateEditorInfo(bool allChilds = true) override;
+ void updateAttributes() override;
+ void updateTarget();
+ void finalizeCreation() override;
+ void checkVisibility(double scaleFactor) override;
+ bool containsScenePoint(const QPointF &p) const override;
+
+ void updateUIProperties() override;
+ void updateTargetType();
+ void setTag(ScxmlTag *tag) override;
+ qreal textWidth() const;
+ QRectF wholeBoundingRect() const;
+
+ TransitionTargetType targetType() const
+ {
+ return m_targetType;
+ }
+
+ void grabMouse(ItemType targetType);
+ void storeGeometry(bool block = false);
+ void storeValues(bool block = false);
+ void updateComponents();
+ void textItemPositionChanged();
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void updateToolTip();
+ void readUISpecifiedProperties(const ScxmlTag *tag) override;
+ void checkSelectionBeforeContextMenu(QGraphicsSceneMouseEvent *e) override;
+ void createContextMenu(QMenu *menu) override;
+ void selectedMenuAction(const QAction *action) override;
+
+private:
+ void textHasChanged(const QString &text);
+ void checkWarningItems();
+ void storeMovePoint(bool block = false);
+ void storeTargetFactors(bool block = false);
+ void updateZValue();
+ void removeUnnecessaryPoints();
+ void findEndItem();
+ void updateEventName();
+ void createGrabbers();
+ void removeGrabbers();
+ void updateGrabberPositions();
+ void removeTransition(TransitionPoint p);
+ void snapToAnyPoint(int index, const QPointF &newPoint, int diff = 8);
+ void snapPointToPoint(int index, const QPointF &newPoint, int diff = 8);
+ QPointF loadPoint(const QString &name);
+ void savePoint(const QPointF &p, const QString &name);
+ QPointF calculateTargetFactor(ConnectableItem *item, const QPointF &pos);
+ QPointF sceneTargetPoint(TransitionPoint p);
+ QPointF findIntersectionPoint(ConnectableItem *item, const QLineF &line, const QPointF &defaultPoint);
+ QVector<CornerGrabberItem*> m_cornerGrabbers;
+ CornerGrabberItem *m_selectedCornerGrabber = nullptr;
+
+ QPolygonF m_cornerPoints;
+
+ ConnectableItem *m_startItem = nullptr;
+ ConnectableItem *m_oldStartItem = nullptr;
+ ConnectableItem *m_endItem = nullptr;
+
+ QPolygonF m_arrow;
+ qreal m_arrowSize = 10;
+ qreal m_arrowAngle;
+ QPen m_pen;
+ QPen m_highlightPen;
+ bool m_lineSelected = false;
+
+ TagTextItem *m_eventTagItem;
+ TransitionWarningItem *m_warningItem = nullptr;
+ TransitionTargetType m_targetType = ExternalTarget;
+ bool m_movingFirstPoint = false;
+ bool m_movingLastPoint = false;
+ bool m_mouseGrabbed = false;
+ ItemType m_grabbedTargetType = UnknownType;
+ int m_selectedGrabberIndex = -1;
+ QPointF m_startTargetFactor;
+ QPointF m_endTargetFactor;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/transitionwarningitem.cpp b/src/plugins/scxmleditor/plugin_interface/transitionwarningitem.cpp
new file mode 100644
index 00000000000..b92cd8ef8dd
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/transitionwarningitem.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "transitionwarningitem.h"
+#include "transitionitem.h"
+
+#include <utils/utilsicons.h>
+
+using namespace ScxmlEditor::PluginInterface;
+
+TransitionWarningItem::TransitionWarningItem(TransitionItem *parent)
+ : WarningItem(parent)
+ , m_parentItem(parent)
+{
+ setSeverity(OutputPane::Warning::WarningType);
+ setTypeName(tr("Transition"));
+ setDescription(tr("Transitions should be connected."));
+
+ setPixmap(Utils::Icons::WARNING.pixmap());
+}
+
+void TransitionWarningItem::check()
+{
+ if (m_parentItem) {
+ if (m_parentItem->targetType() == TransitionItem::ExternalNoTarget) {
+ setReason(tr("Not Connected (%1)").arg(m_parentItem->tagValue("event")));
+ setWarningActive(true);
+ } else
+ setWarningActive(false);
+ }
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/transitionwarningitem.h b/src/plugins/scxmleditor/plugin_interface/transitionwarningitem.h
new file mode 100644
index 00000000000..a0a5f5c3266
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/transitionwarningitem.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "warningitem.h"
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class TransitionItem;
+
+/**
+ * @brief The TransitionWarningItem class provides the warning item for the check Transition connections.
+ */
+class TransitionWarningItem : public WarningItem
+{
+ Q_OBJECT
+
+public:
+ TransitionWarningItem(TransitionItem *parent = nullptr);
+
+ int type() const override
+ {
+ return TransitionWarningType;
+ }
+
+ void check() override;
+
+private:
+ TransitionItem *m_parentItem;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/undocommands.cpp b/src/plugins/scxmleditor/plugin_interface/undocommands.cpp
new file mode 100644
index 00000000000..0a729cb80ca
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/undocommands.cpp
@@ -0,0 +1,403 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "undocommands.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+BaseUndoCommand::BaseUndoCommand(ScxmlDocument *doc, QUndoCommand *parent)
+ : QUndoCommand(parent)
+ , m_doc(doc)
+{
+}
+
+void BaseUndoCommand::undo()
+{
+ m_doc->setUndoRedoRunning(true);
+ doUndo();
+ m_doc->setUndoRedoRunning(false);
+}
+
+void BaseUndoCommand::redo()
+{
+ m_doc->setUndoRedoRunning(!m_firstTime);
+ doRedo();
+ if (m_firstTime)
+ m_firstTime = false;
+ m_doc->setUndoRedoRunning(false);
+}
+
+// AddRemoveTagsBeginCommand
+AddRemoveTagsBeginCommand::AddRemoveTagsBeginCommand(ScxmlDocument *doc, ScxmlTag *tag, QUndoCommand *parent)
+ : BaseUndoCommand(doc, parent)
+ , m_document(doc)
+ , m_tag(tag)
+{
+}
+
+void AddRemoveTagsBeginCommand::doUndo()
+{
+ emit m_document->endTagChange(ScxmlDocument::TagRemoveTags, m_tag, m_tag->index());
+}
+void AddRemoveTagsBeginCommand::doRedo()
+{
+ emit m_document->beginTagChange(ScxmlDocument::TagAddTags, m_tag, m_tag->index());
+}
+
+// AddRemoveTagsEndCommand
+AddRemoveTagsEndCommand::AddRemoveTagsEndCommand(ScxmlDocument *doc, ScxmlTag *tag, QUndoCommand *parent)
+ : BaseUndoCommand(doc, parent)
+ , m_document(doc)
+ , m_tag(tag)
+{
+}
+
+void AddRemoveTagsEndCommand::doUndo()
+{
+ emit m_document->beginTagChange(ScxmlDocument::TagRemoveTags, m_tag, m_tag->index());
+}
+void AddRemoveTagsEndCommand::doRedo()
+{
+ emit m_document->endTagChange(ScxmlDocument::TagAddTags, m_tag, m_tag->index());
+}
+
+// AddRemoveTagCommand
+AddRemoveTagCommand::AddRemoveTagCommand(ScxmlDocument *doc, ScxmlTag *parentTag, ScxmlTag *tag, ScxmlDocument::TagChange change, QUndoCommand *parent)
+ : BaseUndoCommand(doc, parent)
+ , m_document(doc)
+ , m_tag(tag)
+ , m_parentTag(parentTag)
+ , m_change(change)
+{
+ m_tag->setDocument(m_document);
+}
+
+AddRemoveTagCommand::~AddRemoveTagCommand()
+{
+ // If type was ADD, then delete tag
+ if (m_change == ScxmlDocument::TagAddChild)
+ delete m_tag;
+}
+
+void AddRemoveTagCommand::doAction(bool add)
+{
+ if (add) {
+ int r = m_parentTag->childIndex(m_tag);
+ if (r < 0)
+ r = m_parentTag->childCount();
+ emit m_document->beginTagChange(ScxmlDocument::TagAddChild, m_parentTag, QVariant(r));
+ m_parentTag->appendChild(m_tag);
+ emit m_document->endTagChange(ScxmlDocument::TagAddChild, m_parentTag, QVariant(r));
+ } else {
+ int r = m_parentTag->childIndex(m_tag);
+ if (r >= 0) {
+ emit m_document->beginTagChange(ScxmlDocument::TagRemoveChild, m_parentTag, QVariant(r));
+ m_parentTag->removeChild(m_tag);
+ emit m_document->endTagChange(ScxmlDocument::TagRemoveChild, m_parentTag, QVariant(r));
+ }
+ }
+}
+
+void AddRemoveTagCommand::doUndo()
+{
+ doAction(m_change != ScxmlDocument::TagAddChild);
+}
+void AddRemoveTagCommand::doRedo()
+{
+ doAction(m_change == ScxmlDocument::TagAddChild);
+}
+
+// SetAttributeCommand
+SetAttributeCommand::SetAttributeCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &key, const QString &value, QUndoCommand *parent)
+ : BaseUndoCommand(doc, parent)
+ , m_document(doc)
+ , m_tag(tag)
+ , m_key(key)
+ , m_value(value)
+{
+ m_oldValue = m_tag->attribute(m_key);
+}
+
+void SetAttributeCommand::doAction(const QString &key, const QString &value)
+{
+ emit m_document->beginTagChange(ScxmlDocument::TagAttributesChanged, m_tag, m_tag->attribute(key));
+ m_tag->setAttribute(key, value);
+ emit m_document->endTagChange(ScxmlDocument::TagAttributesChanged, m_tag, value);
+}
+
+int SetAttributeCommand::id() const
+{
+ return (int)ScxmlDocument::TagAttributesChanged;
+}
+bool SetAttributeCommand::mergeWith(const QUndoCommand *other)
+{
+ if (other->id() == id()) {
+ QString key = static_cast<const SetAttributeCommand*>(other)->m_key;
+ auto tag = static_cast<const SetAttributeCommand*>(other)->m_tag;
+ if (tag == m_tag && key == m_key) {
+ m_value = static_cast<const SetAttributeCommand*>(other)->m_value;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SetAttributeCommand::doUndo()
+{
+ doAction(m_key, m_oldValue);
+}
+void SetAttributeCommand::doRedo()
+{
+ doAction(m_key, m_value);
+}
+
+// SetContentCommand
+SetContentCommand::SetContentCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &content, QUndoCommand *parent)
+ : BaseUndoCommand(doc, parent)
+ , m_document(doc)
+ , m_tag(tag)
+ , m_content(content)
+{
+ m_oldContent = m_tag->content();
+}
+
+void SetContentCommand::doAction(const QString &content)
+{
+ emit m_document->beginTagChange(ScxmlDocument::TagContentChanged, m_tag, m_tag->content());
+ m_tag->setContent(content);
+ emit m_document->endTagChange(ScxmlDocument::TagContentChanged, m_tag, content);
+}
+
+int SetContentCommand::id() const
+{
+ return (int)ScxmlDocument::TagContentChanged;
+}
+bool SetContentCommand::mergeWith(const QUndoCommand *other)
+{
+ if (other->id() == id()) {
+ auto tag = static_cast<const SetContentCommand*>(other)->m_tag;
+ if (tag == m_tag) {
+ m_content = static_cast<const SetContentCommand*>(other)->m_content;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SetContentCommand::doUndo()
+{
+ doAction(m_oldContent);
+}
+void SetContentCommand::doRedo()
+{
+ doAction(m_content);
+}
+
+// SetEditorInfoCommand
+SetEditorInfoCommand::SetEditorInfoCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &key, const QString &value, QUndoCommand *parent)
+ : BaseUndoCommand(doc, parent)
+ , m_document(doc)
+ , m_tag(tag)
+ , m_key(key)
+ , m_value(value)
+{
+ m_oldValue = m_tag->editorInfo(m_key);
+}
+
+void SetEditorInfoCommand::doAction(const QString &key, const QString &value)
+{
+ emit m_document->beginTagChange(ScxmlDocument::TagEditorInfoChanged, m_tag, m_tag->editorInfo(key));
+ m_tag->setEditorInfo(key, value);
+ emit m_document->endTagChange(ScxmlDocument::TagEditorInfoChanged, m_tag, value);
+}
+
+int SetEditorInfoCommand::id() const
+{
+ return (int)ScxmlDocument::TagEditorInfoChanged;
+}
+bool SetEditorInfoCommand::mergeWith(const QUndoCommand *other)
+{
+ if (other->id() == id()) {
+ QString key = static_cast<const SetEditorInfoCommand*>(other)->m_key;
+ auto tag = static_cast<const SetEditorInfoCommand*>(other)->m_tag;
+ if (tag == m_tag && key == m_key) {
+ m_value = static_cast<const SetEditorInfoCommand*>(other)->m_value;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SetEditorInfoCommand::doUndo()
+{
+ doAction(m_key, m_oldValue);
+}
+void SetEditorInfoCommand::doRedo()
+{
+ doAction(m_key, m_value);
+}
+
+// ChangeParentCommand
+ChangeParentCommand::ChangeParentCommand(ScxmlDocument *doc, ScxmlTag *tag, ScxmlTag *newParentTag, int tagIndex, QUndoCommand *parent)
+ : BaseUndoCommand(doc, parent)
+ , m_document(doc)
+ , m_tag(tag)
+ , m_newParentTag(newParentTag)
+ , m_tagIndex(tagIndex)
+{
+ m_oldParentTag = m_tag->parentTag();
+}
+
+void ChangeParentCommand::doAction(ScxmlTag *oldParent, ScxmlTag *newParent)
+{
+ emit m_document->beginTagChange(ScxmlDocument::TagChangeParent, m_tag, m_tag->index());
+
+ int r = oldParent->childIndex(m_tag);
+ emit m_document->beginTagChange(ScxmlDocument::TagChangeParentRemoveChild, oldParent, QVariant(r));
+ oldParent->removeChild(m_tag);
+ emit m_document->endTagChange(ScxmlDocument::TagChangeParentRemoveChild, oldParent, QVariant(r));
+
+ r = newParent->childCount();
+ emit m_document->beginTagChange(ScxmlDocument::TagChangeParentAddChild, newParent, QVariant(r));
+ newParent->insertChild(m_tagIndex, m_tag);
+ emit m_document->endTagChange(ScxmlDocument::TagChangeParentAddChild, newParent, QVariant(r));
+
+ emit m_document->endTagChange(ScxmlDocument::TagChangeParent, m_tag, m_tag->index());
+}
+
+void ChangeParentCommand::doUndo()
+{
+ doAction(m_newParentTag, m_oldParentTag);
+}
+void ChangeParentCommand::doRedo()
+{
+ doAction(m_oldParentTag, m_newParentTag);
+}
+
+ChangeOrderCommand::ChangeOrderCommand(ScxmlDocument *doc, ScxmlTag *tag, ScxmlTag *parentTag, int newPos, QUndoCommand *parent)
+ : BaseUndoCommand(doc, parent)
+ , m_document(doc)
+ , m_tag(tag)
+ , m_parentTag(parentTag)
+ , m_newPos(newPos)
+{
+ m_oldPos = m_tag->index();
+}
+
+void ChangeOrderCommand::doAction(int newPos)
+{
+ emit m_document->beginTagChange(ScxmlDocument::TagChangeOrder, m_tag, newPos);
+ m_parentTag->moveChild(m_tag->index(), newPos);
+ emit m_document->endTagChange(ScxmlDocument::TagChangeOrder, m_tag, m_tag->index());
+}
+
+void ChangeOrderCommand::doUndo()
+{
+ doAction(m_oldPos);
+}
+void ChangeOrderCommand::doRedo()
+{
+ doAction(m_newPos);
+}
+
+ChangeFullNameSpaceCommand::ChangeFullNameSpaceCommand(ScxmlDocument *doc, ScxmlTag *tag, bool state, QUndoCommand *parent)
+ : BaseUndoCommand(doc, parent)
+ , m_document(doc)
+ , m_rootTag(tag)
+ , m_newState(state)
+{
+ m_oldState = !state;
+}
+
+void ChangeFullNameSpaceCommand::makeIdMap(ScxmlTag *tag, QHash<QString, QString> &map, bool use)
+{
+ switch (tag->tagType()) {
+ case History:
+ case Final:
+ case State:
+ case Parallel: {
+ QString strId = tag->attribute("id");
+ QString strIdWithNS = QString::fromLatin1("%1%2").arg(tag->stateNameSpace()).arg(strId);
+ map[use ? strId : strIdWithNS] = use ? strIdWithNS : strId;
+ break;
+ }
+ default:
+ break;
+ }
+
+ foreach (ScxmlTag *child, tag->allChildren()) {
+ makeIdMap(child, map, use);
+ }
+}
+
+void ChangeFullNameSpaceCommand::updateNameSpace(ScxmlTag *tag, const QHash<QString, QString> &map)
+{
+ QString name;
+ switch (tag->tagType()) {
+ case Scxml:
+ case State:
+ name = "initial";
+ break;
+ case Transition:
+ name = "target";
+ break;
+ default:
+ break;
+ }
+
+ if (!name.isEmpty()) {
+ QString attr = tag->attribute(name);
+ if (map.contains(attr))
+ tag->setAttribute(name, map[attr]);
+ }
+
+ foreach (ScxmlTag *child, tag->allChildren()) {
+ updateNameSpace(child, map);
+ }
+}
+
+void ChangeFullNameSpaceCommand::doAction(bool newState)
+{
+ emit m_document->beginTagChange(ScxmlDocument::TagChangeFullNameSpace, m_rootTag, newState);
+
+ QHash<QString, QString> keyMap;
+ makeIdMap(m_rootTag, keyMap, newState);
+ updateNameSpace(m_rootTag, keyMap);
+ m_document->m_useFullNameSpace = newState;
+
+ emit m_document->endTagChange(ScxmlDocument::TagChangeFullNameSpace, m_rootTag, newState);
+}
+
+void ChangeFullNameSpaceCommand::doUndo()
+{
+ doAction(m_oldState);
+}
+void ChangeFullNameSpaceCommand::doRedo()
+{
+ doAction(m_newState);
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/undocommands.h b/src/plugins/scxmleditor/plugin_interface/undocommands.h
new file mode 100644
index 00000000000..28085c7f9ad
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/undocommands.h
@@ -0,0 +1,232 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmldocument.h"
+#include "scxmltag.h"
+#include <QUndoCommand>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class BaseUndoCommand : public QUndoCommand
+{
+public:
+ BaseUndoCommand(ScxmlDocument *doc, QUndoCommand *parent = nullptr);
+
+ void undo() override;
+ void redo() override;
+
+protected:
+ virtual void doUndo() = 0;
+ virtual void doRedo() = 0;
+
+private:
+ ScxmlDocument *m_doc;
+ bool m_firstTime = true;
+};
+
+class AddRemoveTagsBeginCommand : public BaseUndoCommand
+{
+public:
+ AddRemoveTagsBeginCommand(ScxmlDocument *doc, ScxmlTag *tag, QUndoCommand *parent = nullptr);
+
+ void doUndo() override;
+ void doRedo() override;
+
+private:
+ ScxmlDocument *m_document;
+ ScxmlTag *m_tag;
+};
+
+class AddRemoveTagsEndCommand : public BaseUndoCommand
+{
+public:
+ AddRemoveTagsEndCommand(ScxmlDocument *doc, ScxmlTag *tag, QUndoCommand *parent = nullptr);
+
+ void doUndo() override;
+ void doRedo() override;
+
+private:
+ ScxmlDocument *m_document;
+ ScxmlTag *m_tag;
+};
+
+/**
+ * @brief The AddRemoveTagCommand class provides the undo-command to add/remove tag to/from SCXML-document.
+ */
+class AddRemoveTagCommand : public BaseUndoCommand
+{
+public:
+ AddRemoveTagCommand(ScxmlDocument *doc, ScxmlTag *parentTag, ScxmlTag *tag, ScxmlDocument::TagChange change, QUndoCommand *parent = nullptr);
+ ~AddRemoveTagCommand() override;
+
+ void doUndo() override;
+ void doRedo() override;
+
+private:
+ void doAction(bool add);
+ ScxmlDocument *m_document;
+ QPointer<ScxmlTag> m_tag;
+ QPointer<ScxmlTag> m_parentTag;
+ ScxmlDocument::TagChange m_change;
+};
+
+/**
+ * @brief The SetAttributeCommand class provides the undo-command to set attribute-value for the given ScxmlTag.
+ */
+class SetAttributeCommand : public BaseUndoCommand
+{
+public:
+ SetAttributeCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &key, const QString &value, QUndoCommand *parent = nullptr);
+
+ void doUndo() override;
+ void doRedo() override;
+
+ bool mergeWith(const QUndoCommand *other) override;
+ int id() const override;
+
+private:
+ void doAction(const QString &key, const QString &value);
+ ScxmlDocument *m_document;
+ QPointer<ScxmlTag> m_tag;
+ QString m_key;
+ QString m_value;
+ QString m_oldValue;
+};
+
+/**
+ * @brief The SetContentCommand class
+ */
+class SetContentCommand : public BaseUndoCommand
+{
+public:
+ SetContentCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &content, QUndoCommand *parent = nullptr);
+
+ void doUndo() override;
+ void doRedo() override;
+
+ bool mergeWith(const QUndoCommand *other) override;
+ int id() const override;
+
+private:
+ void doAction(const QString &content);
+ ScxmlDocument *m_document;
+ QPointer<ScxmlTag> m_tag;
+ QString m_content;
+ QString m_oldContent;
+};
+
+/**
+ * @brief The SetEditorInfoCommand class provides the undo-command to set editorinfo-value for the given ScxmlTag.
+ */
+class SetEditorInfoCommand : public BaseUndoCommand
+{
+public:
+ SetEditorInfoCommand(ScxmlDocument *doc, ScxmlTag *tag, const QString &key, const QString &value, QUndoCommand *parent = nullptr);
+
+ void doUndo() override;
+ void doRedo() override;
+
+ bool mergeWith(const QUndoCommand *other) override;
+ int id() const override;
+
+private:
+ void doAction(const QString &key, const QString &value);
+ ScxmlDocument *m_document;
+ QPointer<ScxmlTag> m_tag;
+ QString m_key;
+ QString m_value;
+ QString m_oldValue;
+};
+
+/**
+ * @brief The ChangeParentCommand class provides the undo-commant to change parent of the ScxmlTag.
+ */
+class ChangeParentCommand : public BaseUndoCommand
+{
+public:
+ ChangeParentCommand(ScxmlDocument *doc, ScxmlTag *tag, ScxmlTag *newParentTag, int tagIndex, QUndoCommand *parent = nullptr);
+
+ void doUndo() override;
+ void doRedo() override;
+
+private:
+ void doAction(ScxmlTag *oldParent, ScxmlTag *newParent);
+
+ ScxmlDocument *m_document;
+ QPointer<ScxmlTag> m_tag;
+ QPointer<ScxmlTag> m_newParentTag;
+ QPointer<ScxmlTag> m_oldParentTag;
+ int m_tagIndex;
+};
+
+/**
+ * @brief The ChangeOrderCommand class
+ */
+class ChangeOrderCommand : public BaseUndoCommand
+{
+public:
+ ChangeOrderCommand(ScxmlDocument *doc, ScxmlTag *tag, ScxmlTag *parentTag, int newPos, QUndoCommand *parent = nullptr);
+
+ void doUndo() override;
+ void doRedo() override;
+
+private:
+ void doAction(int newPos);
+
+ ScxmlDocument *m_document;
+ QPointer<ScxmlTag> m_tag;
+ QPointer<ScxmlTag> m_parentTag;
+ int m_oldPos;
+ int m_newPos;
+};
+
+/**
+ * @brief The ChangeNameSpaceCommand class
+ */
+class ChangeFullNameSpaceCommand : public BaseUndoCommand
+{
+public:
+ ChangeFullNameSpaceCommand(ScxmlDocument *doc, ScxmlTag *tag, bool state, QUndoCommand *parent = nullptr);
+
+ void doUndo() override;
+ void doRedo() override;
+
+private:
+ void doAction(bool state);
+ void makeIdMap(ScxmlTag *tag, QHash<QString, QString> &map, bool use);
+ void updateNameSpace(ScxmlTag *tag, const QHash<QString, QString> &map);
+
+ ScxmlDocument *m_document;
+ QPointer<ScxmlTag> m_rootTag;
+ bool m_oldState;
+ bool m_newState;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/utilsprovider.cpp b/src/plugins/scxmleditor/plugin_interface/utilsprovider.cpp
new file mode 100644
index 00000000000..ffda8870cca
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/utilsprovider.cpp
@@ -0,0 +1,33 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "utilsprovider.h"
+
+using namespace ScxmlEditor::PluginInterface;
+
+UtilsProvider::UtilsProvider(QObject *parent)
+ : QObject(parent)
+{
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/utilsprovider.h b/src/plugins/scxmleditor/plugin_interface/utilsprovider.h
new file mode 100644
index 00000000000..5b4fd848452
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/utilsprovider.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QGraphicsItem>
+#include <QList>
+#include <QObject>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+class ScxmlTag;
+
+class UtilsProvider : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit UtilsProvider(QObject *parent = nullptr);
+
+ virtual void checkInitialState(const QList<QGraphicsItem*> &items, ScxmlTag *tag) = 0;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/warningitem.cpp b/src/plugins/scxmleditor/plugin_interface/warningitem.cpp
new file mode 100644
index 00000000000..79d00611e39
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/warningitem.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "warningitem.h"
+#include "graphicsscene.h"
+#include "warningmodel.h"
+
+#include <utils/utilsicons.h>
+
+#include <QPainter>
+#include <QToolTip>
+
+using namespace ScxmlEditor::PluginInterface;
+
+WarningItem::WarningItem(QGraphicsItem *parent)
+ : QGraphicsObject(parent)
+ , m_parentItem(qgraphicsitem_cast<BaseItem*>(parent))
+{
+ setPixmap(Utils::Icons::WARNING.pixmap());
+
+ auto sc = static_cast<GraphicsScene*>(scene());
+ if (sc) {
+ sc->addWarningItem(this);
+ m_warningModel = sc->warningModel();
+ connect(m_warningModel, &OutputPane::WarningModel::modelCleared, this, &WarningItem::check);
+ }
+ setWarningActive(false);
+}
+
+WarningItem::~WarningItem()
+{
+ auto sc = static_cast<GraphicsScene*>(scene());
+ if (sc) {
+ sc->removeWarningItem(this);
+ delete m_warning;
+ m_warning = nullptr;
+ }
+}
+
+void WarningItem::check()
+{
+}
+
+QRectF WarningItem::boundingRect() const
+{
+ return QRectF(QPointF(), m_pixmap.size() * m_pixmap.devicePixelRatio());
+}
+
+void WarningItem::mousePressEvent(QGraphicsSceneMouseEvent *e)
+{
+ QToolTip::showText(e->screenPos(), toolTip(), 0);
+ QGraphicsObject::mousePressEvent(e);
+}
+
+QVariant WarningItem::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ switch (change) {
+ case ItemSceneHasChanged: {
+ auto sc = static_cast<GraphicsScene*>(scene());
+ if (sc) {
+ sc->addWarningItem(this);
+ m_warningModel = sc->warningModel();
+ connect(m_warningModel, &OutputPane::WarningModel::modelCleared, this, &WarningItem::check);
+ }
+ break;
+ }
+ case QGraphicsItem::ItemVisibleHasChanged: {
+ auto sc = static_cast<GraphicsScene*>(scene());
+ if (sc)
+ sc->warningVisibilityChanged(m_severity, this);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return QGraphicsObject::itemChange(change, value);
+}
+
+void WarningItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
+
+ painter->drawPixmap(0, 0, m_pixmap);
+}
+
+void WarningItem::setReason(const QString &reason)
+{
+ m_reason = reason;
+ if (m_warning)
+ m_warning->setReason(reason);
+
+ setToolTip(m_reason);
+}
+
+void WarningItem::setDescription(const QString &description)
+{
+ m_description = description;
+ if (m_warning)
+ m_warning->setDescription(description);
+}
+
+void WarningItem::setSeverity(ScxmlEditor::OutputPane::Warning::Severity type)
+{
+ m_severity = type;
+ if (m_warning)
+ m_warning->setSeverity(type);
+}
+
+void WarningItem::setTypeName(const QString &name)
+{
+ m_typeName = name;
+ if (m_warning)
+ m_warning->setTypeName(name);
+}
+
+void WarningItem::setWarningActive(bool active)
+{
+ if (active && m_warning == nullptr && m_warningModel) {
+ m_warning = m_warningModel->createWarning(m_severity, m_typeName, m_reason, m_description);
+ connect(m_warning, &OutputPane::Warning::dataChanged, this, &WarningItem::checkVisibility);
+ } else if (!active && m_warning) {
+ m_warning->deleteLater();
+ m_warning = nullptr;
+ }
+
+ checkVisibility();
+}
+
+void WarningItem::checkVisibility()
+{
+ setVisible(m_warning && m_warning->isActive());
+}
+
+void WarningItem::setPixmap(const QPixmap &pixmap)
+{
+ m_pixmap = pixmap.scaled(QSize(25, 25) * pixmap.devicePixelRatio());
+}
+
+ScxmlTag *WarningItem::tag() const
+{
+ return m_parentItem ? m_parentItem->tag() : 0;
+}
+
+ScxmlEditor::OutputPane::Warning *WarningItem::warning() const
+{
+ return m_warning;
+}
diff --git a/src/plugins/scxmleditor/plugin_interface/warningitem.h b/src/plugins/scxmleditor/plugin_interface/warningitem.h
new file mode 100644
index 00000000000..b7a398df39b
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/warningitem.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "baseitem.h"
+#include "warning.h"
+#include "warningmodel.h"
+#include <QGraphicsObject>
+#include <QGraphicsSceneMouseEvent>
+#include <QPixmap>
+#include <QPointer>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+/**
+ * @brief The WarningItem class is a base class for the all scene-based Warning-items.
+ *
+ * When status changes, warning must inform it to scene. Scene will check the other warnings too.
+ */
+class WarningItem : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+ explicit WarningItem(QGraphicsItem *parent = nullptr);
+ ~WarningItem() override;
+
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+ QRectF boundingRect() const override;
+ ScxmlTag *tag() const;
+
+ void setSeverity(OutputPane::Warning::Severity type);
+ void setTypeName(const QString &name);
+ void setDescription(const QString &description);
+ OutputPane::Warning *warning() const;
+
+ void setReason(const QString &reason);
+ void setPixmap(const QPixmap &pixmap);
+ void setWarningActive(bool active);
+ virtual void check();
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *e) override;
+
+private:
+ void checkVisibility();
+
+ OutputPane::Warning::Severity m_severity = OutputPane::Warning::ErrorType;
+ QString m_typeName;
+ QString m_description;
+ QString m_reason;
+ QPixmap m_pixmap;
+ BaseItem *m_parentItem;
+ QPointer<OutputPane::Warning> m_warning;
+ QPointer<OutputPane::WarningModel> m_warningModel;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/warningprovider.h b/src/plugins/scxmleditor/plugin_interface/warningprovider.h
new file mode 100644
index 00000000000..65536b8b49d
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/warningprovider.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QObject>
+
+namespace ScxmlEditor {
+
+namespace OutputPane {
+class WarningModel;
+} // namespace OutputPane
+
+namespace PluginInterface {
+
+class ScxmlTag;
+
+class WarningProvider : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit WarningProvider(QObject *parent = nullptr)
+ : QObject(parent)
+ {
+ }
+
+ virtual void init(OutputPane::WarningModel *model) = 0;
+};
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/resources.qrc b/src/plugins/scxmleditor/resources.qrc
new file mode 100644
index 00000000000..0a8382e3fe9
--- /dev/null
+++ b/src/plugins/scxmleditor/resources.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/scxmleditor">
+ <file>ScxmlEditor.mimetypes.xml</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/scxmleditor/scxmlcontext.cpp b/src/plugins/scxmleditor/scxmlcontext.cpp
new file mode 100644
index 00000000000..57b926da5b4
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmlcontext.cpp
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmlcontext.h"
+
+using namespace ScxmlEditor::Internal;
+
+ScxmlContext::ScxmlContext(const Core::Context &context, QWidget *widget, QObject *parent)
+ : Core::IContext(parent)
+{
+ setContext(context);
+ setWidget(widget);
+}
diff --git a/src/plugins/scxmleditor/scxmlcontext.h b/src/plugins/scxmleditor/scxmlcontext.h
new file mode 100644
index 00000000000..0765ce37b1b
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmlcontext.h
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <coreplugin/icontext.h>
+
+namespace ScxmlEditor {
+namespace Internal {
+
+class ScxmlContext : public Core::IContext
+{
+public:
+ explicit ScxmlContext(const Core::Context &contexts, QWidget *widget, QObject *parent = nullptr);
+};
+
+} // namespace Internal
+} // namespace Designer
diff --git a/src/plugins/scxmleditor/scxmleditor.pro b/src/plugins/scxmleditor/scxmleditor.pro
new file mode 100644
index 00000000000..4de417ae7eb
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditor.pro
@@ -0,0 +1,30 @@
+include(../../qtcreatorplugin.pri)
+
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ scxmlcontext.h \
+ scxmleditor_global.h \
+ scxmleditorconstants.h \
+ scxmleditordata.h \
+ scxmleditordocument.h \
+ scxmleditorfactory.h \
+ scxmleditorplugin.h \
+ scxmleditorstack.h \
+ scxmltexteditor.h
+
+SOURCES += \
+ scxmlcontext.cpp \
+ scxmleditordata.cpp \
+ scxmleditordocument.cpp \
+ scxmleditorfactory.cpp \
+ scxmleditorplugin.cpp \
+ scxmleditorstack.cpp \
+ scxmltexteditor.cpp
+
+RESOURCES += \
+ resources.qrc
+
+include(plugin_interface/plugin_interface.pri)
+include(common/common.pri)
+include(outputpane/outputpane.pri)
diff --git a/src/plugins/scxmleditor/scxmleditor.qbs b/src/plugins/scxmleditor/scxmleditor.qbs
new file mode 100644
index 00000000000..bb2b633392c
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditor.qbs
@@ -0,0 +1,162 @@
+import qbs 1.0
+
+QtcPlugin {
+ name: "ScxmlEditor"
+
+ Depends { name: "Qt.widgets" }
+ Depends { name: "Utils" }
+
+ Depends { name: "Core" }
+ Depends { name: "TextEditor" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "QtSupport" }
+
+ Depends { name: "app_version_header" }
+
+ cpp.includePaths: base.concat([
+ ".",
+ common.prefix,
+ outputpane.prefix,
+ plugin_interface.prefix,
+ ])
+
+ files: [
+ "scxmlcontext.cpp", "scxmlcontext.h",
+ "scxmleditor_global.h",
+ "scxmleditorconstants.h",
+ "scxmleditordata.cpp", "scxmleditordata.h",
+ "scxmleditordocument.cpp", "scxmleditordocument.h",
+ "scxmleditorfactory.cpp", "scxmleditorfactory.h",
+ "scxmleditorplugin.cpp", "scxmleditorplugin.h",
+ "scxmleditorstack.cpp", "scxmleditorstack.h",
+ "scxmltexteditor.cpp", "scxmltexteditor.h",
+
+ "resources.qrc",
+ ]
+
+ Group {
+ id: common
+ name: "Common"
+ prefix: "common/"
+ files: [
+ "colorpicker.cpp", "colorpicker.h",
+ "colorsettings.cpp", "colorsettings.h",
+ "colorthemedialog.cpp", "colorthemedialog.h",
+ "colorthemes.cpp", "colorthemes.h",
+ "colorthemeview.cpp", "colorthemeview.h",
+ "colortoolbutton.cpp", "colortoolbutton.h",
+ "dragshapebutton.cpp", "dragshapebutton.h",
+ "graphicsview.cpp", "graphicsview.h",
+ "magnifier.cpp", "magnifier.h",
+ "mainwidget.cpp", "mainwidget.h",
+ "movableframe.cpp", "movableframe.h",
+ "navigator.cpp", "navigator.h",
+ "navigatorgraphicsview.cpp", "navigatorgraphicsview.h",
+ "navigatorslider.cpp", "navigatorslider.h",
+ "search.cpp", "search.h",
+ "searchmodel.cpp", "searchmodel.h",
+ "shapegroupwidget.cpp", "shapegroupwidget.h",
+ "shapestoolbox.cpp", "shapestoolbox.h",
+ "sizegrip.cpp", "sizegrip.h",
+ "stateproperties.cpp", "stateproperties.h",
+ "stateview.cpp", "stateview.h",
+ "statistics.cpp", "statistics.h",
+ "statisticsdialog.cpp", "statisticsdialog.h",
+ "structure.cpp", "structure.h",
+ "structuremodel.cpp", "structuremodel.h",
+ "treeview.h", "treeview.cpp",
+
+ "common.qrc",
+
+ "magnifier.ui",
+ "colorpicker.ui",
+ "colorsettings.ui",
+ "colorthemedialog.ui",
+ "mainwidget.ui",
+ "navigator.ui",
+ "navigatorslider.ui",
+ "search.ui",
+ "shapegroupwidget.ui",
+ "shapestoolbox.ui",
+ "stateproperties.ui",
+ "stateview.ui",
+ "statistics.ui",
+ "statisticsdialog.ui",
+ "structure.ui",
+ ]
+ }
+
+ Group {
+ id: outputpane
+ name: "Output Pane"
+ prefix: "outputpane/"
+ files: [
+ "errorwidget.cpp", "errorwidget.h",
+ "outputpane.h",
+ "outputtabwidget.cpp", "outputtabwidget.h",
+ "tableview.cpp", "tableview.h",
+ "warning.cpp", "warning.h",
+ "warningmodel.cpp", "warningmodel.h",
+
+ "outputpane.qrc",
+
+ "errorwidget.ui",
+ "outputtabwidget.ui",
+ ]
+ }
+
+ Group {
+ id: plugin_interface
+ name: "Plugin Interface"
+ prefix: "plugin_interface/"
+ files: [
+ "actionhandler.cpp", "actionhandler.h",
+ "actionprovider.h",
+ "attributeitemdelegate.cpp", "attributeitemdelegate.h",
+ "attributeitemmodel.cpp", "attributeitemmodel.h",
+ "baseitem.cpp", "baseitem.h",
+ "connectableitem.cpp", "connectableitem.h",
+ "cornergrabberitem.cpp", "cornergrabberitem.h",
+ "finalstateitem.cpp", "finalstateitem.h",
+ "genericscxmlplugin.cpp", "genericscxmlplugin.h",
+ "graphicsitemprovider.h",
+ "graphicsscene.cpp", "graphicsscene.h",
+ "highlightitem.cpp", "highlightitem.h",
+ "historyitem.cpp", "historyitem.h",
+ "idwarningitem.cpp", "idwarningitem.h",
+ "imageprovider.cpp", "imageprovider.h",
+ "initialstateitem.cpp", "initialstateitem.h",
+ "initialwarningitem.cpp", "initialwarningitem.h",
+ "isceditor.h",
+ "layoutitem.cpp", "layoutitem.h",
+ "mytypes.h",
+ "parallelitem.cpp", "parallelitem.h",
+ "quicktransitionitem.cpp", "quicktransitionitem.h",
+ "scattributeitemdelegate.cpp", "scattributeitemdelegate.h",
+ "scattributeitemmodel.cpp", "scattributeitemmodel.h",
+ "sceneutils.cpp", "sceneutils.h",
+ "scgraphicsitemprovider.cpp", "scgraphicsitemprovider.h",
+ "scshapeprovider.cpp", "scshapeprovider.h",
+ "scutilsprovider.cpp", "scutilsprovider.h",
+ "scxmldocument.cpp", "scxmldocument.h",
+ "scxmlnamespace.cpp", "scxmlnamespace.h",
+ "scxmltag.cpp", "scxmltag.h",
+ "scxmltagutils.cpp", "scxmltagutils.h",
+ "scxmltypes.h",
+ "scxmluifactory.cpp", "scxmluifactory.h",
+ "serializer.cpp", "serializer.h",
+ "shapeprovider.cpp", "shapeprovider.h",
+ "snapline.cpp", "snapline.h",
+ "stateitem.cpp", "stateitem.h",
+ "statewarningitem.cpp", "statewarningitem.h",
+ "tagtextitem.cpp", "tagtextitem.h",
+ "textitem.cpp", "textitem.h",
+ "transitionitem.cpp", "transitionitem.h",
+ "transitionwarningitem.cpp", "transitionwarningitem.h",
+ "undocommands.cpp", "undocommands.h",
+ "utilsprovider.cpp", "utilsprovider.h",
+ "warningitem.cpp", "warningitem.h",
+ "warningprovider.h",
+ ]
+ }
+}
diff --git a/src/plugins/scxmleditor/scxmleditor_dependencies.pri b/src/plugins/scxmleditor/scxmleditor_dependencies.pri
new file mode 100644
index 00000000000..fa396a263fe
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditor_dependencies.pri
@@ -0,0 +1,10 @@
+QTC_PLUGIN_NAME = ScxmlEditor
+QTC_LIB_DEPENDS += \
+ aggregation \
+ extensionsystem \
+ utils
+QTC_PLUGIN_DEPENDS += \
+ coreplugin \
+ texteditor \
+ projectexplorer \
+ qtsupport
diff --git a/src/plugins/scxmleditor/scxmleditor_global.h b/src/plugins/scxmleditor/scxmleditor_global.h
new file mode 100644
index 00000000000..e87a9e42e98
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditor_global.h
@@ -0,0 +1,34 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QtGlobal>
+
+#if defined(SCXMLEDITOR_LIBRARY)
+# define SCXMLEDITORSHARED_EXPORT Q_DECL_EXPORT
+#else
+# define SCXMLEDITORSHARED_EXPORT Q_DECL_IMPORT
+#endif
diff --git a/src/plugins/scxmleditor/scxmleditorconstants.h b/src/plugins/scxmleditor/scxmleditorconstants.h
new file mode 100644
index 00000000000..8dd3bc360e8
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditorconstants.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmleditor_global.h"
+
+namespace ScxmlEditor {
+namespace Constants {
+
+const char INFO_READ_ONLY[] = "ScxmlEditor.ReadOnly";
+
+const char C_SCXMLEDITOR[] = "Qt5.ScxmlEditor";
+const char C_SCXMLEDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("OpenWith::Editors", "Scxml Editor");
+
+const char K_SCXML_EDITOR_ID[] = "ScxmlEditor.XmlEditor";
+const char C_SCXML_EDITOR[] = "Scxml Editor";
+
+const char C_SCXMLTAG_ATTRIBUTE_ID[] = "id";
+const char C_SCXMLTAG_ATTRIBUTE_EVENT[] = "event";
+
+const char C_SCXMLTAG_TAGTYPE[] = "tagType";
+const char C_SCXMLTAG_ACTIONTYPE[] = "actionType";
+const char C_SCXMLTAG_PARENTTAG[] = "parentTag";
+
+const char C_SCXML_EDITORINFO_COLORS[] = "colors";
+const char C_SCXML_EDITORINFO_GEOMETRY[] = "geometry";
+const char C_SCXML_EDITORINFO_SCENEGEOMETRY[] = "scenegeometry";
+const char C_SCXML_EDITORINFO_LOCALGEOMETRY[] = "localGeometry";
+const char C_SCXML_EDITORINFO_MOVEPOINT[] = "movePoint";
+const char C_SCXML_EDITORINFO_STARTTARGETFACTORS[] = "startTargetFactors";
+const char C_SCXML_EDITORINFO_ENDTARGETFACTORS[] = "endTargetFactors";
+const char C_SCXML_EDITORINFO_FONTCOLOR[] = "fontColor";
+const char C_SCXML_EDITORINFO_STATECOLOR[] = "stateColor";
+
+const char C_STATE_WARNING_ID[] = "IDWarning";
+const char C_STATE_WARNING_STATE[] = "StateWarning";
+const char C_STATE_WARNING_TRANSITION[] = "TransitionWarning";
+const char C_STATE_WARNING_INITIAL[] = "InitialWarning";
+
+const char C_UI_FACTORY_OBJECT_ACTIONPROVIDER[] = "actionProvider";
+
+const char C_COLOR_SCHEME_DEFAULT[] = "factory_default_theme";
+const char C_COLOR_SCHEME_SCXMLDOCUMENT[] = "scxmldocument_theme";
+
+const char C_OBJECTNAME_WARNINGPROVIDER[] = "warningProvider";
+
+const char C_WARNINGMODEL_FILTER_ACTIVE[] = "active";
+const char C_WARNINGMODEL_FILTER_NOT[] = "not";
+
+const char C_SETTINGS_SPLITTER[] = "ScxmlEditor/HorizontalSplitter";
+const char C_SETTINGS_LASTEXPORTFOLDER[] = "ScxmlEditor/LastExportFolder";
+const char C_SETTINGS_LASTSAVESCREENSHOTFOLDER[] = "ScxmlEditor/LastSaveScreenshotFolder";
+const char C_SETTINGS_COLORSETTINGS_COLORTHEMES[] = "ScxmlEditor/ColorSettingsColorThemes";
+const char C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME[] = "ScxmlEditor/ColorSettingsCurrentColorTheme";
+const char C_SETTINGS_ERRORPANE_GEOMETRY[] = "ScxmlEditor/ErrorPaneGeometry";
+const char C_SETTINGS_ERRORPANE_SHOWERRORS[] = "ScxmlEditor/ErrorPaneShowErrors";
+const char C_SETTINGS_ERRORPANE_SHOWWARNINGS[] = "ScxmlEditor/ErrorPaneShowWarnings";
+const char C_SETTINGS_ERRORPANE_SHOWINFOS[] = "ScxmlEditor/ErrorPaneShowInfos";
+
+} // namespace ScxmlEditor
+} // namespace Constants
diff --git a/src/plugins/scxmleditor/scxmleditordata.cpp b/src/plugins/scxmleditor/scxmleditordata.cpp
new file mode 100644
index 00000000000..76f01b692d7
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditordata.cpp
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmleditordata.h"
+#include "mainwidget.h"
+#include "scxmlcontext.h"
+#include "scxmleditorconstants.h"
+#include "scxmleditordocument.h"
+#include "scxmleditorstack.h"
+#include "scxmltexteditor.h"
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/designmode.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/idocument.h>
+#include <coreplugin/infobar.h>
+#include <coreplugin/minisplitter.h>
+#include <coreplugin/modemanager.h>
+#include <coreplugin/outputpane.h>
+
+#include <projectexplorer/projectexplorerconstants.h>
+
+#include <utils/qtcassert.h>
+
+#include <QVBoxLayout>
+
+using namespace ScxmlEditor::Common;
+using namespace ScxmlEditor::PluginInterface;
+
+namespace ScxmlEditor {
+
+namespace Internal {
+
+class ScxmlTextEditorWidget : public TextEditor::TextEditorWidget
+{
+public:
+ ScxmlTextEditorWidget()
+ {
+ }
+
+ void finalizeInitialization()
+ {
+ setReadOnly(true);
+ }
+};
+
+class ScxmlTextEditorFactory : public TextEditor::TextEditorFactory
+{
+public:
+ ScxmlTextEditorFactory()
+ {
+ setId(ScxmlEditor::Constants::K_SCXML_EDITOR_ID);
+ setEditorCreator([]() { return new ScxmlTextEditor; });
+ setEditorWidgetCreator([]() { return new ScxmlTextEditorWidget; });
+ setUseGenericHighlighter(true);
+ setDuplicatedSupported(false);
+ }
+
+ ScxmlTextEditor *create(ScxmlEditor::Common::MainWidget *designWidget)
+ {
+ setDocumentCreator([designWidget]() { return new ScxmlEditorDocument(designWidget); });
+ return qobject_cast<ScxmlTextEditor*>(createEditor());
+ }
+};
+
+ScxmlEditorData::ScxmlEditorData(QObject *parent)
+ : QObject(parent)
+{
+ m_contexts.add(ScxmlEditor::Constants::C_SCXMLEDITOR);
+
+ QObject::connect(EditorManager::instance(), &EditorManager::currentEditorChanged, [this](IEditor *editor) {
+ if (editor && editor->document()->id() == Constants::K_SCXML_EDITOR_ID) {
+ auto xmlEditor = qobject_cast<ScxmlTextEditor*>(editor);
+ QTC_ASSERT(xmlEditor, return );
+ QWidget *dw = m_widgetStack->widgetForEditor(xmlEditor);
+ QTC_ASSERT(dw, return );
+ m_widgetStack->setVisibleEditor(xmlEditor);
+ m_mainToolBar->setCurrentEditor(xmlEditor);
+ updateToolBar();
+ auto designWidget = static_cast<MainWidget*>(m_widgetStack->currentWidget());
+ if (designWidget)
+ designWidget->refresh();
+ }
+ });
+
+ m_xmlEditorFactory = new ScxmlTextEditorFactory;
+}
+
+ScxmlEditorData::~ScxmlEditorData()
+{
+ if (m_context)
+ ICore::removeContextObject(m_context);
+
+ if (m_modeWidget) {
+ m_designMode->unregisterDesignWidget(m_modeWidget);
+ delete m_modeWidget;
+ m_modeWidget = nullptr;
+ }
+
+ delete m_xmlEditorFactory;
+}
+
+void ScxmlEditorData::fullInit()
+{
+ // Create widget-stack, toolbar, mainToolbar and whole design-mode widget
+ m_widgetStack = new ScxmlEditorStack;
+ m_widgetToolBar = new QToolBar;
+ m_mainToolBar = createMainToolBar();
+ m_designMode = DesignMode::instance();
+ m_modeWidget = createModeWidget();
+
+ // Create undo/redo group/actions
+ m_undoGroup = new QUndoGroup(m_widgetToolBar);
+ m_undoAction = m_undoGroup->createUndoAction(m_widgetToolBar);
+ m_undoAction->setIcon(QIcon(":/scxmleditor/images/icon-undo.png"));
+ m_undoAction->setToolTip(tr("Undo (Ctrl + Z)"));
+
+ m_redoAction = m_undoGroup->createRedoAction(m_widgetToolBar);
+ m_redoAction->setIcon(QIcon(":/scxmleditor/images/icon-redo.png"));
+ m_redoAction->setToolTip(tr("Redo (Ctrl + Y)"));
+
+ ActionManager::registerAction(m_undoAction, Core::Constants::UNDO, m_contexts);
+ ActionManager::registerAction(m_redoAction, Core::Constants::REDO, m_contexts);
+
+ Context scxmlContexts = m_contexts;
+ scxmlContexts.add(Core::Constants::C_EDITORMANAGER);
+ m_context = new ScxmlContext(scxmlContexts, m_modeWidget, this);
+ ICore::addContextObject(m_context);
+
+ m_designMode->registerDesignWidget(m_modeWidget, QStringList(QLatin1String(ProjectExplorer::Constants::SCXML_MIMETYPE)), m_contexts);
+}
+
+IEditor *ScxmlEditorData::createEditor()
+{
+ auto designWidget = new MainWidget;
+ ScxmlTextEditor *xmlEditor = m_xmlEditorFactory->create(designWidget);
+
+ m_undoGroup->addStack(designWidget->undoStack());
+ m_widgetStack->add(xmlEditor, designWidget);
+ m_mainToolBar->addEditor(xmlEditor);
+
+ if (xmlEditor) {
+ InfoBarEntry info(Id(Constants::INFO_READ_ONLY),
+ tr("This file can only be edited in <b>Design</b> mode."));
+ info.setCustomButtonInfo(tr("Switch Mode"), []() { ModeManager::activateMode(Core::Constants::MODE_DESIGN); });
+ xmlEditor->document()->infoBar()->addInfo(info);
+ }
+
+ return xmlEditor;
+}
+
+void ScxmlEditorData::updateToolBar()
+{
+ auto designWidget = static_cast<MainWidget*>(m_widgetStack->currentWidget());
+ if (designWidget && m_widgetToolBar) {
+ m_undoGroup->setActiveStack(designWidget->undoStack());
+ m_widgetToolBar->clear();
+ m_widgetToolBar->addAction(m_undoAction);
+ m_widgetToolBar->addAction(m_redoAction);
+ m_widgetToolBar->addSeparator();
+ m_widgetToolBar->addAction(designWidget->action(ActionCopy));
+ m_widgetToolBar->addAction(designWidget->action(ActionCut));
+ m_widgetToolBar->addAction(designWidget->action(ActionPaste));
+ m_widgetToolBar->addAction(designWidget->action(ActionScreenshot));
+ m_widgetToolBar->addAction(designWidget->action(ActionExportToImage));
+ m_widgetToolBar->addAction(designWidget->action(ActionFullNamespace));
+ m_widgetToolBar->addSeparator();
+ m_widgetToolBar->addAction(designWidget->action(ActionZoomIn));
+ m_widgetToolBar->addAction(designWidget->action(ActionZoomOut));
+ m_widgetToolBar->addAction(designWidget->action(ActionPan));
+ m_widgetToolBar->addAction(designWidget->action(ActionFitToView));
+ m_widgetToolBar->addSeparator();
+ m_widgetToolBar->addWidget(designWidget->toolButton(ToolButtonAdjustment));
+ m_widgetToolBar->addWidget(designWidget->toolButton(ToolButtonAlignment));
+ m_widgetToolBar->addWidget(designWidget->toolButton(ToolButtonStateColor));
+ m_widgetToolBar->addWidget(designWidget->toolButton(ToolButtonFontColor));
+ m_widgetToolBar->addWidget(designWidget->toolButton(ToolButtonColorTheme));
+ m_widgetToolBar->addSeparator();
+ m_widgetToolBar->addAction(designWidget->action(ActionMagnifier));
+ m_widgetToolBar->addAction(designWidget->action(ActionNavigator));
+ m_widgetToolBar->addSeparator();
+ m_widgetToolBar->addAction(designWidget->action(ActionStatistics));
+ }
+}
+
+EditorToolBar *ScxmlEditorData::createMainToolBar()
+{
+ auto toolBar = new EditorToolBar;
+ toolBar->setToolbarCreationFlags(EditorToolBar::FlagsStandalone);
+ toolBar->setNavigationVisible(false);
+ toolBar->addCenterToolBar(m_widgetToolBar);
+
+ return toolBar;
+}
+
+QWidget *ScxmlEditorData::createModeWidget()
+{
+ auto widget = new QWidget;
+
+ widget->setObjectName("ScxmlEditorDesignModeWidget");
+ auto layout = new QVBoxLayout;
+ layout->setMargin(0);
+ layout->setSpacing(0);
+ layout->addWidget(m_mainToolBar);
+ // Avoid mode switch to 'Edit' mode when the application started by
+ // 'Run' in 'Design' mode emits output.
+ auto splitter = new MiniSplitter(Qt::Vertical);
+ splitter->addWidget(m_widgetStack);
+ auto outputPane = new OutputPanePlaceHolder(m_designMode->id(), splitter);
+ outputPane->setObjectName("DesignerOutputPanePlaceHolder");
+ splitter->addWidget(outputPane);
+ layout->addWidget(splitter);
+ widget->setLayout(layout);
+
+ return widget;
+}
+
+} // namespace Internal
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/scxmleditordata.h b/src/plugins/scxmleditor/scxmleditordata.h
new file mode 100644
index 00000000000..527659fbbb2
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditordata.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QToolBar>
+#include <QUndoGroup>
+
+#include <QCoreApplication>
+#include <coreplugin/designmode.h>
+#include <coreplugin/editortoolbar.h>
+
+using namespace Core;
+
+namespace ScxmlEditor {
+namespace Internal {
+
+class ScxmlTextEditorFactory;
+class ScxmlEditorWidget;
+class ScxmlEditorStack;
+class ScxmlContext;
+
+class ScxmlEditorData : public QObject
+{
+public:
+ ScxmlEditorData(QObject *parent = nullptr);
+ ~ScxmlEditorData();
+
+ void fullInit();
+ IEditor *createEditor();
+
+private:
+ void updateToolBar();
+ QWidget *createModeWidget();
+ EditorToolBar *createMainToolBar();
+
+ ScxmlContext *m_context = nullptr;
+ Context m_contexts;
+ QWidget *m_modeWidget = nullptr;
+ ScxmlEditorStack *m_widgetStack = nullptr;
+ DesignMode *m_designMode = nullptr;
+ QToolBar *m_widgetToolBar = nullptr;
+ EditorToolBar *m_mainToolBar = nullptr;
+ QUndoGroup *m_undoGroup = nullptr;
+ QAction *m_undoAction = nullptr;
+ QAction *m_redoAction = nullptr;
+
+ ScxmlTextEditorFactory *m_xmlEditorFactory = nullptr;
+};
+
+} // namespace Internal
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/scxmleditordocument.cpp b/src/plugins/scxmleditor/scxmleditordocument.cpp
new file mode 100644
index 00000000000..7a496dc3339
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditordocument.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmleditordocument.h"
+#include "mainwidget.h"
+#include "scxmleditorconstants.h"
+
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/session.h>
+#include <qtsupport/qtkitinformation.h>
+#include <utils/fileutils.h>
+#include <utils/qtcassert.h>
+
+#include <QFileInfo>
+#include <QGuiApplication>
+#include <QTextCodec>
+#include <QTextDocument>
+
+using namespace Utils;
+
+using namespace ScxmlEditor::Common;
+using namespace ScxmlEditor::Internal;
+
+ScxmlEditorDocument::ScxmlEditorDocument(MainWidget *designWidget, QObject *parent)
+ : m_designWidget(designWidget)
+{
+ setMimeType(QLatin1String(ProjectExplorer::Constants::SCXML_MIMETYPE));
+ setParent(parent);
+ setId(Core::Id(ScxmlEditor::Constants::K_SCXML_EDITOR_ID));
+
+ // Designer needs UTF-8 regardless of settings.
+ setCodec(QTextCodec::codecForName("UTF-8"));
+ connect(m_designWidget, &Common::MainWidget::dirtyChanged, this, [this]{
+ emit changed();
+ });
+}
+
+Core::IDocument::OpenResult ScxmlEditorDocument::open(QString *errorString, const QString &fileName, const QString &realFileName)
+{
+ Q_UNUSED(realFileName)
+
+ if (fileName.isEmpty())
+ return OpenResult::ReadError;
+
+ if (!m_designWidget)
+ return OpenResult::ReadError;
+
+ const QFileInfo fi(fileName);
+ const QString absfileName = fi.absoluteFilePath();
+ if (!m_designWidget->load(absfileName)) {
+ *errorString = m_designWidget->errorMessage();
+ return OpenResult::ReadError;
+ }
+
+ setFilePath(Utils::FileName::fromString(absfileName));
+
+ return OpenResult::Success;
+}
+
+bool ScxmlEditorDocument::save(QString *errorString, const QString &name, bool autoSave)
+{
+ const FileName oldFileName = filePath();
+ const FileName actualName = name.isEmpty() ? oldFileName : FileName::fromString(name);
+ if (actualName.isEmpty())
+ return false;
+ bool dirty = m_designWidget->isDirty();
+
+ m_designWidget->setFileName(actualName.toString());
+ if (!m_designWidget->save()) {
+ *errorString = m_designWidget->errorMessage();
+ m_designWidget->setFileName(oldFileName.toString());
+ return false;
+ }
+
+ if (autoSave) {
+ m_designWidget->setFileName(oldFileName.toString());
+ m_designWidget->save();
+ return true;
+ }
+
+ setFilePath(actualName);
+
+ if (dirty != m_designWidget->isDirty())
+ emit changed();
+
+ return true;
+}
+
+void ScxmlEditorDocument::setFilePath(const FileName &newName)
+{
+ m_designWidget->setFileName(newName.toString());
+ IDocument::setFilePath(newName);
+}
+
+bool ScxmlEditorDocument::shouldAutoSave() const
+{
+ return false;
+}
+
+bool ScxmlEditorDocument::isSaveAsAllowed() const
+{
+ return true;
+}
+
+MainWidget *ScxmlEditorDocument::designWidget() const
+{
+ return m_designWidget;
+}
+
+bool ScxmlEditorDocument::isModified() const
+{
+ return m_designWidget && m_designWidget->isDirty();
+}
+
+bool ScxmlEditorDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type)
+{
+ if (flag == FlagIgnore) {
+ return true;
+ } if (type == TypePermissions) {
+ emit changed();
+ } else {
+ emit aboutToReload();
+ emit reloadRequested(errorString, filePath().toString());
+ const bool success = errorString->isEmpty();
+ emit reloadFinished(success);
+ return success;
+ }
+
+ return true;
+}
+
+QString ScxmlEditorDocument::designWidgetContents() const
+{
+ return m_designWidget->contents();
+}
+
+void ScxmlEditorDocument::syncXmlFromDesignWidget()
+{
+ document()->setPlainText(designWidgetContents());
+}
diff --git a/src/plugins/scxmleditor/scxmleditordocument.h b/src/plugins/scxmleditor/scxmleditordocument.h
new file mode 100644
index 00000000000..2ccb98213d5
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditordocument.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/target.h>
+#include <texteditor/textdocument.h>
+
+#include <QPointer>
+
+QT_FORWARD_DECLARE_CLASS(QDesignerFormWindowInterface)
+
+namespace ScxmlEditor {
+
+namespace Common {
+class MainWidget;
+} // namespace Common
+
+namespace Internal {
+
+class ScxmlEditorDocument : public TextEditor::TextDocument
+{
+ Q_OBJECT
+
+public:
+ explicit ScxmlEditorDocument(Common::MainWidget *designWidget, QObject *parent = nullptr);
+
+ // IDocument
+ OpenResult open(QString *errorString, const QString &fileName, const QString &realFileName) override;
+ bool save(QString *errorString, const QString &fileName, bool autoSave) override;
+ bool shouldAutoSave() const override;
+ bool isSaveAsAllowed() const override;
+ bool isModified() const override;
+ bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override;
+
+ // Internal
+ Common::MainWidget *designWidget() const;
+ void syncXmlFromDesignWidget();
+ QString designWidgetContents() const;
+ void setFilePath(const Utils::FileName&) override;
+
+signals:
+ void reloadRequested(QString *errorString, const QString &);
+
+private:
+ QPointer<Common::MainWidget> m_designWidget;
+};
+
+} // namespace Internal
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/scxmleditorfactory.cpp b/src/plugins/scxmleditor/scxmleditorfactory.cpp
new file mode 100644
index 00000000000..6a923237d13
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditorfactory.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmleditorfactory.h"
+
+#include "scxmleditorconstants.h"
+#include "scxmleditordata.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/fileiconprovider.h>
+#include <projectexplorer/projectexplorerconstants.h>
+
+#include <QGuiApplication>
+#include <QFileInfo>
+
+using namespace ScxmlEditor::Constants;
+using namespace ScxmlEditor::Internal;
+
+ScxmlEditorFactory::ScxmlEditorFactory()
+{
+ setId(K_SCXML_EDITOR_ID);
+ setDisplayName(QCoreApplication::translate("ScxmlEditor", C_SCXMLEDITOR_DISPLAY_NAME));
+ addMimeType(ProjectExplorer::Constants::SCXML_MIMETYPE);
+
+ Core::FileIconProvider::registerIconOverlayForSuffix(":/projectexplorer/images/fileoverlay_scxml.png", "scxml");
+}
+
+Core::IEditor *ScxmlEditorFactory::createEditor()
+{
+ if (!m_editorData) {
+ m_editorData = new ScxmlEditorData(this);
+ QGuiApplication::setOverrideCursor(Qt::WaitCursor);
+ m_editorData->fullInit();
+ QGuiApplication::restoreOverrideCursor();
+ }
+ return m_editorData->createEditor();
+}
diff --git a/src/plugins/scxmleditor/scxmleditorfactory.h b/src/plugins/scxmleditor/scxmleditorfactory.h
new file mode 100644
index 00000000000..1e621e0615c
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditorfactory.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <coreplugin/editormanager/ieditorfactory.h>
+
+namespace ScxmlEditor {
+namespace Internal {
+
+class ScxmlEditorData;
+
+class ScxmlEditorFactory : public Core::IEditorFactory
+{
+ Q_OBJECT
+
+public:
+ explicit ScxmlEditorFactory();
+
+ Core::IEditor *createEditor() override;
+
+private:
+ ScxmlEditorData* m_editorData = nullptr;
+};
+
+} // namespace Internal
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/scxmleditorplugin.cpp b/src/plugins/scxmleditor/scxmleditorplugin.cpp
new file mode 100644
index 00000000000..8a7a487b5ef
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditorplugin.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmleditorplugin.h"
+#include "scxmleditorconstants.h"
+#include "scxmleditorfactory.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/designmode.h>
+#include <coreplugin/icontext.h>
+#include <coreplugin/icore.h>
+
+#include <QAction>
+#include <QMainWindow>
+#include <QMenu>
+#include <QMessageBox>
+#include <utils/mimetypes/mimedatabase.h>
+
+#include <QtPlugin>
+
+using namespace Core;
+using namespace ScxmlEditor::Internal;
+
+ScxmlEditorPlugin::ScxmlEditorPlugin()
+{
+}
+
+bool ScxmlEditorPlugin::initialize(const QStringList &arguments, QString *errorString)
+{
+ Q_UNUSED(arguments)
+ Q_UNUSED(errorString)
+
+ Utils::MimeDatabase::addMimeTypes(":/scxmleditor/ScxmlEditor.mimetypes.xml");
+ addAutoReleasedObject(new ScxmlEditorFactory);
+
+ return true;
+}
+
+void ScxmlEditorPlugin::extensionsInitialized()
+{
+ DesignMode::instance()->setDesignModeIsRequired();
+}
+
+ExtensionSystem::IPlugin::ShutdownFlag ScxmlEditorPlugin::aboutToShutdown()
+{
+ return SynchronousShutdown;
+}
diff --git a/src/plugins/scxmleditor/scxmleditorplugin.h b/src/plugins/scxmleditor/scxmleditorplugin.h
new file mode 100644
index 00000000000..71e8552ccf0
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditorplugin.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+#include "scxmleditor_global.h"
+
+#include <extensionsystem/iplugin.h>
+
+namespace ScxmlEditor {
+namespace Internal {
+
+class ScxmlEditorPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ScxmlEditor.json")
+
+public:
+ ScxmlEditorPlugin();
+
+ bool initialize(const QStringList &arguments, QString *errorString) override;
+ void extensionsInitialized() override;
+ ShutdownFlag aboutToShutdown() override;
+};
+
+} // namespace Internal
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/scxmleditorstack.cpp b/src/plugins/scxmleditor/scxmleditorstack.cpp
new file mode 100644
index 00000000000..bf8307a2184
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditorstack.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmleditorstack.h"
+#include "scxmleditordocument.h"
+#include "scxmltexteditor.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/imode.h>
+#include <coreplugin/modemanager.h>
+
+#include <utils/qtcassert.h>
+
+using namespace ScxmlEditor;
+using namespace ScxmlEditor::Internal;
+
+ScxmlEditorStack::ScxmlEditorStack(QWidget *parent)
+ : QStackedWidget(parent)
+{
+ setObjectName("ScxmlEditorStack");
+}
+
+void ScxmlEditorStack::add(ScxmlTextEditor *editor, QWidget *w)
+{
+ connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeAboutToChange,
+ this, &ScxmlEditorStack::modeAboutToChange);
+
+ m_editors.append(editor);
+ addWidget(w);
+ connect(editor, &ScxmlTextEditor::destroyed,
+ this, &ScxmlEditorStack::removeScxmlTextEditor);
+}
+
+void ScxmlEditorStack::removeScxmlTextEditor(QObject *xmlEditor)
+{
+ const int i = m_editors.indexOf(static_cast<ScxmlTextEditor*>(xmlEditor));
+ QTC_ASSERT(i >= 0, return);
+
+ QWidget *widget = this->widget(i);
+ if (widget) {
+ removeWidget(widget);
+ widget->deleteLater();
+ }
+ m_editors.removeAt(i);
+}
+
+bool ScxmlEditorStack::setVisibleEditor(Core::IEditor *xmlEditor)
+{
+ const int i = m_editors.indexOf(static_cast<ScxmlTextEditor*>(xmlEditor));
+ QTC_ASSERT(i >= 0, return false);
+
+ if (i != currentIndex())
+ setCurrentIndex(i);
+
+ return true;
+}
+
+QWidget *ScxmlEditorStack::widgetForEditor(ScxmlTextEditor *xmlEditor)
+{
+ const int i = m_editors.indexOf(xmlEditor);
+ QTC_ASSERT(i >= 0, return nullptr);
+
+ return widget(i);
+}
+
+void ScxmlEditorStack::modeAboutToChange(Core::Id m)
+{
+ // Sync the editor when entering edit mode
+ if (m == Core::Constants::MODE_EDIT) {
+ for (const ScxmlTextEditor *editor: m_editors)
+ if (ScxmlEditorDocument *document = qobject_cast<ScxmlEditorDocument*>(editor->textDocument()))
+ document->syncXmlFromDesignWidget();
+ }
+}
diff --git a/src/plugins/scxmleditor/scxmleditorstack.h b/src/plugins/scxmleditor/scxmleditorstack.h
new file mode 100644
index 00000000000..3fc228313ab
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmleditorstack.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <coreplugin/id.h>
+
+#include <QStackedWidget>
+
+namespace Core {
+class IEditor;
+class IMode;
+}
+
+namespace ScxmlEditor {
+
+class ScxmlTextEditor;
+
+namespace Internal {
+
+class ScxmlEditorStack : public QStackedWidget {
+ Q_OBJECT
+
+public:
+ ScxmlEditorStack(QWidget *parent = nullptr);
+
+ void add(ScxmlTextEditor *editor, QWidget *widget);
+ QWidget *widgetForEditor(ScxmlTextEditor *editor);
+ void removeScxmlTextEditor(QObject*);
+ bool setVisibleEditor(Core::IEditor *xmlEditor);
+
+private:
+ void modeAboutToChange(Core::Id m);
+
+ QVector<ScxmlTextEditor*> m_editors;
+};
+
+} // namespace Internal
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/scxmltexteditor.cpp b/src/plugins/scxmleditor/scxmltexteditor.cpp
new file mode 100644
index 00000000000..1e6e2e6761f
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmltexteditor.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "scxmltexteditor.h"
+#include "mainwidget.h"
+#include "scxmleditorconstants.h"
+#include "scxmleditordocument.h"
+
+#include <coreplugin/coreconstants.h>
+#include <texteditor/textdocument.h>
+
+#include <utils/fileutils.h>
+#include <utils/qtcassert.h>
+
+#include <QBuffer>
+#include <QFileInfo>
+
+using namespace ScxmlEditor;
+using namespace ScxmlEditor::Internal;
+
+ScxmlTextEditor::ScxmlTextEditor()
+{
+ addContext(ScxmlEditor::Constants::K_SCXML_EDITOR_ID);
+ addContext(ScxmlEditor::Constants::C_SCXML_EDITOR);
+}
+
+void ScxmlTextEditor::finalizeInitialization()
+{
+ // Revert to saved/load externally modified files.
+ ScxmlEditorDocument *document = qobject_cast<ScxmlEditorDocument*>(textDocument());
+ connect(document, &ScxmlEditorDocument::reloadRequested,
+ [this](QString *errorString, const QString &fileName) {
+ open(errorString, fileName, fileName);
+ });
+}
+
+bool ScxmlTextEditor::open(QString *errorString, const QString &fileName, const QString & /*realFileName*/)
+{
+ ScxmlEditorDocument *document = qobject_cast<ScxmlEditorDocument*>(textDocument());
+ Common::MainWidget *designWidget = document->designWidget();
+ QTC_ASSERT(designWidget, return false);
+
+ if (fileName.isEmpty())
+ return true;
+
+ const QFileInfo fi(fileName);
+ const QString absfileName = fi.absoluteFilePath();
+
+ if (!designWidget->load(absfileName)) {
+ *errorString = designWidget->errorMessage();
+ return false;
+ }
+
+ document->syncXmlFromDesignWidget();
+ document->setFilePath(Utils::FileName::fromString(absfileName));
+
+ return true;
+}
diff --git a/src/plugins/scxmleditor/scxmltexteditor.h b/src/plugins/scxmleditor/scxmltexteditor.h
new file mode 100644
index 00000000000..8c13ad1530f
--- /dev/null
+++ b/src/plugins/scxmleditor/scxmltexteditor.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "scxmleditor_global.h"
+#include <texteditor/texteditor.h>
+
+namespace ScxmlEditor {
+namespace Internal { class ScxmlEditorDocument; }
+
+class ScxmlTextEditor : public TextEditor::BaseTextEditor
+{
+ Q_OBJECT
+
+public:
+ ScxmlTextEditor();
+
+ void finalizeInitialization() override;
+ bool open(QString *errorString, const QString &fileName, const QString &realFileName);
+
+ QWidget *toolBar() override
+ {
+ return nullptr;
+ }
+
+ bool isDesignModePreferred() const override
+ {
+ return true;
+ }
+};
+
+} // namespace ScxmlEditor